$businessId, 'created_by' => $userId, 'status' => 'pending', ])); if (! empty($data['accounting_account_id']) && ! empty($data['counter_account_id'])) { $this->postJournalForCheque($cheque, (int) $data['counter_account_id'], $userId, 'cheque_register'); } return $cheque; } public function allowedTransitions(string $status): array { $allowed = [ 'pending' => ['deposited', 'cleared', 'cancelled', 'endorsed'], 'deposited' => ['cleared', 'bounced'], 'cleared' => [], 'bounced' => ['pending'], 'endorsed' => ['cleared', 'cancelled'], 'cancelled' => [], ]; return $allowed[$status] ?? []; } public function transitionRequiresCounterAccount(string $newStatus): bool { return in_array($newStatus, ['deposited', 'cleared', 'bounced'], true); } public function transition(AccountingCheque $cheque, string $newStatus, int $userId, ?int $counterAccountId = null): AccountingCheque { if (! in_array($newStatus, $this->allowedTransitions($cheque->status), true)) { throw new \RuntimeException('Invalid cheque status transition.'); } if ($this->transitionRequiresCounterAccount($newStatus) && empty($counterAccountId)) { throw new \RuntimeException('Counter account is required for this cheque transition.'); } if (in_array($newStatus, ['deposited', 'cleared', 'bounced'], true) && $counterAccountId && $cheque->accounting_account_id) { $this->postJournalForCheque($cheque, $counterAccountId, $userId, 'cheque_'.$newStatus, $newStatus !== 'deposited'); } $cheque->status = $newStatus; $cheque->save(); return $cheque; } protected function postJournalForCheque( AccountingCheque $cheque, int $counterAccountId, int $userId, string $sourceType, bool $reverseDirection = false ): void { $amount = (float) $cheque->amount; $bankAccountId = (int) $cheque->accounting_account_id; if ($cheque->direction === 'received') { $debitAccount = $reverseDirection ? $counterAccountId : $bankAccountId; $creditAccount = $reverseDirection ? $bankAccountId : $counterAccountId; } else { $debitAccount = $reverseDirection ? $bankAccountId : $counterAccountId; $creditAccount = $reverseDirection ? $counterAccountId : $bankAccountId; } $mapping = $this->postingService->post(new FinancialPostingRequest( businessId: $cheque->business_id, userId: $userId, sourceType: $sourceType, sourceId: $cheque->id, note: 'Cheque '.$cheque->cheque_number, lines: [ ['accounting_account_id' => $debitAccount, 'amount' => $amount, 'type' => 'debit'], ['accounting_account_id' => $creditAccount, 'amount' => $amount, 'type' => 'credit'], ], operationDate: optional($cheque->due_date)->format('Y-m-d') ?? now()->toDateString(), )); $cheque->journal_mapping_id = $mapping?->id; $cheque->save(); } }