diff --git a/app/Http/Controllers/KhazanahController.php b/app/Http/Controllers/KhazanahController.php index 6332ed02..dd6c3a19 100644 --- a/app/Http/Controllers/KhazanahController.php +++ b/app/Http/Controllers/KhazanahController.php @@ -29,6 +29,7 @@ use Carbon\Carbon; use PDF; use DateTime; use App\Helper; +use App\Services\NotificationService; use Log; class KhazanahController extends Controller @@ -1084,6 +1085,7 @@ class KhazanahController extends Controller if($test == 'success'){ event(new PendahuluanSerahan("Lulus","pendahuluan",$auth_user['cawangan'],$request->teller)); + $this->notifyTellerAliranTunai('Lulus', 'pendahuluan', $auth_user['cawangan'], $request->teller, $amount); return redirect()->route('khazanah.pendahulu'); }else{ return redirect()->route('khazanah.pendahulu')->with('error','Permohonan tidak berjaya! Permohonan Semula perlu melebihi 1 minit.'); @@ -1109,6 +1111,7 @@ class KhazanahController extends Controller if($test == 'success'){ event(new PendahuluanSerahan("Lulus","serahan",$auth_user['cawangan'],$request->teller)); + $this->notifyTellerAliranTunai('Lulus', 'serahan', $auth_user['cawangan'], $request->teller, $request->amt); return redirect()->route('khazanah.serahan'); }else{ return redirect()->route('khazanah.serahan')->with('error','Permohonan tidak berjaya! Permohonan Semula perlu melebihi 1 minit.'); @@ -2848,10 +2851,43 @@ class KhazanahController extends Controller ]); event(new PendahuluanSerahan("Batal","all",$auth_user['cawangan'],$kodlaluan)); + $this->notifyTellerAliranTunai('Batal', 'all', $kodcaw, $kodlaluan); return response('Berjaya'); } + /** + * Inbox for the teller waiting on aliran tunai. Failures must not block approval. + * + * @param string $pengesahan + * @param string $jenis + * @param string $kodcaw + * @param string $teller + * @param mixed $amaun + * @return void + */ + protected function notifyTellerAliranTunai($pengesahan, $jenis, $kodcaw, $teller, $amaun = 0) + { + try { + $raw = str_replace(',', '', (string) $amaun); + $amount = is_numeric($raw) ? (float) $raw : 0; + + app(NotificationService::class)->notifyAliranTunaiDecided($kodcaw, $teller, [ + 'pengesahan' => $pengesahan, + 'jenis' => $jenis, + 'amaun' => $amount, + 'kodlaluan' => $teller, + 'khazanah' => Auth::user() ? Auth::user()->name : '', + ]); + } catch (\Throwable $e) { + Log::warning('Aliran tunai teller notification failed: '.$e->getMessage(), [ + 'kodcaw' => $kodcaw, + 'teller' => $teller, + 'jenis' => $jenis, + ]); + } + } + public function SuratAkuanGadaian(Request $request) { $auth_user = Auth::user(); diff --git a/app/Http/Controllers/OperasiController.php b/app/Http/Controllers/OperasiController.php index 7b5f6491..c394cf34 100644 --- a/app/Http/Controllers/OperasiController.php +++ b/app/Http/Controllers/OperasiController.php @@ -9,6 +9,7 @@ use App\Events\StatusLiked; use App\Events\NotifikasiKelulusan; use App\Events\NotifikasiAnggota; use App\Events\Dashboard\KomoditiNotify; +use App\Services\NotificationService; use Illuminate\Support\Facades\Auth; use Illuminate\Http\Request; @@ -309,9 +310,48 @@ class OperasiController extends Controller event(new StatusLiked($auth_user['cawangan'], $auth_user['name'])); + $this->notifyGadaiAboveTenThousand($auth_user, $request, $cawangan_info); + return response()->json($siri, 201); } + /** + * Inbox for PC/PPC and operasi when teller mohon kelulusan gadai >= RM10,000. + */ + protected function notifyGadaiAboveTenThousand($auth_user, Request $request, $cawangan_info) + { + $raw = str_replace(',', '', (string) $request->input('jumpinjam', 0)); + $pinjaman = is_numeric($raw) ? (float) $raw : 0; + if ($pinjaman < NotificationService::GADAI_KELULUSAN_MIN) { + return; + } + + $status = strtolower((string) $request->input('status', '')); + if (strpos($status, 'tebus') !== false) { + return; + } + + $namaCaw = ''; + if (is_array($cawangan_info)) { + $namaCaw = isset($cawangan_info['details_caw']) ? $cawangan_info['details_caw'] : ''; + } + + try { + app(NotificationService::class)->notifyGadaiKelulusan($auth_user['cawangan'], [ + 'norujukan' => $request->norujukan, + 'pinjaman' => $pinjaman, + 'teller' => $request->teller ?: $auth_user['name'], + 'cawangan_nama' => $namaCaw, + 'status' => $request->status, + ]); + } catch (\Throwable $e) { + Log::warning('Gadai kelulusan notification failed: '.$e->getMessage(), [ + 'norujukan' => $request->norujukan, + 'cawangan' => $auth_user['cawangan'], + ]); + } + } + public function createkelulusanAnggota(Request $request) { @@ -320,8 +360,6 @@ class OperasiController extends Controller $api_url = config('api.url'); $current = Carbon::now(); - Log::info('createkelulusanAnggota', $request->all()); - // Dapatkan maklumat cawangan $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')]) ->get($api_url . '/api/cawangan/' . $auth_user['cawangan']) @@ -360,7 +398,6 @@ class OperasiController extends Controller 'body' => $response->body(), ]); } catch (\Throwable $th) { - Log::error('❌ createkelulusanAnggota error: ' . $th->getMessage()); return response()->json([ 'success' => false, 'message' => $th->getMessage(), diff --git a/app/Services/NotificationService.php b/app/Services/NotificationService.php index 1ca32a52..bb2953f6 100644 --- a/app/Services/NotificationService.php +++ b/app/Services/NotificationService.php @@ -4,6 +4,7 @@ namespace App\Services; use App\StaffNotification; use App\User; +use Illuminate\Support\Facades\Log; class NotificationService { @@ -12,6 +13,10 @@ class NotificationService const TYPE_PEMBIAYAAN_BATCH_OPERASI = 'pembiayaan_online.batch_operasi'; const TYPE_PEMBIAYAAN_REJECTED_PC = 'pembiayaan_online.rejected_pc'; const TYPE_PEMBIAYAAN_PAID_PC = 'pembiayaan_online.paid_pc'; + const TYPE_GADAI_KELULUSAN = 'gadai.kelulusan'; + const TYPE_ALIRAN_TUNAI_PENDING = 'aliran_tunai.pending_khazanah'; + const TYPE_ALIRAN_TUNAI_DECIDED = 'aliran_tunai.decided_teller'; + const GADAI_KELULUSAN_MIN = 10000; /** * Create one inbox row per staff matching role + branch. @@ -70,6 +75,10 @@ class NotificationService $role = isset($audience['role']) ? $audience['role'] : null; $data = isset($input['data']) && is_array($input['data']) ? $input['data'] : []; + if ($type === self::TYPE_ALIRAN_TUNAI_PENDING && $kodcaw) { + return $this->notifyAliranTunaiPending($kodcaw, $data); + } + $payload = $this->payloadForType($type, $kodcaw, $data); if (!empty($input['title'])) { @@ -93,6 +102,90 @@ class NotificationService return collect(); } + /** + * Gadai >= RM10,000: inbox for PC/PPC at the branch and all operasi. + * + * @param string $kodcaw + * @param array $data + * @return \Illuminate\Support\Collection + */ + public function notifyGadaiKelulusan($kodcaw, array $data) + { + $pinjaman = isset($data['pinjaman']) ? (float) $data['pinjaman'] : 0; + if ($pinjaman < self::GADAI_KELULUSAN_MIN) { + return collect(); + } + + $payload = $this->payloadForType(self::TYPE_GADAI_KELULUSAN, $kodcaw, $data); + + $created = collect(); + foreach (['pc', 'ppc'] as $role) { + $created = $created->merge( + $this->notifyRoleAtCawangan($role, $kodcaw, array_merge($payload, [ + 'url' => '/kelulusan', + ])) + ); + } + + return $created->merge( + $this->notifyRole('operasi', array_merge($payload, [ + 'url' => '/operasi/kelulusan', + ])) + ); + } + + /** + * Teller pendahuluan/serahan: inbox for PC/PPC at the branch. + * + * @param string $kodcaw + * @param array $data + * @return \Illuminate\Support\Collection + */ + public function notifyAliranTunaiPending($kodcaw, array $data) + { + $payload = $this->payloadForType(self::TYPE_ALIRAN_TUNAI_PENDING, $kodcaw, $data); + + $created = collect(); + foreach (['pc', 'ppc'] as $role) { + $created = $created->merge($this->notifyRoleAtCawangan($role, $kodcaw, $payload)); + } + + return $created; + } + + /** + * Khazanah lulus/batal: inbox for the teller who requested cash. + * + * @param string $kodcaw + * @param string $tellerName + * @param array $data + * @return \Illuminate\Support\Collection + */ + public function notifyAliranTunaiDecided($kodcaw, $tellerName, array $data) + { + $tellerName = trim((string) $tellerName); + if ($tellerName === '') { + return collect(); + } + + $payload = $this->payloadForType(self::TYPE_ALIRAN_TUNAI_DECIDED, $kodcaw, $data); + + $user = User::query() + ->where('name', $tellerName) + ->where('cawangan', $kodcaw) + ->first(); + + if (!$user) { + $user = User::query()->where('name', $tellerName)->first(); + } + + if (!$user) { + return collect(); + } + + return collect([$this->notifyUser($user, $kodcaw, $user->roles, $payload)]); + } + /** * @param string $type * @param string|null $kodcaw @@ -223,6 +316,78 @@ class NotificationService ]; } + if ($type === self::TYPE_ALIRAN_TUNAI_PENDING) { + $jenis = $this->aliranTunaiJenis($data); + $amaun = $this->aliranTunaiAmaun($data, $jenis); + $teller = isset($data['kodlaluan']) ? trim((string) $data['kodlaluan']) : ''; + $label = $jenis === 'serahan' ? 'serahan' : 'pendahuluan'; + + return [ + 'type' => $type, + 'cawangan' => $kodcaw, + 'title' => $jenis === 'serahan' ? 'Permohonan serahan modal' : 'Permohonan pendahuluan', + 'body' => sprintf( + '%s mohon %s RM %s', + $teller !== '' ? $teller : 'Teller', + $label, + number_format($amaun, 2) + ), + 'url' => $jenis === 'serahan' ? '/serahan' : '/pendahuluan', + 'data' => $data, + ]; + } + + if ($type === self::TYPE_ALIRAN_TUNAI_DECIDED) { + $jenis = $this->aliranTunaiJenis($data); + $amaun = $this->aliranTunaiAmaun($data, $jenis); + $lulus = isset($data['pengesahan']) && strcasecmp((string) $data['pengesahan'], 'Lulus') === 0; + $label = 'Pendahuluan'; + if ($jenis === 'serahan') { + $label = 'Serahan'; + } elseif ($jenis === 'all') { + $label = 'Aliran tunai'; + } + + if ($lulus) { + $title = $label.' diluluskan'; + $body = sprintf('%s RM %s telah diluluskan', $label, number_format($amaun, 2)); + } else { + $title = $label.' tidak diluluskan'; + $body = sprintf('Permohonan %s tidak diluluskan', strtolower($label)); + } + + return [ + 'type' => $type, + 'cawangan' => $kodcaw, + 'title' => $title, + 'body' => $body, + 'url' => '/aliran_tunai', + 'data' => $data, + ]; + } + + if ($type === self::TYPE_GADAI_KELULUSAN) { + $norujukan = !empty($data['norujukan']) ? $data['norujukan'] : 'gadai'; + $amaun = number_format(isset($data['pinjaman']) ? (float) $data['pinjaman'] : 0, 2); + $teller = isset($data['teller']) ? trim((string) $data['teller']) : ''; + $namaCaw = !empty($data['cawangan_nama']) ? $data['cawangan_nama'] : $kodcaw; + + return [ + 'type' => $type, + 'cawangan' => $kodcaw, + 'title' => 'Kelulusan gadai melebihi RM 10,000', + 'body' => sprintf( + '%s — %s (RM %s)%s', + $namaCaw, + $norujukan, + $amaun, + $teller !== '' ? ' — '.$teller : '' + ), + 'url' => '/kelulusan', + 'data' => $data, + ]; + } + return [ 'type' => $type, 'cawangan' => $kodcaw, @@ -233,6 +398,46 @@ class NotificationService ]; } + /** + * @param array $data + * @return string pendahuluan|serahan|all + */ + protected function aliranTunaiJenis(array $data) + { + if (!empty($data['jenis'])) { + $jenis = strtolower(trim((string) $data['jenis'])); + if ($jenis === 'serahan' || $jenis === 'pendahuluan' || $jenis === 'all') { + return $jenis; + } + } + + $modal = isset($data['modal']) ? (float) $data['modal'] : 0; + $serahan = isset($data['serahan']) ? (float) $data['serahan'] : 0; + if ($serahan > 0 && $modal <= 0) { + return 'serahan'; + } + + return 'pendahuluan'; + } + + /** + * @param array $data + * @param string $jenis + * @return float + */ + protected function aliranTunaiAmaun(array $data, $jenis) + { + if (isset($data['amaun']) && is_numeric($data['amaun'])) { + return (float) $data['amaun']; + } + + if ($jenis === 'serahan') { + return isset($data['serahan']) ? (float) $data['serahan'] : 0; + } + + return isset($data['modal']) ? (float) $data['modal'] : 0; + } + /** * Create one inbox row for a specific user. * diff --git a/resources/views/dummy/borang_pengesahan_online.blade.php b/resources/views/dummy/borang_pengesahan_online.blade.php index 1f88054d..e8844cd4 100644 --- a/resources/views/dummy/borang_pengesahan_online.blade.php +++ b/resources/views/dummy/borang_pengesahan_online.blade.php @@ -204,7 +204,7 @@ />
Untuk diisi dan ditandatangani oleh pelanggan semasa transaksi gadai.
| Slot | +Hantar senarai sebelum | +Proses Operasi | +
|---|---|---|
| + {{ $row['label'] ?? $rowSlot }} + @if($isCurrent) + Semasa + @elseif($isDone) + Tamat + @endif + | +{{ $row['cutoff'] ?? '-' }} | +~ {{ $row['proses'] ?? '-' }} | +
Semak setiap rekod satu-satu (maklumat pelanggan, bank, amaun). Hanya rekod berstatus SAH boleh dipilih dan dihantar ke Operasi. + @if($slotLewat) + Cutoff {{ $slotName }} ({{ $slotCutoff }}) sudah tamat — hantar sekarang jika perlu; Operasi mungkin proses esok. + @else + Sila hantar senarai {{ $slotName }} sebelum {{ $slotCutoff }}. + @endif