Experiment/merge annur (#20)
Build Docker Image / build-frontend (push) Successful in 55s

Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Reviewed-on: #20
This commit was merged in pull request #20.
This commit is contained in:
2026-09-20 12:49:39 +08:00
parent 8a2bae7a88
commit 918502b504
30 changed files with 1011 additions and 190 deletions
@@ -23,9 +23,10 @@ class AdminFeatureController extends Controller
$metaRes = $this->api()->get($api_url . '/api/features');
$meta = $metaRes->successful() ? $metaRes->json() : [];
$keys = is_array($meta) && !empty($meta['keys']) ? $meta['keys'] : ['pembiayaan_online'];
$keys = is_array($meta) && !empty($meta['keys']) ? $meta['keys'] : ['pembiayaan_online', 'rebet_annur'];
$labels = is_array($meta) && !empty($meta['labels']) ? $meta['labels'] : [
'pembiayaan_online' => 'Pembiayaan Online',
'rebet_annur' => 'Rebet / diskaun Annur',
];
$listRes = $this->api()->get($api_url . '/api/features/cawangan');
+22 -2
View File
@@ -32,14 +32,30 @@ class GadaianController extends Controller
$this->AliranTunaiController = new AliranTunaiController();
}
protected function isPembiayaanOnlineEnabled($kodcaw)
protected function isFeatureEnabled($kodcaw, $feature)
{
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])
->get(config('api.url') . '/api/cawangan/' . $kodcaw)
->json();
$features = is_array($cawangan_info) ? ($cawangan_info['features'] ?? []) : [];
return !empty($features['pembiayaan_online']);
return !empty($features[$feature]);
}
protected function isPembiayaanOnlineEnabled($kodcaw)
{
return $this->isFeatureEnabled($kodcaw, 'pembiayaan_online');
}
protected function parseMoney($value)
{
if ($value === null || $value === '') {
return 0.0;
}
$cleaned = preg_replace('/[^0-9.\-]/', '', str_replace([',', '%', ' '], '', (string) $value));
return is_numeric($cleaned) ? (float) $cleaned : 0.0;
}
public function index(Request $request)
@@ -211,6 +227,8 @@ class GadaianController extends Controller
'onepercent' => $onepercent,
'margin_semasa' => $margin_pinjaman,
'nokp' => $request->nokp,
'kadar_rebate' => $this->parseMoney($request->kadar_rebate),
'rebate' => $this->parseMoney($request->rebate),
])->json();
@@ -423,6 +441,8 @@ class GadaianController extends Controller
'ujrah' => $ujrah,
'onepercent' => $onepercent,
'margin_semasa' => $margin_pinjaman,
'kadar_rebate' => $this->parseMoney($request->kadar_rebate),
'rebate' => $this->parseMoney($request->rebate),
])->json();
$update_komuditi = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/komuditi/update', [
'unit_komuditi' => round($request->pinjaman, 2),
+15 -2
View File
@@ -2473,6 +2473,14 @@ class KhazanahController extends Controller
$hapuskira = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/CalculationHapusKira/' . $auth_user['cawangan'],['tarikh' => $tarikh_imbangan2])->json();
$xtra = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/Xtra/' . $auth_user['cawangan'],['tarikh' => $tarikh_imbangan2])->json();
$rebetAnnur = !empty(($cawangan_info['features'] ?? [])['rebet_annur']);
$rebetpromo = 0;
$pelarasanrebate = 0;
if ($rebetAnnur) {
$rebetpromo = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/RebetPromoCalculation/' . $auth_user['cawangan'],['tarikh' => $tarikh_imbangan2])->json();
$pelarasanrebate = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/PelarasanRebate/' . $auth_user['cawangan'],['tarikh' => $tarikh_imbangan2])->json();
}
//TunaiDitangan Sementara Upah
if($tarikh_imbangan2 >= '2021-04-22' && $tarikh_imbangan2 < '2021-05-05')
@@ -2523,6 +2531,7 @@ class KhazanahController extends Controller
if($imbangduga['pindahkekpkb'] > 0) array_push($hartasemasa,['glcode' => '1000/090','descpt' => 'Pindah Ke KPKB(IP)', 'amaun' => $imbangduga['pindahkekpkb']]);
if($peruntukanhapuskira > 0) array_push($hartasemasa,['glcode' => '1000/015','descpt' => 'Peruntukan Hapus Kira', 'amaun' => $peruntukanhapuskira]);
if($belian1stop > 0) array_push($hartasemasa,['glcode' => '8000/501','descpt' => 'Belian 1 Stop', 'amaun' => $belian1stop]);
if($rebetpromo > 0) array_push($hartasemasa,['glcode' => '5000/242','descpt' => 'Diskaun Promosi', 'amaun' => ($rebetpromo * -1)]);
$totalhartasemasa = 0;
foreach($hartasemasa as $index => $hs)
@@ -2554,6 +2563,8 @@ class KhazanahController extends Controller
if($utgterkumpulxtra > 0) array_push($tanggungansemasa,['glcode' => '7000/201','descpt' => 'Untung/Rugi Terkumpul Xtra','amaun' => $utgterkumpulxtra]);
if($jualan1stop > 0) array_push($tanggungansemasa,['glcode' => '9000/501','descpt' => 'Jualan 1 Stop','amaun' => $jualan1stop]);
if($hapuskira > 0) array_push($tanggungansemasa,['glcode' => '3000/011','descpt' => 'Hapus Kira','amaun' => -$hapuskira]);
if($rebetpromo > 0) array_push($tanggungansemasa,['glcode' => '5000/242','descpt' => 'Diskaun Promosi', 'amaun' => ($rebetpromo * -1)]);
if($pelarasanrebate > 0) array_push($tanggungansemasa,['glcode' => '5000/243','descpt' => 'Pelarasan Rebate', 'amaun' => ($pelarasanrebate * -1)]);
$totaltanggungansemasa = 0;
foreach($tanggungansemasa as $index => $ts)
@@ -4031,8 +4042,9 @@ class KhazanahController extends Controller
$cawangan=Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/cawangan/detail/'.$auth_user['cawangan'])->json();
$cawangan_detail=$cawangan[0];
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
return view('print.laporan_gadaian_ujrah', compact('gadai','cawangan_detail','tarikh_cetak','mula_date','api_url','hingga_date','statistik','reportPembiayaan','reportLoanAmount','reportMarhunValue','reportMarhunMargin','reportCustomerJob','reportCustomerRace','reportCustomerRace'));
return view('print.laporan_gadaian_ujrah', compact('gadai','cawangan_detail','cawangan_info','tarikh_cetak','mula_date','api_url','hingga_date','statistik','reportPembiayaan','reportLoanAmount','reportMarhunValue','reportMarhunMargin','reportCustomerJob','reportCustomerRace','reportCustomerRace'));
}
@@ -4075,7 +4087,8 @@ class KhazanahController extends Controller
$cawangan=Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/cawangan/detail/'.$auth_user['cawangan'])->json();
$cawangan_detail=$cawangan[0];
return view('print.laporan_tebus_ujrah', compact('tebus','cawangan_detail','tarikh_cetak','mula_date','hingga_date','tebuspengasingan','api_url','upahlama','ansuran','totalkeuntunganrebet'));
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
return view('print.laporan_tebus_ujrah', compact('tebus','cawangan_detail','cawangan_info','tarikh_cetak','mula_date','hingga_date','tebuspengasingan','api_url','upahlama','ansuran','totalkeuntunganrebet'));
}
public function laporanAnsuranUjrah(){
@@ -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.
*/