where('roles', $role) ->where('cawangan', $kodcaw) ->get(); $created = collect(); foreach ($users as $user) { $created->push($this->notifyUser($user, $kodcaw, $role, $payload)); } return $created; } /** * Create one inbox row per staff matching role (any branch). For HQ like operasi. * * @param string $role * @param array $payload * @return \Illuminate\Support\Collection */ public function notifyRole($role, array $payload) { $kodcaw = isset($payload['cawangan']) ? $payload['cawangan'] : null; $users = User::query()->where('roles', $role)->get(); $created = collect(); foreach ($users as $user) { $created->push($this->notifyUser($user, $kodcaw, $role, $payload)); } return $created; } /** * Entry point for api_arrahn (and local callers). Builds title/url from type when omitted. * * @param array $input * @return \Illuminate\Support\Collection */ public function dispatch(array $input) { $type = isset($input['type']) ? $input['type'] : ''; $kodcaw = isset($input['cawangan']) ? $input['cawangan'] : null; $audience = isset($input['audience']) && is_array($input['audience']) ? $input['audience'] : []; $role = isset($audience['role']) ? $audience['role'] : null; $data = isset($input['data']) && is_array($input['data']) ? $input['data'] : []; $payload = $this->payloadForType($type, $kodcaw, $data); if (!empty($input['title'])) { $payload['title'] = $input['title']; } if (array_key_exists('body', $input) && $input['body'] !== null && $input['body'] !== '') { $payload['body'] = $input['body']; } if (!empty($input['url'])) { $payload['url'] = $input['url']; } if ($role === 'operasi') { return $this->notifyRole('operasi', $payload); } if ($role && $kodcaw) { return $this->notifyRoleAtCawangan($role, $kodcaw, $payload); } return collect(); } /** * @param string $type * @param string|null $kodcaw * @param array $data * @return array */ protected function payloadForType($type, $kodcaw, array $data) { if ($type === self::TYPE_MODAL_PENDING_OPERASI) { $jenis = isset($data['jenis']) ? strtoupper((string) $data['jenis']) : 'HAD'; $label = $jenis === 'TOPUP' ? 'tambah modal' : 'had modal'; $nama = !empty($data['cawangan_nama']) ? $data['cawangan_nama'] : $kodcaw; $amaun = number_format(isset($data['amaun']) ? (float) $data['amaun'] : 0, 2); $oleh = isset($data['dimohon_oleh']) ? trim((string) $data['dimohon_oleh']) : ''; return [ 'type' => $type, 'cawangan' => $kodcaw, 'title' => 'Permohonan modal online', 'body' => sprintf( '%s mohon %s RM %s%s', $nama, $label, $amaun, $oleh !== '' ? ' — '.$oleh : '' ), 'url' => '/pembiayaan-online/operasi/topup', 'data' => $data, ]; } if ($type === self::TYPE_PEMBIAYAAN_BATCH_OPERASI) { $nama = !empty($data['cawangan_nama']) ? $data['cawangan_nama'] : $kodcaw; $batchNo = !empty($data['batch_no']) ? $data['batch_no'] : 'batch'; $batchId = isset($data['batch_id']) ? $data['batch_id'] : null; $submitted = isset($data['submitted_count']) ? (int) $data['submitted_count'] : 0; $jumlah = number_format(isset($data['jumlah_keseluruhan']) ? (float) $data['jumlah_keseluruhan'] : 0, 2); $oleh = isset($data['pc_submitted_by']) ? trim((string) $data['pc_submitted_by']) : ''; $appended = !empty($data['appended']); if ($appended) { $body = sprintf( '%s tambah %d rekod ke %s (RM %s)%s', $nama, $submitted, $batchNo, $jumlah, $oleh !== '' ? ' — '.$oleh : '' ); } else { $body = sprintf( '%s hantar %s — %d rekod (RM %s)%s', $nama, $batchNo, $submitted, $jumlah, $oleh !== '' ? ' — '.$oleh : '' ); } return [ 'type' => $type, 'cawangan' => $kodcaw, 'title' => 'Batch pembiayaan online', 'body' => $body, 'url' => ($kodcaw && $batchId) ? '/pembiayaan-online/operasi/'.$kodcaw.'/batches/'.$batchId : '/pembiayaan-online/operasi', 'data' => $data, ]; } if ($type === self::TYPE_PEMBIAYAAN_REJECTED_PC) { $norujukan = !empty($data['norujukan']) ? $data['norujukan'] : 'rekod'; $reason = isset($data['reject_reason']) ? trim((string) $data['reject_reason']) : ''; $amaun = number_format(isset($data['pinjaman']) ? (float) $data['pinjaman'] : 0, 2); return [ 'type' => $type, 'cawangan' => $kodcaw, 'title' => 'Pembiayaan online ditolak', 'body' => sprintf( '%s (RM %s) ditolak%s', $norujukan, $amaun, $reason !== '' ? ' — '.$reason : '' ), 'url' => '/pembiayaan-online/pc', 'data' => $data, ]; } if ($type === self::TYPE_PEMBIAYAAN_PAID_PC) { $paidCount = isset($data['paid_count']) ? (int) $data['paid_count'] : 1; $batchNo = !empty($data['batch_no']) ? $data['batch_no'] : ''; $norujukan = !empty($data['norujukan']) ? $data['norujukan'] : ''; $nama = isset($data['nama_pelanggan']) ? trim((string) $data['nama_pelanggan']) : ''; $amaun = number_format( isset($data['pinjaman']) ? (float) $data['pinjaman'] : (isset($data['jumlah_keseluruhan']) ? (float) $data['jumlah_keseluruhan'] : 0), 2 ); if ($paidCount > 1 || ($batchNo !== '' && $norujukan === '')) { $body = sprintf( '%d rekod telah dibayar%s (RM %s)', $paidCount > 0 ? $paidCount : 0, $batchNo !== '' ? ' dalam '.$batchNo : '', $amaun ); } else { $body = sprintf( '%s telah dibayar (RM %s)%s', $norujukan !== '' ? $norujukan : 'Rekod', $amaun, $nama !== '' ? ' — '.$nama : '' ); } return [ 'type' => $type, 'cawangan' => $kodcaw, 'title' => 'Pembiayaan online telah dibayar', 'body' => $body, 'url' => '/pembiayaan-online/pc/paid', 'data' => $data, ]; } return [ 'type' => $type, 'cawangan' => $kodcaw, 'title' => isset($data['title']) ? $data['title'] : $type, 'body' => isset($data['body']) ? $data['body'] : null, 'url' => isset($data['url']) ? $data['url'] : null, 'data' => $data, ]; } /** * Create one inbox row for a specific user. * * @param \App\User $user * @param string|null $kodcaw * @param string|null $role * @param array $payload * @return \App\StaffNotification */ public function notifyUser(User $user, $kodcaw, $role, array $payload) { return StaffNotification::create([ 'user_id' => $user->id, 'cawangan' => $kodcaw, 'role' => $role ?: $user->roles, 'type' => $payload['type'], 'title' => $payload['title'], 'body' => isset($payload['body']) ? $payload['body'] : null, 'url' => isset($payload['url']) ? $payload['url'] : null, 'data' => isset($payload['data']) ? $payload['data'] : null, ]); } /** * @param \App\User $user * @param int $limit * @return \Illuminate\Support\Collection */ public function inboxFor(User $user, $limit = 20) { return StaffNotification::query() ->where('user_id', $user->id) ->orderByDesc('id') ->limit($limit) ->get(); } /** * @param \App\User $user * @return int */ public function unreadCount(User $user) { return StaffNotification::query() ->where('user_id', $user->id) ->unread() ->count(); } /** * @param \App\User $user * @param int $id * @return \App\StaffNotification|null */ public function markAsRead(User $user, $id) { $notification = StaffNotification::query() ->where('user_id', $user->id) ->where('id', $id) ->first(); if (!$notification) { return null; } if ($notification->read_at === null) { $notification->read_at = now(); $notification->save(); } return $notification; } /** * @param \App\User $user * @return int */ public function markAllAsRead(User $user) { return StaffNotification::query() ->where('user_id', $user->id) ->unread() ->update(['read_at' => now()]); } /** * @param \App\User $user * @param int $limit * @return array */ public function inboxPayload(User $user, $limit = 20) { return [ 'unread_count' => $this->unreadCount($user), 'notifications' => $this->inboxFor($user, $limit)->map(function (StaffNotification $notification) { return $notification->toInboxArray(); })->values(), ]; } }