diff --git a/app/Http/Controllers/PembiayaanOnlineController.php b/app/Http/Controllers/PembiayaanOnlineController.php index 9dfee280..b267a7df 100644 --- a/app/Http/Controllers/PembiayaanOnlineController.php +++ b/app/Http/Controllers/PembiayaanOnlineController.php @@ -115,6 +115,19 @@ class PembiayaanOnlineController extends Controller 'update_customer_bank' => true, ]; + if ($request->hasFile('bukti_akaun')) { + $file = $request->file('bukti_akaun'); + if (!$file->isValid()) { + return redirect()->back()->with('error', 'Fail slip bank tidak sah.'); + } + if ($file->getSize() > 4 * 1024 * 1024) { + return redirect()->back()->with('error', 'Saiz fail maksimum 4MB.'); + } + $payload['bukti_akaun_data'] = base64_encode(file_get_contents($file->getRealPath())); + $payload['bukti_akaun_filename'] = $file->getClientOriginalName(); + $payload['bukti_akaun_type'] = $file->getMimeType() ?: 'application/octet-stream'; + } + $res = $this->api() ->asForm() ->put($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id, $payload); @@ -125,7 +138,55 @@ class PembiayaanOnlineController extends Controller return redirect()->back()->with('error', $msg); } - return redirect()->route('pembiayaan.online.pc')->with('message', 'Rekod dikemaskini.'); + return redirect()->route('pembiayaan.online.pc') + ->with('message', 'Rekod dikemaskini. Sila semak semula sebelum hantar ke Operasi.'); + } + + /** + * PC verifies one queue line before it can be submitted to Operasi. + */ + public function verifyLine(Request $request, $id) + { + $this->abortUnlessPembiayaanOnline(); + $kodcaw = $this->kodcaw(); + $api_url = config('api.url'); + $auth_user = Auth::user(); + + $res = $this->api() + ->asForm() + ->post($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/verify', [ + 'pc_verified_by' => $auth_user['name'], + ]); + + if (!$res->successful()) { + $body = $res->json(); + $msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal semak rekod.'; + return redirect()->back()->with('error', $msg); + } + + return redirect()->route('pembiayaan.online.pc')->with('message', 'Rekod disahkan. Boleh dipilih untuk hantar ke Operasi.'); + } + + /** + * Undo PC verification on a queue line. + */ + public function unverifyLine(Request $request, $id) + { + $this->abortUnlessPembiayaanOnline(); + $kodcaw = $this->kodcaw(); + $api_url = config('api.url'); + + $res = $this->api() + ->asForm() + ->post($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/unverify'); + + if (!$res->successful()) { + $body = $res->json(); + $msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal batal semakan.'; + return redirect()->back()->with('error', $msg); + } + + return redirect()->route('pembiayaan.online.pc')->with('message', 'Semakan dibatalkan. Sila semak semula sebelum hantar.'); } /** @@ -259,17 +320,87 @@ class PembiayaanOnlineController extends Controller return redirect()->back()->with('error', 'Fail bukti kosong.'); } - $raw = $bukti['data']; + return $this->streamBase64File($bukti, 'bukti-' . $id); + } + + /** + * PC: view uploaded customer bank slip. + */ + public function viewBuktiAkaun($id) + { + $this->abortUnlessPembiayaanOnline(); + $kodcaw = $this->kodcaw(); + $res = $this->api() + ->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/bukti-akaun'); + + if (!$res->successful()) { + $body = $res->json(); + $msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Slip bank tidak dijumpai.'; + return redirect()->back()->with('error', $msg); + } + + $payload = $res->json(); + $file = $payload['file'] ?? null; + if (!$file || empty($file['data'])) { + return redirect()->back()->with('error', 'Fail slip bank kosong.'); + } + + return $this->streamBase64File($file, 'slip-bank-' . $id); + } + + /** + * PC: view SAG for a queue line (does not mark SAG as printed). + */ + public function viewSag($id) + { + $this->abortUnlessPembiayaanOnline(); + return $this->renderLineSag($this->kodcaw(), $id); + } + + /** + * Operasi: view uploaded customer bank slip. + */ + public function operasiViewBuktiAkaun($kodcaw, $id) + { + $res = $this->api() + ->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/bukti-akaun'); + + if (!$res->successful()) { + $body = $res->json(); + $msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Slip bank tidak dijumpai.'; + return redirect()->back()->with('error', $msg); + } + + $payload = $res->json(); + $file = $payload['file'] ?? null; + if (!$file || empty($file['data'])) { + return redirect()->back()->with('error', 'Fail slip bank kosong.'); + } + + return $this->streamBase64File($file, 'slip-bank-' . $id); + } + + /** + * Operasi: view SAG for a batch line. + */ + public function operasiViewSag($kodcaw, $id) + { + return $this->renderLineSag($kodcaw, $id); + } + + protected function streamBase64File(array $file, $fallbackName) + { + $raw = $file['data']; if (strpos($raw, 'base64,') !== false) { $raw = substr($raw, strpos($raw, 'base64,') + 7); } $binary = base64_decode($raw, true); if ($binary === false) { - $binary = base64_decode($bukti['data']); + $binary = base64_decode($file['data']); } - $type = $bukti['type'] ?? 'application/octet-stream'; - $filename = $bukti['filename'] ?? ('bukti-' . $id); + $type = $file['type'] ?? 'application/octet-stream'; + $filename = $file['filename'] ?? $fallbackName; return response($binary, 200, [ 'Content-Type' => $type, @@ -277,6 +408,115 @@ class PembiayaanOnlineController extends Controller ]); } + protected function renderLineSag($kodcaw, $id) + { + $api_url = config('api.url'); + $res = $this->api()->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id); + if (!$res->successful()) { + $body = $res->json(); + $msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Rekod tidak dijumpai.'; + return redirect()->back()->with('error', $msg); + } + + $line = $res->json(); + $norujukan = is_array($line) ? trim((string) ($line['norujukan'] ?? '')) : ''; + if ($norujukan === '') { + return redirect()->back()->with('error', 'No. SAG tidak dijumpai.'); + } + + return $this->renderSagByNorujukan($kodcaw, $norujukan); + } + + protected function renderSagByNorujukan($kodcaw, $norujukan) + { + $api_url = config('api.url'); + $auth_user = Auth::user(); + $auth_username = $auth_user['name'] ?? ''; + + $gadai = $this->api() + ->get($api_url . '/api/gadai/norujukan/' . $kodcaw . '/' . $norujukan) + ->json(); + $gadai_detail = is_array($gadai) ? ($gadai[0] ?? null) : null; + if (!is_array($gadai_detail) || empty($gadai_detail['norujukan'])) { + return redirect()->back()->with('error', 'SAG tidak dijumpai.'); + } + + $kakitangan = \App\User::where([['role_harian', 'khazanah'], ['cawangan', $kodcaw]])->first(); + + $customer = $this->api() + ->get($api_url . '/api/customers/' . ($gadai_detail['nokp'] ?? '')) + ->json(); + $customer_detail = is_array($customer) ? $customer : []; + + $marhun = $this->api() + ->get($api_url . '/api/marhun/' . $norujukan) + ->json(); + $marhun_detail = is_array($marhun) ? ($marhun[0] ?? []) : []; + if (!is_array($marhun_detail)) { + $marhun_detail = []; + } + + $mutu = $this->api() + ->get($api_url . '/api/marhun/mutu/sag', [ + 'kodcaw' => $kodcaw, + 'norujukan' => $norujukan, + ]) + ->json(); + + $cawangan = $this->api() + ->get($api_url . '/api/cawangan/detail/' . $kodcaw) + ->json(); + $cawangan_detail = is_array($cawangan) ? ($cawangan[0] ?? []) : []; + + $kadar_ujrah = $this->api() + ->get($api_url . '/api/kadarujrah/by_group_id/', [ + 'kodcaw' => $cawangan_detail['kodcaw'] ?? $kodcaw, + ]) + ->json(); + + $pembayaran_segera = 'https://pay.arrahn.com.my'; + + $komoditi = $this->api()->get($api_url . '/api/komuditi/check')->json(); + $product = $this->api() + ->get($api_url . '/api/komuditi/description', [ + 'kodproduk' => is_array($komoditi) ? ($komoditi['kodproduct'] ?? '') : '', + ]) + ->json(); + + $nilai = is_array($komoditi) ? (float) ($komoditi['nilai'] ?? 0) : 0; + $kuantiti = $nilai > 0 ? ((float) ($gadai_detail['pinjaman'] ?? 0) / $nilai) : 0; + + if (($gadai_detail['tagbaru'] ?? 0) == 1) { + return view('print.sag_ujrah_3', compact( + 'komoditi', + 'product', + 'kuantiti', + 'gadai_detail', + 'customer_detail', + 'marhun_detail', + 'cawangan_detail', + 'api_url', + 'pembayaran_segera', + 'auth_username', + 'mutu', + 'kadar_ujrah', + 'kakitangan' + )); + } + + return view('print.sag', compact( + 'gadai_detail', + 'customer_detail', + 'marhun_detail', + 'cawangan_detail', + 'api_url', + 'pembayaran_segera', + 'auth_username', + 'mutu', + 'kakitangan' + )); + } + /** * PC: tanda rekod PAID sebagai selesai semak / blast. */ diff --git a/resources/views/components/datatable.blade.php b/resources/views/components/datatable.blade.php index fbe981b6..8dff122d 100644 --- a/resources/views/components/datatable.blade.php +++ b/resources/views/components/datatable.blade.php @@ -131,7 +131,8 @@ @endforeach -
- {{ $slot }} + + {{ $slot }} + diff --git a/resources/views/dummy/borang_pengesahan_online.blade.php b/resources/views/dummy/borang_pengesahan_online.blade.php index acdc772f..1f88054d 100644 --- a/resources/views/dummy/borang_pengesahan_online.blade.php +++ b/resources/views/dummy/borang_pengesahan_online.blade.php @@ -243,7 +243,7 @@| Status | -SAG | -Pelanggan | -No KP | -Bank | -No Akaun | -Nama Akaun | -Pinjaman (RM) | -Rujukan Bayaran | -Tindakan | +{{ $loop->iteration }} | +{{ $st ?: '-' }} | +{{ $line['norujukan'] ?? '-' }} | +{{ $line['nama_pelanggan'] ?? '-' }} | +{{ $line['nokp'] ?? '-' }} | +{{ $line['nama_bank'] ?? $line['kodbank'] ?? '-' }} | +{{ $line['noakaun'] ?? '-' }} | +{{ $line['nama_akaun'] ?? '-' }} | +{{ number_format((float)($line['pinjaman'] ?? 0), 2) }} | +{{ $line['no_rujukan_bayaran'] ?? '-' }} | ++ + @if(!empty($line['bukti_akaun_file_id'])) + + Slip Bank + + @else + Tiada slip + @endif + | ++ @if(in_array($st, ['IN_BATCH', 'OPS_REVIEW'], true)) + + + @elseif($st === 'APPROVED') + + + @elseif($st === 'PAID') + Selesai + @else + - + @endif + |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ $st ?: '-' }} | -{{ $line['norujukan'] ?? '-' }} | -{{ $line['nama_pelanggan'] ?? '-' }} | -{{ $line['nokp'] ?? '-' }} | -{{ $line['nama_bank'] ?? $line['kodbank'] ?? '-' }} | -{{ $line['noakaun'] ?? '-' }} | -{{ $line['nama_akaun'] ?? '-' }} | -{{ number_format((float)($line['pinjaman'] ?? 0), 2) }} | -{{ $line['no_rujukan_bayaran'] ?? '-' }} | -- @if(in_array($st, ['IN_BATCH', 'OPS_REVIEW'], true)) - - - @elseif($st === 'APPROVED') - - - @elseif($st === 'PAID') - Selesai - @else - - - @endif - | -||||||||||||
| Tiada rekod dalam batch. | -|||||||||||||||||||||
+ Semak setiap rekod satu-satu (maklumat pelanggan, bank, amaun). + Hanya rekod berstatus SAH boleh dipilih dan dihantar ke Operasi. +