diff --git a/app/Http/Controllers/LaporanController.php b/app/Http/Controllers/LaporanController.php
index e7e09b7..32199ec 100644
--- a/app/Http/Controllers/LaporanController.php
+++ b/app/Http/Controllers/LaporanController.php
@@ -2,12 +2,15 @@
namespace App\Http\Controllers;
+use App\Models\ItemTransaksi;
+use App\Models\Produk;
use App\Models\Transaksi;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\DB;
class LaporanController extends Controller
{
@@ -15,7 +18,7 @@ class LaporanController extends Controller
{
$filter = $request->query('filter', 'bulan');
$page = $request->query('page', 1);
-
+
$allSalesNames = Transaksi::select('nama_sales')->distinct()->pluck('nama_sales');
if ($filter === 'hari') {
@@ -28,7 +31,7 @@ class LaporanController extends Controller
private function laporanHarian(int $page, Collection $allSalesNames)
{
$perPage = 7;
-
+
$endDate = Carbon::today()->subDays(($page - 1) * $perPage);
$startDate = $endDate->copy()->subDays($perPage - 1);
@@ -50,9 +53,9 @@ class LaporanController extends Controller
if (isset($transaksisByDay[$dateString])) {
$transaksisPerTanggal = $transaksisByDay[$dateString];
-
+
$salesDataTransaksi = $transaksisPerTanggal->groupBy('nama_sales')
- ->map(fn ($transaksisPerSales) => $this->hitungDataSales($transaksisPerSales));
+ ->map(fn($transaksisPerSales) => $this->hitungDataSales($transaksisPerSales));
$fullSalesData = $allSalesNames->map(function ($namaSales) use ($salesDataTransaksi) {
return $salesDataTransaksi->get($namaSales) ?? $this->defaultSalesData($namaSales);
@@ -102,29 +105,29 @@ class LaporanController extends Controller
$laporan = $transaksis->groupBy(function ($transaksi) {
return Carbon::parse($transaksi->created_at)->format('F Y');
})
- ->map(function ($transaksisPerTanggal, $tanggal) use ($allSalesNames) {
-
- $salesDataTransaksi = $transaksisPerTanggal
- ->groupBy('nama_sales')
- ->map(fn ($transaksisPerSales) => $this->hitungDataSales($transaksisPerSales));
+ ->map(function ($transaksisPerTanggal, $tanggal) use ($allSalesNames) {
- $fullSalesData = $allSalesNames->map(function ($namaSales) use ($salesDataTransaksi) {
- return $salesDataTransaksi->get($namaSales) ?? $this->defaultSalesData($namaSales);
+ $salesDataTransaksi = $transaksisPerTanggal
+ ->groupBy('nama_sales')
+ ->map(fn($transaksisPerSales) => $this->hitungDataSales($transaksisPerSales));
+
+ $fullSalesData = $allSalesNames->map(function ($namaSales) use ($salesDataTransaksi) {
+ return $salesDataTransaksi->get($namaSales) ?? $this->defaultSalesData($namaSales);
+ });
+
+ $totalItem = $fullSalesData->sum('item_terjual');
+ $totalBerat = $fullSalesData->sum('berat_terjual_raw');
+ $totalPendapatan = $fullSalesData->sum('pendapatan_raw');
+
+ return [
+ 'tanggal' => $tanggal,
+ 'total_item_terjual' => $totalItem > 0 ? $totalItem : '-',
+ 'total_berat' => $totalBerat > 0 ? number_format($totalBerat, 2, ',', '.') . 'g' : '-',
+ 'total_pendapatan' => $totalPendapatan > 0 ? 'Rp' . number_format($totalPendapatan, 2, ',', '.') : '-',
+ 'sales' => $this->formatSalesDataValues($fullSalesData)->values(),
+ ];
});
- $totalItem = $fullSalesData->sum('item_terjual');
- $totalBerat = $fullSalesData->sum('berat_terjual_raw');
- $totalPendapatan = $fullSalesData->sum('pendapatan_raw');
-
- return [
- 'tanggal' => $tanggal,
- 'total_item_terjual' => $totalItem > 0 ? $totalItem : '-',
- 'total_berat' => $totalBerat > 0 ? number_format($totalBerat, 2, ',', '.') . 'g' : '-',
- 'total_pendapatan' => $totalPendapatan > 0 ? 'Rp' . number_format($totalPendapatan, 2, ',', '.') : '-',
- 'sales' => $this->formatSalesDataValues($fullSalesData)->values(),
- ];
- });
-
$paginatedData = new LengthAwarePaginator(
$laporan->forPage($page, $perPage)->values(),
$laporan->count(),
@@ -132,15 +135,16 @@ class LaporanController extends Controller
$page,
['path' => request()->url(), 'query' => request()->query()]
);
-
+
return response()->json($paginatedData);
}
private function hitungDataSales(Collection $transaksisPerSales): array
{
- $itemTerjual = $transaksisPerSales->sum(fn ($t) => $t->itemTransaksi->count());
- $beratTerjual = $transaksisPerSales->sum(fn ($t) =>
- $t->itemTransaksi->sum(fn ($it) => $it->item->produk->berat ?? 0)
+ $itemTerjual = $transaksisPerSales->sum(fn($t) => $t->itemTransaksi->count());
+ $beratTerjual = $transaksisPerSales->sum(
+ fn($t) =>
+ $t->itemTransaksi->sum(fn($it) => $it->item->produk->berat ?? 0)
);
$pendapatan = $transaksisPerSales->sum('total_harga');
@@ -151,7 +155,7 @@ class LaporanController extends Controller
'pendapatan_raw' => $pendapatan,
];
}
-
+
private function defaultSalesData(string $namaSales): array
{
return [
@@ -168,9 +172,105 @@ class LaporanController extends Controller
$sale['item_terjual'] = $sale['item_terjual'] > 0 ? $sale['item_terjual'] : '-';
$sale['berat_terjual'] = $sale['berat_terjual_raw'] > 0 ? number_format($sale['berat_terjual_raw'], 2, ',', '.') . 'g' : '-';
$sale['pendapatan'] = $sale['pendapatan_raw'] > 0 ? 'Rp' . number_format($sale['pendapatan_raw'], 2, ',', '.') : '-';
-
+
unset($sale['berat_terjual_raw'], $sale['pendapatan_raw']);
return $sale;
});
}
-}
\ No newline at end of file
+
+public function detail(Request $request)
+ {
+ // 1. VALIDASI DAN PENGAMBILAN PARAMETER FILTER
+ $request->validate([
+ 'tanggal' => 'required|date_format:Y-m-d',
+ ]);
+
+ $tanggal = $request->query('tanggal');
+ $namaSales = $request->query('nama_sales');
+ $posisi = $request->query('posisi');
+ $namaPembeli = $request->query('nama_pembeli'); // Untuk pencarian
+
+ $carbonDate = Carbon::parse($tanggal);
+
+ // 2. QUERY UTAMA UNTUK MENGAMBIL DATA PRODUK YANG TERJUAL BERDASARKAN FILTER
+ // Query ini hanya akan mengambil produk yang memiliki transaksi sesuai filter.
+ $produkTerjualQuery = ItemTransaksi::query()
+ ->join('items', 'item_transaksis.id_item', '=', 'items.id')
+ ->join('produks', 'items.id_produk', '=', 'produks.id')
+ ->join('transaksis', 'item_transaksis.id_transaksi', '=', 'transaksis.id')
+ // Filter Wajib: Tanggal
+ ->whereDate('transaksis.created_at', $carbonDate)
+ // Filter Opsional: Nama Sales
+ ->when($namaSales, function ($query, $namaSales) {
+ return $query->where('transaksis.nama_sales', $namaSales);
+ })
+ // Filter Opsional: Posisi Asal Item
+ ->when($posisi, function ($query, $posisi) {
+ return $query->where('item_transaksis.posisi_asal', $posisi);
+ })
+ // Filter Opsional: Nama Pembeli (menggunakan LIKE untuk pencarian)
+ ->when($namaPembeli, function ($query, $namaPembeli) {
+ return $query->where('transaksis.nama_pembeli', 'like', "%{$namaPembeli}%");
+ })
+ ->select(
+ 'produks.id as id_produk',
+ 'produks.nama as nama_produk',
+ DB::raw('COUNT(item_transaksis.id) as jumlah_item_terjual'),
+ DB::raw('SUM(produks.berat) as berat_terjual'),
+ DB::raw('SUM(item_transaksis.harga_deal) as pendapatan')
+ )
+ ->groupBy('produks.id', 'produks.nama')
+ ->get()
+ // Mengubah collection menjadi array asosiatif dengan key id_produk agar mudah dicari
+ ->keyBy('id_produk');
+
+
+ // 3. MENGAMBIL SEMUA PRODUK DARI DATABASE
+ $semuaProduk = Produk::query()->select('id', 'nama')->get();
+
+ // 4. MENGGABUNGKAN DATA SEMUA PRODUK DENGAN PRODUK YANG TERJUAL
+ $detailItem = $semuaProduk->map(function ($produk) use ($produkTerjualQuery) {
+ // Cek apakah produk ini ada di dalam daftar produk yang terjual
+ if ($produkTerjualQuery->has($produk->id)) {
+ $dataTerjual = $produkTerjualQuery->get($produk->id);
+ return [
+ 'nama_produk' => $produk->nama,
+ 'jumlah_item_terjual' => (int) $dataTerjual->jumlah_item_terjual,
+ 'berat_terjual' => (float) $dataTerjual->berat_terjual,
+ 'pendapatan' => (float) $dataTerjual->pendapatan,
+ ];
+ } else {
+ // Jika produk tidak terjual, berikan nilai default "-"
+ return [
+ 'nama_produk' => $produk->nama,
+ 'jumlah_item_terjual' => '-',
+ 'berat_terjual' => '-',
+ 'pendapatan' => '-',
+ ];
+ }
+ });
+
+ // 5. MENGHITUNG TOTAL REKAP HARIAN DARI DATA YANG SUDAH DIFILTER
+ $totalPendapatan = $produkTerjualQuery->sum('pendapatan');
+ $totalItemTerjual = $produkTerjualQuery->sum('jumlah_item_terjual');
+ $totalBeratTerjual = $produkTerjualQuery->sum('berat_terjual');
+
+ // 6. MENYUSUN STRUKTUR RESPONSE FINAL
+ $response = [
+ 'filter' => [
+ 'tanggal' => $carbonDate->isoFormat('dddd, D MMMM Y'),
+ 'nama_sales' => $namaSales,
+ 'posisi' => $posisi,
+ 'nama_pembeli' => $namaPembeli,
+ ],
+ 'rekap_harian' => [
+ 'total_item_terjual' => $totalItemTerjual,
+ 'total_berat_terjual' => $totalBeratTerjual,
+ 'total_pendapatan' => $totalPendapatan,
+ ],
+ 'produk' => $detailItem,
+ ];
+
+ return response()->json($response);
+ }
+}
diff --git a/app/Models/ItemTransaksi.php b/app/Models/ItemTransaksi.php
index eb9f4ac..994e6e2 100644
--- a/app/Models/ItemTransaksi.php
+++ b/app/Models/ItemTransaksi.php
@@ -13,7 +13,8 @@ class ItemTransaksi extends Model
protected $fillable = [
'id_transaksi',
'id_item',
- 'harga_deal'
+ 'harga_deal',
+ 'posisi_asal'
];
protected $hidden = ['created_at', 'updated_at', 'deleted_at'];
diff --git a/app/Models/Transaksi.php b/app/Models/Transaksi.php
index 31af4ef..3c49d24 100644
--- a/app/Models/Transaksi.php
+++ b/app/Models/Transaksi.php
@@ -37,14 +37,4 @@ class Transaksi extends Model
{
return $this->hasMany(ItemTransaksi::class, 'id_transaksi');
}
-
- public function items()
- {
- return $this->hasMany(ItemTransaksi::class, 'id_transaksi');
- }
-
- public function foto ()
- {
- return $this->hasMany(Foto::class, 'id_produk');
- }
}
diff --git a/database/factories/ProdukFactory.php b/database/factories/ProdukFactory.php
index 59c6fde..1d6901b 100644
--- a/database/factories/ProdukFactory.php
+++ b/database/factories/ProdukFactory.php
@@ -17,12 +17,16 @@ class ProdukFactory extends Factory
*/
public function definition(): array
{
+ $kategori = Kategori::inRandomOrder()->first();
+
$harga_per_gram = $this->faker->numberBetween(80, 120) * 10000;
$berat = $this->faker->randomFloat(2, 1, 10);
- $kategoriList = Kategori::all()->pluck('id')->toArray();
+
return [
- 'nama' => $this->faker->words(3, true),
- 'id_kategori' => $this->faker->randomElement($kategoriList),
+ 'nama' => $kategori->nama . ' ' . $this->faker->words(mt_rand(1, 2), true),
+
+ 'id_kategori' => $kategori->id,
+
'berat' => $berat,
'kadar' => $this->faker->numberBetween(10, 24),
'harga_per_gram' => $harga_per_gram,
diff --git a/database/factories/TransaksiFactory.php b/database/factories/TransaksiFactory.php
index 999c8bb..fee977d 100644
--- a/database/factories/TransaksiFactory.php
+++ b/database/factories/TransaksiFactory.php
@@ -23,15 +23,16 @@ class TransaksiFactory extends Factory
$kasir = User::inRandomOrder()->first();
$date = $this->faker->dateTimeBetween('-3 months');
+ $ongkos_bikin = $this->faker->numberBetween(8, 12) * 10000;
return [
'id_kasir' => $kasir?->id,
'id_sales' => $sales?->id,
- 'nama_sales' => $sales?->nama ?? $this->faker->name(),
- 'nama_pembeli' => $sales?->nama ?? $this->faker->name(),
+ 'nama_sales' => $sales?->nama,
+ 'nama_pembeli' => $this->faker->name(),
'no_hp' => $this->faker->phoneNumber(),
'alamat' => $this->faker->address(),
- 'ongkos_bikin' => $this->faker->randomFloat(2, 0, 1000000),
- 'total_harga' => $this->faker->randomFloat(2, 100000, 5000000),
+ 'ongkos_bikin' => $ongkos_bikin,
+ 'total_harga' => $ongkos_bikin,
'created_at' => $date,
'updated_at' => $date,
];
diff --git a/database/migrations/2025_08_26_031033_create_item_transaksis_table.php b/database/migrations/2025_08_26_031033_create_item_transaksis_table.php
index 25d4629..929e80b 100644
--- a/database/migrations/2025_08_26_031033_create_item_transaksis_table.php
+++ b/database/migrations/2025_08_26_031033_create_item_transaksis_table.php
@@ -16,6 +16,7 @@ return new class extends Migration
$table->foreignId('id_transaksi')->constrained('transaksis')->onDelete('cascade');
$table->foreignId('id_item')->constrained('items');
$table->double('harga_deal');
+ $table->string('posisi_asal', 100);
$table->timestamps();
});
}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 3f3816c..33bbc37 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -77,17 +77,24 @@ class DatabaseSeeder extends Seeder
}
}
- Transaksi::factory(20)->create()->each(function ($transaksi) {
- $jumlah_item = rand(1, 5);
+ Transaksi::factory(40)->create()->each(function ($transaksi) {
+ $jumlah_item = rand(1, 2);
$items = Item::where('is_sold', false)->inRandomOrder()->limit($jumlah_item)->get();
if ($items->isEmpty()) return;
+ $total_harga = $transaksi->total_harga;
foreach ($items as $item) {
$transaksi->itemTransaksi()->create([
'id_item' => $item->id,
'harga_deal' => $item->produk->harga_jual,
+ 'posisi_asal' => $item->id_nampan ? 'Nampan ' . $item->nampan->nama : 'Brankas',
]);
- $item->update(['is_sold' => true]);
+ $item->update([
+ 'id_nampan' => null,
+ 'is_sold' => true,
+ ]);
+ $total_harga += $item->produk->harga_jual;
}
+ $transaksi->update(['total_harga' => $total_harga]);
});
}
}
diff --git a/resources/js/components/DetailLaporan.vue b/resources/js/components/DetailLaporan.vue
new file mode 100644
index 0000000..9be9c5c
--- /dev/null
+++ b/resources/js/components/DetailLaporan.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+ Nama Produk
+ Item Terjual
+ Total Berat
+ Total Pendapatan
+
+
+
+
+
+
+
+ Tidak ada data untuk ditampilkan.
+
+
+
+
+ {{ item.nama_produk }}
+ {{ item.jumlah_item_terjual }}
+ {{ item.berat_terjual }} gr
+ Rp {{ item.pendapatan }}
+
Laporan