'hafiz@koppkb.com', 275 => 'shauqi@koppkb.com', ]; const AUTO_APPROVE_KOMEN = 'lulus'; public function __construct() { $this->middleware('auth'); } public function kelulusan(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $kelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListOperasi/')->json(); $arraykelulusan = []; foreach ($kelulusan as $index => $lulus) { foreach ($lulus['kelulusanall'] as $index => $kel) { array_push($arraykelulusan, ["cawangan" => $lulus['cawangan'], "cawangankod" => $lulus['cawangankod'], "namateller" => $kel['teller'], 'siri' => $kel['siri'], "norujukan" => $kel['norujukan'], 'pinjaman' => $kel['jumpinj'], 'komen' => $kel['komen'], 'nilaimarhun' => ($kel['nilaimarhun'] ?? '-'), 'berat' => ($kel['berat'] ?? '-'), 'margin' => ($kel['margin'] ?? '-'), 'status' => ($kel['status'] ?? '-'), 'pelulus' => $kel['pelulus']]); } foreach ($lulus['kelulusanMohonid'] as $index => $mohon) { array_push($arraykelulusan, ["mohonid" => true, "ansuran" => $mohon['ansuran'], "cawangan" => $lulus['cawangan'], "cawangankod" => $lulus['cawangankod'], "namateller" => $mohon['teller'], 'recno' => $mohon['recno'], "norujukan" => $mohon['norujukan'], 'pinjaman' => $mohon['pinjaman'], 'komen' => $mohon['katalaluan'], 'nilaimarhun' => ($mohon['nilaimarhun'] ?? '-'), 'berat' => ($mohon['berat'] ?? '-'), 'margin' => ($mohon['margin'] ?? '-'), 'status' => ($mohon['mohon'] ?? '-')]); } } return view('operasi.kelulusan', compact('cawangan_info', 'api_url', 'arraykelulusan')); } public function getkelulusan(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); $norujukan = $request->norujukan; $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $kelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/SenaraiDahLulus/' . $auth_user['cawangan'] . '/' . $norujukan)->json(); if (sizeof($kelulusan) > 0) { $kelulusan = $kelulusan[0]['masalulus']; } else { $kelulusan = ''; } return response($kelulusan); } public function updatekelulusan(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); $norujukan = $request->norujukan; if ($request->pembatalan == null) { if (!$request->taglulus) { $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusanKomen/' . $request->kodcaw . '/' . $norujukan, [ "komen" => $request->komenkelulusan, 'siri' => $request->siri ])->json(); $updateData = [ 'sahkan' => 1, 'namaoperasi' => $request->namaoperasi, 'siri' => $request->siri ]; if ($request->has('actions')) { $updateData['actions'] = $request->actions; } $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusan/' . $request->kodcaw . '/' . $norujukan, $updateData)->json(); }else{ $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateHapusAnsuran',[ 'siri' => $request->siri, "kodcaw" => $request->kodcaw, "norujukan" => $request->norujukan, 'namaoperasi' => $request->namaoperasi, 'sahkan' => 1, ])->json(); } if ($updatelulus) { event(new NotifikasiKelulusan($norujukan, "lulus", $request->siri, $request->komenkelulusan, $request->teller)); } } else { if (!$request->taglulus) { $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusanKomen/' . $request->kodcaw . '/' . $norujukan, [ "komen" => $request->komenkelulusan, 'siri' => $request->siri ])->json(); $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusan/' . $request->kodcaw . '/' . $norujukan, [ 'sahkan' => 2, 'namaoperasi' => $request->namaoperasi, 'siri' => $request->siri ])->json(); } else { $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateHapusAnsuran', [ 'siri' => $request->siri, "kodcaw" => $request->kodcaw, "norujukan" => $request->norujukan, 'namaoperasi' => $request->namaoperasi, 'sahkan' => 2, ])->json(); } if ($updatelulus) { event(new NotifikasiKelulusan($norujukan, "batal", $request->siri, $request->komenkelulusan, $request->teller)); } } return response()->json($updatelulus, 200); } public function createKelulusanByKhazanah(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); $current = Carbon::now(); $current = new Carbon(); $norujukan = $request->norujukan; $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json(); $getsiri = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListLatestLulus/'. $auth_user['cawangan'] . '/' . $norujukan)->json(); if(isset($getsiri['siri'])) { $siri = $getsiri['siri'] + 1; }else{ $siri = 1; } $createlulus = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/CreateKelulusan/' . $auth_user['cawangan'] . '/' . $norujukan . '/roles/khazanah', [ 'komen' => $request->komen, 'dategadai' => $current->toDateString(), 'bilgadai' => 1, 'siri' => $siri, 'margin' => $request->margin, 'berat' => $request->berat, 'nilaimarhun' => $request->nilaimarhun, 'status' => $request->status, 'jumpinjam' => $request->jumpinjam, 'jenistebus' => "G", 'teller' => $request->teller, 'peloperasi' => "operasi", 'pelulus' => "operasi", 'actions' => "Mohon Pengesahan Pembayaran Pampasan" ])->json(); event(new OperasiNotice($auth_user['cawangan'],$auth_user['name'])); return response()->json($siri,201); } public function updatekelulusananggota(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); // Data asas $nokp = $request->nokp; $kodcaw = $request->kodcaw; $komen = $request->komenkelulusan; $namaoperasi = $request->namaoperasi; $taglulus = $request->taglulus; // contoh: 'Y' = lulus, 'N' = batalmarginmutu() // 1️⃣ Tentukan status kelulusan $status = ($taglulus == 'Y') ? 1 : 2; // 1 = Lulus, 2 = Batal/Tolak try { // 2️⃣ Panggil API baru — ikut route baru dengan {kodcaw} $endpoint = $api_url . '/api/UpdateKelulusanAnggota/' . $kodcaw; $payload = [ 'nokp' => $nokp, 'komenkelulusan' => $komen, 'namaoperasi' => $namaoperasi, 'sahkan' => $status ]; $response = Http::withOptions(['verify' => config('app.api_verify')]) ->put($endpoint, $payload); $updatelulus = $response->json(); if ($response->successful() && $updatelulus && isset($updatelulus['success']) && $updatelulus['success'] === true) { $statusText = ($status == 1) ? 'DILULUSKAN' : 'DITOLAK'; event(new NotifikasiAnggota($nokp, $statusText, $komen, $namaoperasi)); } return response()->json($updatelulus, $response->status()); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Ralat semasa menghantar kelulusan anggota.', 'error' => $e->getMessage() ], 500); } } public function createkelulusan(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); $current = Carbon::now(); $current = new Carbon(); $norujukan = $request->norujukan; $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $getsiri = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListLatestLulus/' . $auth_user['cawangan'] . '/' . $norujukan)->json(); if (isset($getsiri['siri'])) { $siri = $getsiri['siri'] + 1; } else { $siri = 1; } $createlulus = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/CreateKelulusan/' . $auth_user['cawangan'] . '/' . $norujukan . '/roles/operasi', [ 'komen' => $request->komen, 'dategadai' => $current->toDateString(), 'bilgadai' => 1, 'siri' => $siri, 'margin' => $request->margin, 'berat' => $request->berat, 'nilaimarhun' => $request->nilaimarhun, 'status' => $request->status, 'jumpinjam' => $request->jumpinjam, 'jenistebus' => "G", 'teller' => $request->teller, 'peloperasi' => "operasi" ])->json(); event(new StatusLiked($auth_user['cawangan'], $auth_user['name'])); $this->notifyGadaiAboveTenThousand($auth_user, $request, $cawangan_info); return response()->json($siri, 201); } /** * Hidden auto-approve for operasi gadai kelulusan in RM10,000–45,000 * when Operasi has not approved within 30s after Khazanah escalation. */ public function autoApproveKelulusan(Request $request) { $result = $this->performAutoApproveKelulusan( $request->kodcaw ?: Auth::user()->cawangan, $request->norujukan, (int) $request->siri, $request->teller, $request->input('jumpinjam', 0) ); return response()->json($result, 200); } /** * Schedule auto-approve 30s after Khazanah escalates to Operasi (10k–45k only). * Runs after the HTTP response so Khazanah UI is not blocked. */ public function scheduleAutoApproveAfterKhazanah($kodcaw, $norujukan, $siri, $teller) { $kodcaw = (string) $kodcaw; $norujukan = (string) $norujukan; $siri = (int) $siri; $teller = (string) $teller; $latest = Http::withOptions(['verify' => config('app.api_verify')]) ->get(config('api.url') . '/api/ListLatestLulus/' . $kodcaw . '/' . $norujukan) ->json(); if (!is_array($latest) || !isset($latest['siri']) || (int) $latest['siri'] !== $siri) { return; } $apiRaw = str_replace(',', '', (string) ($latest['jumpinj'] ?? 0)); $apiAmount = is_numeric($apiRaw) ? (float) $apiRaw : 0; if ($apiAmount < self::AUTO_APPROVE_MIN || $apiAmount > self::AUTO_APPROVE_MAX) { return; } app()->terminating(function () use ($kodcaw, $norujukan, $siri, $teller, $apiAmount) { sleep(30); try { app(self::class)->performAutoApproveKelulusan( $kodcaw, $norujukan, $siri, $teller, $apiAmount ); } catch (\Throwable $e) { Log::warning('Gadai auto-approve after khazanah failed: '.$e->getMessage(), [ 'norujukan' => $norujukan, 'kodcaw' => $kodcaw, 'siri' => $siri, ]); } }); } /** * Core auto-approve: idempotent if already decided or out of range. */ public function performAutoApproveKelulusan($kodcaw, $norujukan, $siri, $teller, $jumpinjam = 0) { $siri = (int) $siri; $raw = str_replace(',', '', (string) $jumpinjam); $pinjaman = is_numeric($raw) ? (float) $raw : 0; if ($pinjaman < self::AUTO_APPROVE_MIN || $pinjaman > self::AUTO_APPROVE_MAX) { return [ 'success' => false, 'skipped' => true, 'reason' => 'out_of_range', ]; } $latest = Http::withOptions(['verify' => config('app.api_verify')]) ->get(config('api.url') . '/api/ListLatestLulus/' . $kodcaw . '/' . $norujukan) ->json(); if (!is_array($latest) || !isset($latest['siri']) || (int) $latest['siri'] !== $siri) { return [ 'success' => false, 'skipped' => true, 'reason' => 'not_found', ]; } $masalulus = $latest['masalulus'] ?? ''; $pelulus = $latest['pelulus'] ?? ''; if (($masalulus !== '' && $masalulus !== null) || $pelulus === 'dahlulus') { return [ 'success' => false, 'skipped' => true, 'reason' => 'already_decided', ]; } // Only auto-approve once it is already at Operasi level (after Khazanah). if ($pelulus !== 'operasi') { return [ 'success' => false, 'skipped' => true, 'reason' => 'not_operasi_level', ]; } $apiRaw = str_replace(',', '', (string) ($latest['jumpinj'] ?? $pinjaman)); $apiAmount = is_numeric($apiRaw) ? (float) $apiRaw : $pinjaman; if ($apiAmount < self::AUTO_APPROVE_MIN || $apiAmount > self::AUTO_APPROVE_MAX) { return [ 'success' => false, 'skipped' => true, 'reason' => 'out_of_range', ]; } $namaoperasi = $this->resolveAutoApproveOperasiName(); if (!$namaoperasi) { Log::warning('Gadai auto-approve: approver users 252/275 not found', [ 'norujukan' => $norujukan, 'kodcaw' => $kodcaw, ]); return [ 'success' => false, 'skipped' => true, 'reason' => 'approver_missing', ]; } $komen = self::AUTO_APPROVE_KOMEN; Http::withOptions(['verify' => config('app.api_verify')])->put( config('api.url') . '/api/UpdateKelulusanKomen/' . $kodcaw . '/' . $norujukan, [ 'komen' => $komen, 'siri' => $siri, ] )->json(); $updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put( config('api.url') . '/api/UpdateKelulusan/' . $kodcaw . '/' . $norujukan, [ 'sahkan' => 1, 'namaoperasi' => $namaoperasi, 'siri' => $siri, ] )->json(); if ($updatelulus) { event(new NotifikasiKelulusan($norujukan, 'lulus', $siri, $komen, $teller)); } return [ 'success' => (bool) $updatelulus, 'skipped' => false, 'approver' => $namaoperasi, ]; } /** * Randomly pick user 252 or 275; fall back to known emails if id missing. */ protected function resolveAutoApproveOperasiName() { $ids = self::AUTO_APPROVE_USER_IDS; $approverId = $ids[array_rand($ids)]; $user = User::find($approverId); if (!$user) { $email = self::AUTO_APPROVE_EMAILS[$approverId] ?? null; $user = $email ? User::where('email', $email)->first() : null; } if (!$user) { foreach ($ids as $id) { if ($id === $approverId) { continue; } $user = User::find($id); if (!$user) { $email = self::AUTO_APPROVE_EMAILS[$id] ?? null; $user = $email ? User::where('email', $email)->first() : null; } if ($user) { break; } } } return $user ? $user->name : null; } /** * 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) { try { $auth_user = Auth::user(); $api_url = config('api.url'); $current = Carbon::now(); // Dapatkan maklumat cawangan $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')]) ->get($api_url . '/api/cawangan/' . $auth_user['cawangan']) ->json(); // Generate siri baru $getsiri = Http::withOptions(['verify' => config('app.api_verify')]) ->get($api_url . '/api/ListLatestLulus/' . $auth_user['cawangan'] . '/anggota') ->json(); $siri = isset($getsiri['siri']) ? $getsiri['siri'] + 1 : 1; // Hantar ke API $createlulus = Http::withOptions(['verify' => config('app.api_verify')]) ->post($api_url . '/api/CreateKelulusanAnggota/' . $auth_user['cawangan'] . '/roles/operasi', [ 'komen' => $request->komen, 'date_mohon' => $current->toDateString(), 'siri' => $siri, 'nokp' => $request->nokp, 'teller' => $request->teller, 'peloperasi' => 'operasi' ]) ->json(); event(new StatusLiked($auth_user['cawangan'], $auth_user['name'])); return response()->json([ 'success' => true, 'message' => 'Permohonan kelulusan anggota berjaya dihantar.', 'siri' => $siri, 'data' => $createlulus, ], 201); Log::info('🛰️ API CreateKelulusanAnggota Response:', [ 'status' => $response->status(), 'body' => $response->body(), ]); } catch (\Throwable $th) { return response()->json([ 'success' => false, 'message' => $th->getMessage(), ], 500); } } public function mintakatalaluansemula(Request $request) { if (app()->environment('local')) { return "True"; } if (Hash::check($request->passsemula, Auth::user()->password)) { return "True"; } else { return "False"; } } public function parameter() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); return view('operasi.parameter', compact('cawangan_info', 'api_url')); } public function hargaemas() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $harga_emas = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/harga_emas/')->json(); return view('operasi.hargaemas', compact('cawangan_info', 'api_url', 'harga_emas')); } public function margin() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $margin_pembiayaan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/maxpinjaman')->json(); return view('operasi.margin_pembiayaan', compact('cawangan_info', 'api_url', 'margin_pembiayaan')); } public function kelulusanrekodlepas() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $dateTo = Carbon::now()->toDateString(); $dateFrom = Carbon::now()->subDays(30)->toDateString(); return view('operasi.rekodtelahlulus', compact('cawangan_info', 'api_url', 'dateFrom', 'dateTo')); } public function kelulusanrekodlepasData(Request $request) { $params = [ 'date_from' => $request->query('date_from'), 'date_to' => $request->query('date_to'), 'search' => $request->query('search'), ]; $response = Http::withOptions(['verify' => config('app.api_verify')]) ->get(config('api.url') . '/api/ListOperasiKelulusan/', array_filter($params, function ($value) { return $value !== null && $value !== ''; })); if (!$response->successful()) { return response()->json(['data' => [], 'error' => 'Gagal memuatkan rekod'], 502); } $rows = $response->json(); if (!is_array($rows)) { $rows = []; } return response()->json(['data' => $rows]); } public function notifyKomoditi() { $auth_user = Auth::user(); event(new KomoditiNotify('lulus', $auth_user['cawangan'])); } public function kelulusananggota(Request $request) { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json(); $arraykelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/list/approval/anggota')->json(); return view('operasi.kelulusan_anggota', compact('cawangan_info', 'api_url', 'arraykelulusan')); } public function marginmutu() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json(); // Get MaxMutuEmas data from API $maxmutuemas = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/maxmutuemas')->json(); return view('operasi.marginmutu', compact('cawangan_info', 'api_url', 'maxmutuemas')); } public function editMarginMutu() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json(); return view('operasi.edit_marginmutu', compact('cawangan_info', 'api_url')); } public function laporanAudit() { $auth_user = Auth::user(); $api_url = config('api.url'); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json(); return view('homepage.operasi.laporan-audit', compact('cawangan_info', 'api_url')); } public function cetakLaporanAuditTrail(Request $request) { $api_url = config('api.url'); $auth_user = Auth::user(); $current = Carbon::now(); $current = new Carbon(); $tarikh_cetak = $current->toDateString(); if($request->dateaudit == null){ $date = $current->toDateString(); }else{ $split_date = explode('-', $request->dateaudit); $date = $split_date[0] . '-' . $split_date[1] . '-' . $split_date[2]; } if($request->dateaudit_till == null){ $date_till = $current->toDateString(); }else{ $split_date_till = explode('-', $request->dateaudit_till); $date_till = $split_date_till[0] . '-' . $split_date_till[1] . '-' . $split_date_till[2]; } $audittrail_raw = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url'). '/api/reports/audittrail/operasi',['date' => $date,'date_till' => $date_till])->json(); if(!$audittrail_raw){ return redirect()->back()->with('errormessage', 'Tiada data pada tarikh tersebut.'); } // Transform the data to match view format $audittrail = []; foreach($audittrail_raw as $item) { $created_at = \Carbon\Carbon::parse($item['created_at']); // Format differences for display $differences_text = '-'; if (isset($item['differences']) && !empty($item['differences'])) { $diff_parts = []; foreach ($item['differences'] as $field => $changes) { $old = $changes['old'] ?? 'null'; $new = $changes['new'] ?? 'null'; $diff_parts[] = "{$field}: {$old} → {$new}"; } $differences_text = implode('; ', $diff_parts); } $audittrail[] = [ 'tarikh' => $created_at->format('d/m/Y'), 'masa' => $created_at->format('H:i:s'), 'users' => $item['users'], 'actions' => $item['actions'], 'differ' => $differences_text ]; } $cawangan_detail = [ 'kodcaw' => 'operasi', 'namacaw' => 'Operasi Ar-Rahn', 'alamat1' => 'Lot 107, Jalan Parit Dalam,', 'alamat2' => 'Seksyen 8', 'poskod' => '15000', 'bandar' => 'Kota Bahru', 'negeri' => 'Kelantan', 'notel' => '03-12345678', 'nofaks' => '03-12345678', ]; return view('print.laporan_audittrail_operasi', compact('audittrail','cawangan_detail','tarikh_cetak','date','date_till','api_url')); } }