DONE: add notification table on kaunter, notifies pc and operasi for pembiayaan online; WIP: notification for gadaian above 10000

This commit is contained in:
ISMAIL MASSERAN
2026-09-21 09:24:40 +08:00
parent 9629a075d7
commit 676600cf84
7 changed files with 282 additions and 4 deletions
+3
View File
@@ -64,5 +64,8 @@ DB_PASSWORD_7=root
TENNANT_MASTER_DB=master_erahn
TENNANT_KAUNTER_DB=kaunter_erahn
KAUNTER_URL=http://host.docker.internal:8081
INTERNAL_NOTIFICATION_SECRET=erahn-local-notification-secret
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
+4
View File
@@ -262,3 +262,7 @@ ONEWAYSMS_AUTH_PASS=APIUK330HDAFOUK330
ADMIN_PHONE_NUMBER=60193450165
ACTIVE_JOB_SERVER=false
# Kaunter inbox (PC modal request → operasi bell). Same secret as kaunter INTERNAL_NOTIFICATION_SECRET.
KAUNTER_URL=http://host.docker.internal:8081
INTERNAL_NOTIFICATION_SECRET=
@@ -6,6 +6,7 @@ use App\Cawangan;
use App\Feature;
use App\ModalOnline;
use App\RequestModalOnline;
use App\Services\StaffNotificationClient;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@@ -362,6 +363,8 @@ class ModalOnlineService
'nota' => $nota,
]);
$this->notifyOperasiModalRequest($kodcaw, $request, $jenis);
return [
'ok' => true,
'status' => 201,
@@ -373,6 +376,54 @@ class ModalOnlineService
];
}
/**
* Inbox for all operasi staff. Failures must not block the request.
*
* @param string $kodcaw
* @param \App\RequestModalOnline $request
* @param string $jenis
* @return void
*/
protected function notifyOperasiModalRequest($kodcaw, $request, $jenis)
{
try {
app(StaffNotificationClient::class)->notify([
'type' => 'modal_online.pending_operasi',
'cawangan' => $kodcaw,
'audience' => ['role' => 'operasi'],
'data' => [
'request_id' => $request->id,
'amaun' => (float) $request->amaun,
'dimohon_oleh' => $request->dimohon_oleh,
'jenis' => $jenis,
'nota' => $request->nota,
'cawangan_nama' => $this->cawanganNama($kodcaw),
],
]);
} catch (\Throwable $e) {
Log::warning('Operasi modal-online notification failed: '.$e->getMessage(), [
'kodcaw' => $kodcaw,
'request_id' => $request->id,
]);
}
}
/**
* @param string $kodcaw
* @return string
*/
protected function cawanganNama($kodcaw)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
if (!$cawangan) {
return $kodcaw;
}
$nama = trim((string) $cawangan->nama_cawangan);
return $nama !== '' ? $nama : $kodcaw;
}
/**
* List top-up requests.
* For PC: pass kodcaw. For Operasi: omit kodcaw (all branches).
@@ -11,6 +11,7 @@ use App\Feature;
use App\Gadai;
use App\Jobs\WhatsappPembiayaanOnlinePaid;
use App\PembiayaanOnline;
use App\Services\StaffNotificationClient;
use App\Services\Whatsapp\WhatsappCloudClient;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
@@ -749,7 +750,7 @@ class PembiayaanOnlineService
$tarikh = $now->toDateString();
try {
return DB::connection($mysql)->transaction(function () use (
$result = DB::connection($mysql)->transaction(function () use (
$mysql,
$kodcaw,
$ids,
@@ -857,6 +858,127 @@ class PembiayaanOnlineService
'payload' => ['message' => 'Gagal hantar batch pembiayaan online'],
];
}
if (!empty($result['ok'])) {
$this->notifyOperasiBatchSubmitted($kodcaw, $ids, $pcBy, $result['payload']);
}
return $result;
}
/**
* Inbox for all operasi staff after PC submit. Failures must not block the batch.
*
* @param string $kodcaw
* @param array $ids
* @param string $pcBy
* @param array $payload
* @return void
*/
protected function notifyOperasiBatchSubmitted($kodcaw, array $ids, $pcBy, array $payload)
{
$batch = isset($payload['batch']) ? $payload['batch'] : null;
if (!$batch) {
return;
}
$batchId = is_array($batch) ? ($batch['id'] ?? null) : $batch->id;
$batchNo = is_array($batch) ? ($batch['batch_no'] ?? '') : $batch->batch_no;
$slot = is_array($batch) ? ($batch['slot'] ?? '') : $batch->slot;
$bilangan = is_array($batch) ? ($batch['bilangan'] ?? 0) : $batch->bilangan;
$jumlah = is_array($batch) ? ($batch['jumlah_keseluruhan'] ?? 0) : $batch->jumlah_keseluruhan;
if (!$batchId) {
return;
}
try {
app(StaffNotificationClient::class)->notify([
'type' => 'pembiayaan_online.batch_operasi',
'cawangan' => $kodcaw,
'audience' => ['role' => 'operasi'],
'data' => [
'batch_id' => $batchId,
'batch_no' => $batchNo,
'slot' => $slot,
'appended' => !empty($payload['appended']),
'submitted_count' => count($ids),
'bilangan' => $bilangan,
'jumlah_keseluruhan' => (float) $jumlah,
'pc_submitted_by' => $pcBy,
'cawangan_nama' => $this->cawanganNama($kodcaw),
],
]);
} catch (\Throwable $e) {
Log::warning('Operasi batch notification failed: '.$e->getMessage(), [
'kodcaw' => $kodcaw,
'batch_id' => $batchId,
]);
}
}
/**
* @param string $kodcaw
* @return string
*/
protected function cawanganNama($kodcaw)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
if (!$cawangan) {
return $kodcaw;
}
$nama = trim((string) $cawangan->nama_cawangan);
return $nama !== '' ? $nama : $kodcaw;
}
/**
* Inbox for PC at this branch. Failures must not block the operasi action.
*
* @param string $kodcaw
* @param string $type
* @param array $data
* @return void
*/
protected function notifyPcPembiayaanOnline($kodcaw, $type, array $data)
{
try {
app(StaffNotificationClient::class)->notify([
'type' => $type,
'cawangan' => $kodcaw,
'audience' => ['role' => 'pc'],
'data' => array_merge($data, [
'cawangan_nama' => $this->cawanganNama($kodcaw),
]),
]);
} catch (\Throwable $e) {
Log::warning('PC pembiayaan-online notification failed: '.$e->getMessage(), [
'kodcaw' => $kodcaw,
'type' => $type,
]);
}
}
/**
* @param mixed $line
* @return array
*/
protected function lineNotifyFields($line)
{
if (!$line) {
return [];
}
$isArr = is_array($line);
return [
'norujukan' => $isArr ? ($line['norujukan'] ?? '') : $line->norujukan,
'nama_pelanggan' => $isArr ? ($line['nama_pelanggan'] ?? '') : $line->nama_pelanggan,
'pinjaman' => $isArr ? ($line['pinjaman'] ?? 0) : $line->pinjaman,
'ops_by' => $isArr ? ($line['ops_by'] ?? '') : $line->ops_by,
'reject_reason' => $isArr ? ($line['reject_reason'] ?? '') : $line->reject_reason,
];
}
/**
@@ -1031,7 +1153,7 @@ class PembiayaanOnlineService
$now = Carbon::now();
try {
return DB::connection($mysql)->transaction(function () use ($mysql, $kodcaw, $id, $opsBy, $reason, $now) {
$result = DB::connection($mysql)->transaction(function () use ($mysql, $kodcaw, $id, $opsBy, $reason, $now) {
$line = PembiayaanOnline::on($mysql)
->where('kodcaw', $kodcaw)
->where('id', $id)
@@ -1102,6 +1224,18 @@ class PembiayaanOnlineService
'payload' => ['message' => 'Gagal tolak pembiayaan online'],
];
}
if (!empty($result['ok'])) {
$this->notifyPcPembiayaanOnline($kodcaw, 'pembiayaan_online.rejected_pc', array_merge(
$this->lineNotifyFields(isset($result['payload']['pembiayaan_online']) ? $result['payload']['pembiayaan_online'] : null),
[
'ops_by' => $opsBy,
'reject_reason' => $reason,
]
));
}
return $result;
}
/**
@@ -1222,7 +1356,7 @@ class PembiayaanOnlineService
}
try {
return DB::connection($mysql)->transaction(function () use (
$result = DB::connection($mysql)->transaction(function () use (
$mysql,
$kodcaw,
$id,
@@ -1321,6 +1455,18 @@ class PembiayaanOnlineService
'payload' => ['message' => 'Gagal tanda PAID / muat naik bukti'],
];
}
if (!empty($result['ok'])) {
$this->notifyPcPembiayaanOnline($kodcaw, 'pembiayaan_online.paid_pc', array_merge(
$this->lineNotifyFields(isset($result['payload']['pembiayaan_online']) ? $result['payload']['pembiayaan_online'] : null),
[
'paid_count' => 1,
'ops_by' => $opsBy,
]
));
}
return $result;
}
/**
@@ -1366,7 +1512,7 @@ class PembiayaanOnlineService
}
try {
return DB::connection($mysql)->transaction(function () use (
$result = DB::connection($mysql)->transaction(function () use (
$mysql,
$kodcaw,
$batchId,
@@ -1458,6 +1604,19 @@ class PembiayaanOnlineService
'payload' => ['message' => 'Gagal tanda PAID batch'],
];
}
if (!empty($result['ok'])) {
$batch = isset($result['payload']['batch']) ? $result['payload']['batch'] : null;
$this->notifyPcPembiayaanOnline($kodcaw, 'pembiayaan_online.paid_pc', [
'paid_count' => isset($result['payload']['dibayar']) ? (int) $result['payload']['dibayar'] : 0,
'batch_id' => $batch ? (is_array($batch) ? ($batch['id'] ?? null) : $batch->id) : $batchId,
'batch_no' => $batch ? (is_array($batch) ? ($batch['batch_no'] ?? '') : $batch->batch_no) : '',
'jumlah_keseluruhan' => $batch ? (is_array($batch) ? ($batch['jumlah_keseluruhan'] ?? 0) : $batch->jumlah_keseluruhan) : 0,
'ops_by' => $opsBy,
]);
}
return $result;
}
/**
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class StaffNotificationClient
{
/**
* Ask kaunter to persist a staff inbox row. Never throws.
*
* @param array $payload
* @return void
*/
public function notify(array $payload)
{
$base = rtrim((string) config('kaunter.url'), '/');
$secret = (string) config('kaunter.notification_secret');
if ($base === '' || $secret === '') {
Log::info('Staff notification skipped: KAUNTER_URL or INTERNAL_NOTIFICATION_SECRET is empty');
return;
}
$timeout = (int) config('kaunter.notification_timeout', 3);
if ($timeout < 1) {
$timeout = 3;
}
try {
$response = Http::timeout($timeout)
->withHeaders([
'X-Internal-Token' => $secret,
'Accept' => 'application/json',
])
->asJson()
->post($base . '/internal/notifications', $payload);
if (!$response->successful()) {
Log::warning('Staff notification to kaunter failed', [
'status' => $response->status(),
'body' => $response->body(),
'type' => isset($payload['type']) ? $payload['type'] : null,
]);
}
} catch (\Throwable $e) {
Log::warning('Staff notification to kaunter errored: '.$e->getMessage(), [
'type' => isset($payload['type']) ? $payload['type'] : null,
]);
}
}
}
+1
View File
@@ -94,6 +94,7 @@ $app->configure('report');
$app->configure('app_parameter');
$app->configure('parameter');
$app->configure('whatsapp');
$app->configure('kaunter');
/*
|--------------------------------------------------------------------------
| Register Middleware
+7
View File
@@ -0,0 +1,7 @@
<?php
return [
'url' => env('KAUNTER_URL', ''),
'notification_secret' => env('INTERNAL_NOTIFICATION_SECRET', ''),
'notification_timeout' => (int) env('KAUNTER_NOTIFICATION_TIMEOUT', 3),
];