diff --git a/app/Http/Controllers/GadaianController.php b/app/Http/Controllers/GadaianController.php index 49f10f80..95423895 100644 --- a/app/Http/Controllers/GadaianController.php +++ b/app/Http/Controllers/GadaianController.php @@ -93,6 +93,65 @@ class GadaianController extends Controller return is_numeric($cleaned) ? (float) $cleaned : 0.0; } + protected function normalizeBorangOnlineFields($norujukan, $pinjaman, $kodbank, $noakaun, $namaAkaun) + { + return [ + 'norujukan' => trim((string) $norujukan), + 'pinjaman' => number_format((float) $this->parseMoney($pinjaman), 2, '.', ''), + 'kodbank' => strtoupper(trim((string) $kodbank)), + 'noakaun' => preg_replace('/\s+/', '', (string) $noakaun), + 'nama_akaun' => strtoupper(trim((string) $namaAkaun)), + ]; + } + + protected function borangOnlineFingerprint(array $fields) + { + return sha1(implode('|', [ + $fields['norujukan'], + $fields['pinjaman'], + $fields['kodbank'], + $fields['noakaun'], + $fields['nama_akaun'], + ])); + } + + protected function markBorangOnlinePrinted(array $fields) + { + Session::put('borang_online_printed', array_merge($fields, [ + 'fingerprint' => $this->borangOnlineFingerprint($fields), + ])); + } + + protected function hasMatchingBorangOnlinePrint(Request $request) + { + $printed = $request->session()->get('borang_online_printed'); + if (!is_array($printed)) { + return false; + } + + $norujukan = (string) $request->input('norujukan_gadai', $request->input('norujukan')); + $fields = $this->normalizeBorangOnlineFields( + $norujukan, + $request->input('pinjaman'), + $request->input('kodbank'), + $request->input('noakaun'), + $request->input('nama_akaun') ?: $request->input('nama_pelanggan') + ); + + return ($printed['norujukan'] ?? '') === $fields['norujukan'] + && ($printed['fingerprint'] ?? '') === $this->borangOnlineFingerprint($fields); + } + + protected function borangOnlinePrintedFor($norujukan) + { + $printed = Session::get('borang_online_printed'); + if (!is_array($printed) || ($printed['norujukan'] ?? '') !== (string) $norujukan) { + return null; + } + + return $printed; + } + public function index(Request $request) { @@ -120,6 +179,7 @@ class GadaianController extends Controller Session::put('nokp', $noic); Session::put('nopelanggan', $customer_info['nopelanggan']); Session::put('page_reload', true); + Session::forget('borang_online_printed'); $gadai = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/gadai/' . $auth_user['cawangan'], [ 'norujukan' => $norujukan, 'nopelanggan' => $customer_info['nopelanggan'], @@ -144,6 +204,7 @@ class GadaianController extends Controller Session::put('nokp', $nokp); Session::put('nopelanggan', $nopelanggan); Session::put('page_reload', true); + Session::forget('borang_online_printed'); $gadai_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/gadai/' . $auth_user['cawangan'], [ 'norujukan' => $norujukan_baru, 'nopelanggan' => $nopelanggan, @@ -156,7 +217,8 @@ class GadaianController extends Controller $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); - return view('gadaian.' . $cawangan_info['version'] . '.index', compact('customer_info', 'limit_pinjaman_semasa', 'cawangan_info', 'norujukan', 'api_url', 'current')); + $borang_online_printed = $this->borangOnlinePrintedFor($norujukan); + return view('gadaian.' . $cawangan_info['version'] . '.index', compact('customer_info', 'limit_pinjaman_semasa', 'cawangan_info', 'norujukan', 'api_url', 'current', 'borang_online_printed')); } public function edit(Request $request, $norujukan) @@ -223,6 +285,22 @@ class GadaianController extends Controller public function store(Request $request) { $auth_user = Auth::user(); + $caraTerima = strtoupper((string) $request->input('cara_terima', 'TUNAI')); + $pinjamanAmt = round((float) $request->pinjaman, 2); + + // Online only within min/max pinjaman; otherwise force cash path. + if ($caraTerima === 'ONLINE' && ($pinjamanAmt < self::MIN_PINJAMAN || $pinjamanAmt > self::MAX_PINJAMAN)) { + $caraTerima = 'TUNAI'; + } + + if ($caraTerima === 'ONLINE' && !$this->isPembiayaanOnlineEnabled($auth_user['cawangan'])) { + $caraTerima = 'TUNAI'; + } + + if ($caraTerima === 'ONLINE' && !$this->hasMatchingBorangOnlinePrint($request)) { + return redirect()->back()->with('error', 'Sila cetak borang pengesahan pelanggan sebelum tekan Terima.'); + } + //$total_nilai_marhun = $request->total_nilai_marhun; //need to get total nilai marhun by api get from table marhun @@ -274,18 +352,6 @@ class GadaianController extends Controller 'teller' => $auth_user['name'] ]); - $caraTerima = strtoupper((string) $request->input('cara_terima', 'TUNAI')); - $pinjamanAmt = round((float) $request->pinjaman, 2); - - // Online only within min/max pinjaman; otherwise force cash path. - if ($caraTerima === 'ONLINE' && ($pinjamanAmt < self::MIN_PINJAMAN || $pinjamanAmt > self::MAX_PINJAMAN)) { - $caraTerima = 'TUNAI'; - } - - if ($caraTerima === 'ONLINE' && !$this->isPembiayaanOnlineEnabled($auth_user['cawangan'])) { - $caraTerima = 'TUNAI'; - } - if ($caraTerima === 'ONLINE') { $online = Http::withOptions(['verify' => config('app.api_verify')]) ->asForm() @@ -320,6 +386,7 @@ class GadaianController extends Controller $request->session()->forget('page_reload'); $request->session()->forget('norujukan'); + $request->session()->forget('borang_online_printed'); $request->session()->flash('activate_tab', 'SAG_baru'); if ($caraTerima === 'ONLINE') { $request->session()->flash('message', 'Gadai berjaya. Pembiayaan online menunggu semakan PC.'); @@ -371,6 +438,14 @@ class GadaianController extends Controller abort(422, 'Sila lengkapkan maklumat bank sebelum cetak borang.'); } + $this->markBorangOnlinePrinted($this->normalizeBorangOnlineFields( + $line['norujukan'], + $line['pinjaman'], + $line['kodbank'], + $line['noakaun'], + $line['nama_akaun'] ?: $line['nama_pelanggan'] + )); + return $this->renderBorangPengesahanOnline($line, $kodcaw); } @@ -501,6 +576,7 @@ class GadaianController extends Controller Http::withOptions(['verify' => config('app.api_verify')])->delete(config('api.url') . '/api/gadai/batal/' . $auth_user['cawangan'] . '/' . $norujukan); $request->session()->forget('page_reload'); $request->session()->forget('norujukan'); + $request->session()->forget('borang_online_printed'); return redirect()->route('customer_info'); } diff --git a/app/Http/Controllers/PembiayaanOnlineController.php b/app/Http/Controllers/PembiayaanOnlineController.php index 43fbf37a..def2c466 100644 --- a/app/Http/Controllers/PembiayaanOnlineController.php +++ b/app/Http/Controllers/PembiayaanOnlineController.php @@ -97,6 +97,52 @@ class PembiayaanOnlineController extends Controller )); } + /** + * Lightweight payload for the PC layout cutoff banner / browser notification. + */ + public function reminderStatus() + { + $cawangan_info = $this->cawanganInfo(); + if (!$this->isPembiayaanOnlineEnabled($cawangan_info)) { + return response()->json(['enabled' => false]); + } + + $kodcaw = $this->kodcaw(); + $api_url = config('api.url'); + + $queue = $this->api() + ->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/queue') + ->json(); + $modal = $this->api() + ->get($api_url . '/api/modal-online/' . $kodcaw) + ->json(); + if (!is_array($queue)) { + $queue = []; + } + if (!is_array($modal)) { + $modal = []; + } + + $slot = (is_array($queue) && isset($queue['slot_cadangan']) && is_array($queue['slot_cadangan'])) + ? $queue['slot_cadangan'] + : []; + + return response()->json([ + 'enabled' => true, + 'ditutup' => (int) ($modal['ditutup'] ?? 0) === 1, + 'bilangan' => (int) ($queue['bilangan'] ?? 0), + 'slot' => $slot['slot'] ?? 'BATCH1', + 'cutoff' => $slot['cutoff'] ?? '11:00', + 'cutoff_at' => $slot['cutoff_at'] ?? null, + 'server_now' => $slot['server_now'] ?? null, + 'lewat' => !empty($slot['lewat']), + 'baki_minit' => (int) ($slot['baki_minit'] ?? 0), + 'url' => route('pembiayaan.online.pc'), + 'remind_minutes' => 30, + 'last_minute' => 10, + ]); + } + /** * Update bank/docs for a queue line. */ diff --git a/resources/views/gadaian/v2/index.blade.php b/resources/views/gadaian/v2/index.blade.php index 3ba68a2c..57a5ae45 100644 --- a/resources/views/gadaian/v2/index.blade.php +++ b/resources/views/gadaian/v2/index.blade.php @@ -305,6 +305,9 @@ Batal +
@@ -413,6 +416,7 @@ var kodcaw_ujrah = ""; const MIN_PINJAMAN = {{ \App\Http\Controllers\GadaianController::MIN_PINJAMAN }}; const MAX_PINJAMAN = {{ \App\Http\Controllers\GadaianController::MAX_PINJAMAN }}; + var printedBorang = @json($borang_online_printed ?? null); $('form input').keydown(function (e) { if (e.keyCode == 13) { e.preventDefault(); @@ -424,6 +428,13 @@ $(document).on("keydown", disableF5); var norujukan = $('#norujukan').val(); getMarhun(norujukan); + @if(session('error')) + Swal.fire({ + icon: 'error', + title: 'Amaran!', + text: @json(session('error')) + }); + @endif $.ajax({ method:'GET', @@ -1301,6 +1312,51 @@ .then(response => response.json()); } + function currentBorangFields() { + var pinjaman = Number(String($('#pinjaman').val() || '').replace(/,/g, '')); + if (!isFinite(pinjaman)) { + pinjaman = 0; + } + return { + norujukan: ($('#norujukan_gadai').val() || $('#norujukan').val() || '').trim(), + pinjaman: pinjaman.toFixed(2), + kodbank: ($('#gadai_kodbank').val() || '').trim().toUpperCase(), + noakaun: ($('#gadai_noakaun').val() || '').replace(/\s+/g, ''), + nama_akaun: ($('#gadai_nama_akaun').val() || $('input[name="nama_pelanggan"]').val() || '').trim().toUpperCase() + }; + } + + function isOnlineCaraTerima() { + var pembiayaanOnlineEnabled = $('#pembiayaan_online_enabled').val() === '1'; + var pinjaman = Number(String($('#pinjaman').val() || '').replace(/,/g, '')); + return pembiayaanOnlineEnabled + && pinjaman >= MIN_PINJAMAN + && pinjaman <= MAX_PINJAMAN + && $('input[name="cara_terima"]:checked').val() === 'ONLINE'; + } + + function hasMatchingPrint() { + if (!printedBorang) { + return false; + } + var cur = currentBorangFields(); + return printedBorang.norujukan === cur.norujukan + && printedBorang.pinjaman === cur.pinjaman + && printedBorang.kodbank === cur.kodbank + && printedBorang.noakaun === cur.noakaun + && printedBorang.nama_akaun === cur.nama_akaun; + } + + function syncTerimaButton() { + var lock = isOnlineCaraTerima() && !hasMatchingPrint(); + $('#terima_gadai').prop('disabled', lock); + if (lock) { + $('#cetak_borang_hint').show(); + } else { + $('#cetak_borang_hint').hide(); + } + } + function toggleCaraTerima(pinjaman) { var pembiayaanOnlineEnabled = $('#pembiayaan_online_enabled').val() === '1'; if (pembiayaanOnlineEnabled && pinjaman >= MIN_PINJAMAN && pinjaman <= MAX_PINJAMAN) { @@ -1311,6 +1367,7 @@ $('#bank_online_section').hide(); $('#cetak_borang_online').hide(); $('#cara_terima_tunai').prop('checked', true); + syncTerimaButton(); } } @@ -1325,16 +1382,20 @@ $('#bank_online_section').hide(); $('#cetak_borang_online').hide(); } + syncTerimaButton(); } $(document).on('change', 'input[name="cara_terima"]', function () { toggleBankOnlineSection(); }); - $(document).on('change', '#gadai_kodbank', function () { + $(document).on('change input', '#gadai_kodbank, #gadai_noakaun, #gadai_nama_akaun', function () { $('#gadai_nama_bank').val($('#gadai_kodbank option:selected').data('nama') || ''); + syncTerimaButton(); }); + syncTerimaButton(); + $(document).on('click', '#cetak_borang_online', function () { var kodbank = $('#gadai_kodbank').val(); var noakaun = ($('#gadai_noakaun').val() || '').trim(); @@ -1367,6 +1428,9 @@ form.append($('', { type: 'hidden', name: 'noakaun', value: noakaun })); form.append($('', { type: 'hidden', name: 'nama_akaun', value: namaAkaun })); form.appendTo('body').trigger('submit').remove(); + + printedBorang = currentBorangFields(); + syncTerimaButton(); }); async function getModalOnline() { @@ -1523,7 +1587,7 @@ let bakiakhir = 0; var kodcaw = $('#kodcaw').val(); if(pinjaman == 0){ - $("#terima_gadai").prop('disabled', false); + syncTerimaButton(); Swal.fire({ icon: 'error', title: 'Amaran!', @@ -1532,6 +1596,16 @@ return false; } + if (isOnlineCaraTerima() && !hasMatchingPrint()) { + syncTerimaButton(); + Swal.fire({ + icon: 'warning', + title: 'Borang belum dicetak', + text: 'Sila cetak borang pengesahan pelanggan sebelum tekan Terima.' + }); + return false; + } + let floatOk = await checkDisbursementFloat(pinjaman); if (!floatOk) { $("#terima_gadai").prop('disabled', false); diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index c532c7a6..d7d138b2 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -401,6 +401,7 @@ @else @endif + @include('layouts.partials.batch_cutoff_banner') @endif