Feature/whatsapp blast (#24)
Build Docker Image / build-frontend (push) Successful in 53s

Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #24
This commit was merged in pull request #24.
This commit is contained in:
2026-09-24 11:28:05 +08:00
parent b41234e10b
commit c437a3f504
33 changed files with 2278 additions and 357 deletions
+123 -13
View File
@@ -11,6 +11,7 @@ use App\Events\SerahanPenilai;
use Illuminate\Http\Request;
use App\Services\CustomerService;
use App\Services\NotificationService;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Auth;
@@ -50,6 +51,37 @@ class GadaianController extends Controller
return $this->isFeatureEnabled($kodcaw, 'pembiayaan_online');
}
/**
* Inbox row for PC at this branch. Failures must not block gadai.
*/
protected function notifyPcPembiayaanOnlinePending($auth_user, Request $request, $pinjamanAmt)
{
try {
app(NotificationService::class)->notifyRoleAtCawangan('pc', $auth_user['cawangan'], [
'type' => NotificationService::TYPE_PEMBIAYAAN_PENDING_PC,
'title' => 'Pembiayaan online menunggu semakan',
'body' => sprintf(
'%s — %s (RM %s)',
$request->nama_pelanggan,
$request->norujukan_gadai,
number_format((float) $pinjamanAmt, 2)
),
'url' => route('pembiayaan.online.pc'),
'data' => [
'norujukan' => $request->norujukan_gadai,
'nama_pelanggan' => $request->nama_pelanggan,
'pinjaman' => $pinjamanAmt,
'created_by' => $auth_user['name'],
],
]);
} catch (\Throwable $e) {
Log::warning('Pembiayaan online PC notification failed: '.$e->getMessage(), [
'norujukan' => $request->norujukan_gadai,
'cawangan' => $auth_user['cawangan'],
]);
}
}
protected function parseMoney($value)
{
if ($value === null || $value === '') {
@@ -61,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)
{
@@ -88,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'],
@@ -112,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,
@@ -124,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)
@@ -191,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
@@ -242,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()
@@ -278,6 +376,8 @@ class GadaianController extends Controller
: 'Gagal daftar pembiayaan online. Sila cuba lagi.';
return redirect()->back()->with('error', $msg);
}
$this->notifyPcPembiayaanOnlinePending($auth_user, $request, $pinjamanAmt);
} else {
$this->AliranTunaiController->alirantunaiGadai($request, $auth_user['name']);
}
@@ -286,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.');
@@ -337,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);
}
@@ -467,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');
}