validate([ 'field' => 'nullable|in:id,no_pol,no_barcode', 'q' => 'nullable|string|max:100', 'tgl_awal' => 'nullable|date_format:Y-m-d', 'tgl_akhir' => 'nullable|date_format:Y-m-d|after_or_equal:tgl_awal', 'status_kendaraan' => 'nullable|in:0,1', 'status_transaksi' => 'nullable|in:0,3,32', 'sort_by' => 'nullable|in:' . implode(',', $sortable), 'sort_dir' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:500', ]); $query = $this->filteredQuery($request); if ($request->query('export') === 'csv') { return $this->exportCsv($query); } $perPage = (int) $request->input('per_page', 50); $rows = $query->paginate($perPage); return response()->json([ 'status' => true, 'data' => $rows->items(), 'summary' => $this->summary($request), 'meta' => [ 'current_page' => $rows->currentPage(), 'per_page' => $rows->perPage(), 'total' => $rows->total(), 'last_page' => $rows->lastPage(), ], ]); } /** * Rekap sedetail logic report (per segmen) dari filter yang sedang aktif. * - keluar = status 0 ; tunai = cara_bayar null/0 ; cashless = cara_bayar 3 * - casual/member/denda/manual/masalah = subset tunai (ikut reportOfficer) */ protected function summary(Request $request): array { $tunai = "(tp.cara_bayar IS NULL OR tp.cara_bayar = 0)"; $keluar = "tp.status = 0"; $query = $this->filteredQuery($request); $query->reorder(); $query->columns = []; $row = $query ->selectRaw(" count(*) as total_rows, coalesce(sum(tp.bayar_keluar), 0) as total_bayar, count(case when {$keluar} then 1 end) as keluar_rows, coalesce(sum(case when {$keluar} then tp.bayar_keluar end), 0) as keluar_bayar, count(case when {$keluar} and {$tunai} then 1 end) as tunai_rows, coalesce(sum(case when {$keluar} and {$tunai} then tp.bayar_keluar end), 0) as tunai_bayar, count(case when {$keluar} and tp.cara_bayar = 3 then 1 end) as cashless_rows, coalesce(sum(case when {$keluar} and tp.cara_bayar = 3 then tp.bayar_keluar end), 0) as cashless_bayar, count(case when {$keluar} and {$tunai} and tp.status_transaksi = '0' and tp.id_op_masuk is not null then 1 end) as casual_rows, coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '0' and tp.id_op_masuk is not null then tp.bayar_keluar end), 0) as casual_bayar, count(case when {$keluar} and {$tunai} and tp.status_transaksi = '3' then 1 end) as member_rows, coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '3' then tp.bayar_keluar end), 0) as member_bayar, count(case when {$keluar} and {$tunai} and tp.status_transaksi = '32' then 1 end) as denda_rows, coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '32' then tp.bayar_keluar end), 0) as denda_bayar, count(case when {$keluar} and {$tunai} and tp.id_op_masuk is null then 1 end) as manual_rows, coalesce(sum(case when {$keluar} and {$tunai} and tp.id_op_masuk is null then tp.bayar_keluar end), 0) as manual_bayar, count(case when {$keluar} and {$tunai} and tp.status_transaksi = '-1' then 1 end) as masalah_rows, coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '-1' then tp.bayar_keluar end), 0) as masalah_bayar ") ->first(); return (array) $row; } protected function filteredQuery(Request $request) { $query = DB::table('transaksi_parkir as tp') ->leftJoin('jenis_mobil as jm', 'jm.id', '=', 'tp.id_kendaraan') ->select( 'tp.id', 'tp.no_pol', 'tp.no_barcode', 'tp.id_kendaraan', 'jm.nama as nama_kendaraan', 'tp.status', 'tp.status_transaksi', 'tp.waktu_masuk', 'tp.waktu_keluar', 'tp.tanggal', 'tp.id_op_masuk', 'tp.id_op_keluar', 'tp.id_pintu_masuk', 'tp.id_pintu_keluar', 'tp.bayar_keluar' ); // 1. Pencarian: id / no_pol / no_barcode if ($request->filled('q')) { $field = $request->input('field', 'id'); $query->where("tp.{$field}", 'ILIKE', '%' . $request->input('q') . '%'); } // 2. Periode awal - akhir (kolom tanggal) if ($request->filled('tgl_awal')) { $query->whereDate('tp.tanggal', '>=', $request->input('tgl_awal')); } if ($request->filled('tgl_akhir')) { $query->whereDate('tp.tanggal', '<=', $request->input('tgl_akhir')); } // 3. Status kendaraan: 1 = masuk, 0 = keluar if ($request->filled('status_kendaraan')) { $query->where('tp.status', (int) $request->input('status_kendaraan')); } // 4. Status transaksi: 0 = casual, 3 = member, 32 = denda if ($request->filled('status_transaksi')) { $query->where('tp.status_transaksi', (int) $request->input('status_transaksi')); } // 5. Sorting klik header (default: tanggal terbaru) $sortMap = ['nama_kendaraan' => 'jm.nama']; $sortBy = $request->input('sort_by', 'tanggal'); $sortDir = $request->input('sort_dir', 'desc'); $query->orderBy($sortMap[$sortBy] ?? "tp.{$sortBy}", $sortDir); return $query; } protected function exportCsv($query): StreamedResponse { $filename = 'transaksi-parkir-' . now()->format('Ymd-His') . '.csv'; return response()->streamDownload(function () use ($query) { $out = fopen('php://output', 'w'); fputcsv($out, ['ID', 'No. Pol', 'Barcode', 'Kendaraan', 'Status Kendaraan', 'Status Transaksi', 'Waktu Masuk', 'Waktu Keluar', 'Op Masuk', 'Op Keluar', 'Pintu Masuk', 'Pintu Keluar', 'Bayar Keluar']); $query->chunk(1000, function ($rows) use ($out) { foreach ($rows as $r) { fputcsv($out, [ $r->id, $r->no_pol, $r->no_barcode, trim(($r->id_kendaraan ?? '') . ' ' . ($r->nama_kendaraan ?? '')), $r->status, $r->status_transaksi, $r->waktu_masuk, $r->waktu_keluar, $r->id_op_masuk, $r->id_op_keluar, $r->id_pintu_masuk, $r->id_pintu_keluar, $r->bayar_keluar, ]); } }); fclose($out); }, $filename, ['Content-Type' => 'text/csv']); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // } }