Files
kaunter/app/Http/Controllers/PembiayaanOnlineController.php
T
ismailmasseran 72e1a8c142
Build Docker Image / build-frontend (push) Successful in 38s
Explore/ismail (#8)
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Reviewed-on: #8
2026-08-27 10:54:38 +08:00

745 lines
23 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
class PembiayaanOnlineController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
protected function api()
{
return Http::withOptions(['verify' => config('app.api_verify')]);
}
protected function kodcaw()
{
return Auth::user()->cawangan;
}
/**
* PC senarai queue + batch history.
*/
public function index(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$queue = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/queue')
->json();
$batches = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches')
->json();
$banks = $this->api()->get($api_url . '/api/bank')->json();
if (!is_array($banks)) {
$banks = [];
}
$modal = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw)
->json();
$lines = $queue['data'] ?? [];
$slot = $queue['slot_cadangan'] ?? null;
if (!is_array($batches)) {
$batches = [];
}
return view('pembiayaan_online.pc.senarai', compact(
'auth_user',
'kodcaw',
'api_url',
'cawangan_info',
'lines',
'batches',
'banks',
'modal',
'slot',
'queue'
));
}
/**
* Update bank/docs for a queue line.
*/
public function updateLine(Request $request, $id)
{
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$payload = [
'kodbank' => $request->input('kodbank'),
'noakaun' => $request->input('noakaun'),
'nama_bank' => $request->input('nama_bank'),
'nama_akaun' => $request->input('nama_akaun'),
'nota' => $request->input('nota'),
'update_customer_bank' => true,
];
$res = $this->api()
->asForm()
->put($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id, $payload);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal kemaskini rekod.';
return redirect()->back()->with('error', $msg);
}
return redirect()->route('pembiayaan.online.pc')->with('message', 'Rekod dikemaskini.');
}
/**
* Submit selected lines as a batch to Operasi.
*/
public function submitBatch(Request $request)
{
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$auth_user = Auth::user();
$ids = $request->input('ids', []);
if (!is_array($ids)) {
$ids = array_filter(explode(',', (string) $ids));
}
$res = $this->api()
->asForm()
->post($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches/submit', [
'ids' => $ids,
'pc_submitted_by' => $auth_user['name'],
'slot' => $request->input('slot'),
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal hantar batch.';
return redirect()->back()->with('error', $msg);
}
$batchNo = $res->json()['batch']['batch_no'] ?? '';
return redirect()->route('pembiayaan.online.pc')
->with('message', 'Batch dihantar ke Operasi' . ($batchNo ? ': ' . $batchNo : '.'));
}
/**
* Batch detail.
*/
public function showBatch($batchId)
{
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$detail = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches/' . $batchId)
->json();
if (!is_array($detail) || empty($detail['batch'])) {
return redirect()->route('pembiayaan.online.pc')->with('error', 'Batch tidak dijumpai.');
}
return view('pembiayaan_online.pc.batch', [
'batch' => $detail['batch'],
'lines' => $detail['lines'] ?? [],
'kodcaw' => $kodcaw,
'api_url' => $api_url,
'cawangan_info' => $cawangan_info,
]);
}
/**
* PC: senarai PAID untuk tally + blast.
*/
public function paidIndex(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$blastFilter = $request->input('blast_sent', '0');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$query = [];
if ($blastFilter !== 'all' && $blastFilter !== '') {
$query['blast_sent'] = $blastFilter;
}
$paid = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/paid', $query)
->json();
$lines = $paid['data'] ?? [];
if (!is_array($lines)) {
$lines = [];
}
$bilangan = $paid['bilangan'] ?? count($lines);
$jumlah = $paid['jumlah'] ?? 0;
return view('pembiayaan_online.pc.paid', compact(
'auth_user',
'kodcaw',
'api_url',
'cawangan_info',
'lines',
'bilangan',
'jumlah',
'blastFilter'
));
}
/**
* PC: view/download bukti bayaran inline.
*/
public function viewBukti($id)
{
$kodcaw = $this->kodcaw();
$res = $this->api()
->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/bukti');
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Bukti tidak dijumpai.';
return redirect()->back()->with('error', $msg);
}
$payload = $res->json();
$bukti = $payload['bukti'] ?? null;
if (!$bukti || empty($bukti['data'])) {
return redirect()->back()->with('error', 'Fail bukti kosong.');
}
$raw = $bukti['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']);
}
$type = $bukti['type'] ?? 'application/octet-stream';
$filename = $bukti['filename'] ?? ('bukti-' . $id);
return response($binary, 200, [
'Content-Type' => $type,
'Content-Disposition' => 'inline; filename="' . $filename . '"',
]);
}
/**
* PC: tanda rekod PAID sebagai selesai semak / blast.
*/
public function markBlast(Request $request)
{
$kodcaw = $this->kodcaw();
$ids = $request->input('ids', []);
if (!is_array($ids)) {
$ids = array_filter(explode(',', (string) $ids));
}
if (count($ids) === 0) {
return redirect()->back()->with('error', 'Sila pilih sekurang-kurangnya satu rekod.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/paid/blast', [
'ids' => $ids,
'blast_by' => Auth::user()['name'],
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tanda selesai.';
return redirect()->back()->with('error', $msg);
}
$n = $res->json()['dikemaskini'] ?? count($ids);
return redirect()
->route('pembiayaan.online.pc.paid')
->with('message', $n . ' rekod ditanda selesai semak/blast.');
}
/**
* Operasi: senarai batch semua cawangan.
*/
public function operasiIndex(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$status = $request->input('status', 'SUBMITTED');
$tarikh = $request->input('tarikh');
$query = ['status' => $status];
if ($tarikh) {
$query['tarikh'] = $tarikh;
}
$res = $this->api()
->get($api_url . '/api/pembiayaan-online/operasi/batches', $query)
->json();
$batches = $res['data'] ?? [];
if (!is_array($batches)) {
$batches = [];
}
return view('pembiayaan_online.operasi.senarai', compact(
'auth_user',
'api_url',
'batches',
'status',
'tarikh'
));
}
/**
* Operasi: detail batch + lines.
*/
public function operasiShowBatch($kodcaw, $batchId)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$detail = $this->api()
->get($api_url . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId)
->json();
if (!is_array($detail) || empty($detail['batch'])) {
return redirect()
->route('pembiayaan.online.operasi')
->with('error', 'Batch tidak dijumpai.');
}
return view('pembiayaan_online.operasi.batch', [
'auth_user' => $auth_user,
'batch' => $detail['batch'],
'lines' => $detail['lines'] ?? [],
'kodcaw' => $kodcaw,
'api_url' => $api_url,
'cawangan_info' => $cawangan_info,
]);
}
public function operasiApproveLine(Request $request, $kodcaw, $id)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/approve', [
'ops_by' => Auth::user()['name'],
]);
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod diluluskan.');
}
public function operasiRejectLine(Request $request, $kodcaw, $id)
{
$reason = trim((string) $request->input('reject_reason', ''));
if ($reason === '') {
return redirect()->back()->with('error', 'Sebab tolak diperlukan.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/reject', [
'ops_by' => Auth::user()['name'],
'reject_reason' => $reason,
]);
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod ditolak. Dikembalikan ke PC.');
}
public function operasiApproveBatch(Request $request, $kodcaw, $batchId)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId . '/approve', [
'ops_by' => Auth::user()['name'],
]);
return $this->opsRedirect($res, $kodcaw, $batchId, 'Semua rekod menunggu dalam batch diluluskan.');
}
public function operasiMarkPaid(Request $request, $kodcaw, $id)
{
$payload = $this->buildPaidPayload($request);
if (isset($payload['error'])) {
return redirect()->back()->with('error', $payload['error']);
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/paid', $payload);
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod ditanda PAID.');
}
public function operasiMarkPaidBatch(Request $request, $kodcaw, $batchId)
{
$payload = $this->buildPaidPayload($request);
if (isset($payload['error'])) {
return redirect()->back()->with('error', $payload['error']);
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId . '/paid', $payload);
return $this->opsRedirect($res, $kodcaw, $batchId, 'Rekod APPROVED dalam batch ditanda PAID.');
}
/**
* @return array
*/
protected function buildPaidPayload(Request $request)
{
$noRujukan = trim((string) $request->input('no_rujukan_bayaran', ''));
if ($noRujukan === '') {
return ['error' => 'No. rujukan bayaran diperlukan.'];
}
if (!$request->hasFile('bukti')) {
return ['error' => 'Fail bukti bayaran diperlukan.'];
}
$file = $request->file('bukti');
if (!$file->isValid()) {
return ['error' => 'Fail bukti tidak sah.'];
}
$raw = base64_encode(file_get_contents($file->getRealPath()));
$mime = $file->getMimeType() ?: 'application/octet-stream';
return [
'ops_by' => Auth::user()['name'],
'no_rujukan_bayaran' => $noRujukan,
'tarikh_bayaran' => $request->input('tarikh_bayaran') ?: date('Y-m-d'),
'bukti_data' => $raw,
'bukti_filename' => $file->getClientOriginalName(),
'bukti_type' => $mime,
];
}
protected function opsRedirect($res, $kodcaw, $batchId, $successMsg)
{
$batchId = $batchId ?: null;
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Tindakan gagal.';
return redirect()->back()->with('error', $msg);
}
if ($batchId) {
return redirect()
->route('pembiayaan.online.operasi.batch', ['kodcaw' => $kodcaw, 'batchId' => $batchId])
->with('message', $successMsg);
}
return redirect()->route('pembiayaan.online.operasi')->with('message', $successMsg);
}
/**
* PC: modal online float + top-up request form/history.
*/
public function topupIndex(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$status = $request->input('status', '');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$modal = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw)
->json();
$query = [];
if ($status !== '' && $status !== 'all') {
$query['status'] = $status;
}
$reqRes = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw . '/topup/requests', $query)
->json();
$requests = $reqRes['data'] ?? [];
if (!is_array($requests)) {
$requests = [];
}
$pendingRes = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw . '/topup/requests', ['status' => 'PENDING'])
->json();
$pendingList = $pendingRes['data'] ?? [];
$hasPending = is_array($pendingList) && count($pendingList) > 0;
$isMissing = !empty($modal['missing']);
$pendingRequest = $modal['pending_request'] ?? null;
if (!$pendingRequest && $hasPending) {
$pendingRequest = $pendingList[0];
}
return view('pembiayaan_online.pc.topup', compact(
'auth_user',
'kodcaw',
'api_url',
'cawangan_info',
'modal',
'requests',
'status',
'hasPending',
'isMissing',
'pendingRequest'
));
}
/**
* PC: submit top-up request.
*/
public function topupRequest(Request $request)
{
$kodcaw = $this->kodcaw();
$amaun = (float) $request->input('amaun', 0);
if ($amaun <= 0) {
return redirect()->back()->with('error', 'Amaun mesti lebih daripada 0.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/topup/request', [
'amaun' => $amaun,
'dimohon_oleh' => Auth::user()['name'],
'nota' => $request->input('nota'),
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal hantar permohonan.';
return redirect()->back()->with('error', $msg)->withInput();
}
return redirect()
->route('pembiayaan.online.pc.topup')
->with('message', 'Permohonan modal online dihantar. Menunggu kelulusan Operasi.');
}
/**
* Operasi: senarai permohonan tambah modal online.
*/
public function operasiTopupIndex(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$status = $request->input('status', 'PENDING');
$tarikh = $request->input('tarikh');
$query = [];
if ($status === 'all') {
$query['status'] = 'all';
} elseif ($status !== '' && $status !== null) {
$query['status'] = $status;
} else {
$query['status'] = 'PENDING';
}
if ($tarikh) {
$query['tarikh'] = $tarikh;
}
$res = $this->api()
->get($api_url . '/api/modal-online/operasi/topup/requests', $query)
->json();
$requests = $res['data'] ?? [];
if (!is_array($requests)) {
$requests = [];
}
return view('pembiayaan_online.operasi.topup', compact(
'auth_user',
'api_url',
'requests',
'status',
'tarikh'
));
}
public function operasiTopupApprove(Request $request, $id)
{
$amaun = $request->input('amaun');
if ($amaun !== null && $amaun !== '' && (float) $amaun <= 0) {
return redirect()->back()->with('error', 'Amaun diluluskan mesti lebih daripada 0.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/operasi/topup/' . $id . '/approve', [
'diluluskan_oleh' => Auth::user()['name'],
'nota' => $request->input('nota'),
'amaun' => $amaun,
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal lulus permohonan.';
return redirect()->back()->with('error', $msg);
}
$amaunLulus = $res->json()['request']['amaun'] ?? $amaun;
return redirect()
->route('pembiayaan.online.operasi.topup')
->with('message', 'Permohonan diluluskan RM ' . number_format((float) $amaunLulus, 2) . '. Modal online cawangan dikemas kini.');
}
public function operasiTopupReject(Request $request, $id)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/operasi/topup/' . $id . '/reject', [
'diluluskan_oleh' => Auth::user()['name'],
'nota' => $request->input('nota'),
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tolak permohonan.';
return redirect()->back()->with('error', $msg);
}
return redirect()
->route('pembiayaan.online.operasi.topup')
->with('message', 'Permohonan ditolak.');
}
/**
* PC: EOD close branch modal online.
*/
public function closeModal(Request $request)
{
$kodcaw = $this->kodcaw();
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/close', [
'ditutup_oleh' => Auth::user()['name'],
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup modal online.';
return redirect()->back()->with('error', $msg);
}
$dikembalikan = $res->json()['dikembalikan'] ?? 0;
return redirect()
->route('pembiayaan.online.pc.topup')
->with('message', 'Modal online ditutup. Baki dikembalikan RM ' . number_format((float) $dikembalikan, 2) . '.');
}
/**
* Operasi: EOD dashboard semua cawangan.
*/
public function operasiEodIndex(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$tarikh = $request->input('tarikh') ?: date('Y-m-d');
$res = $this->api()
->get($api_url . '/api/modal-online/operasi/eod', [
'zone' => 'all',
'tarikh' => $tarikh,
])
->json();
$rows = $res['data'] ?? [];
if (!is_array($rows)) {
$rows = [];
}
$summary = [
'open' => $res['open'] ?? 0,
'closed' => $res['closed'] ?? 0,
'missing' => $res['missing'] ?? 0,
'bilangan' => $res['bilangan'] ?? count($rows),
];
return view('pembiayaan_online.operasi.eod', compact(
'auth_user',
'api_url',
'rows',
'tarikh',
'summary'
));
}
public function operasiCloseModal(Request $request, $kodcaw)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/close', [
'ditutup_oleh' => Auth::user()['name'],
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup modal online.';
return redirect()->back()->with('error', $msg);
}
return redirect()
->route('pembiayaan.online.operasi.eod')
->with('message', 'Modal online ' . $kodcaw . ' ditutup.');
}
public function operasiCloseAllModal(Request $request)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/operasi/eod/close-all', [
'ditutup_oleh' => Auth::user()['name'],
'zone' => 'all',
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup semua modal online.';
return redirect()->back()->with('error', $msg);
}
$closed = $res->json()['closed'] ?? 0;
$skipped = $res->json()['skipped'] ?? 0;
return redirect()
->route('pembiayaan.online.operasi.eod')
->with('message', 'EOD selesai: ' . $closed . ' ditutup, ' . $skipped . ' sudah ditutup.');
}
}