create(array_merge($payload, [ 'status' => $status, 'sent_at' => $status === 'sent' ? now() : null, 'failed_at' => $status === 'failed' ? now() : null, 'created_by' => $actorId, ])); return $log->toArray(); } public function list(array $filters): array { $q = FinanceNotificationLog::query(); foreach (['status','notification_type','parent_id'] as $field) if (!empty($filters[$field])) $q->where($field, $filters[$field]); if (!empty($filters['date_from'])) $q->whereDate('created_at', '>=', $filters['date_from']); if (!empty($filters['date_to'])) $q->whereDate('created_at', '<=', $filters['date_to']); return ['rows' => $q->orderByDesc('id')->paginate((int)($filters['per_page'] ?? 25))]; } public function nextReceiptNumber(string $schoolYear): string { return DB::transaction(function () use ($schoolYear) { $seq = FinanceReceiptSequence::query()->lockForUpdate()->firstOrCreate(['school_year' => $schoolYear], ['prefix' => 'R', 'next_number' => 1]); $number = $seq->prefix . '-' . $schoolYear . '-' . str_pad((string) $seq->next_number, 6, '0', STR_PAD_LEFT); $seq->next_number = $seq->next_number + 1; $seq->save(); return $number; }); } }