Fix semester context, attendance rosters, and billing workflows
- load global semester helpers consistently and use date-based semester defaults - fix grading and daily attendance duplicate student/section rows - keep attendance violations scoped to the current semester by default - update invoice, refund, discount, payment, and financial aid flows - add configuration cleanup migrations for duplicate calendar/semester keys - refresh parent registration/report-card and print request handling - update related models, services, views, cron notes, and test coverage
This commit is contained in:
@@ -27,6 +27,12 @@ class InvoiceIssuanceService
|
||||
public function issueInvoice(IssueInvoiceCommand $command): InvoiceLedgerResult
|
||||
{
|
||||
$invoiceData = $command->invoiceData;
|
||||
if (trim((string)($invoiceData['invoice_number'] ?? '')) === '') {
|
||||
$invoiceData['invoice_number'] = $this->generateInvoiceNumber(
|
||||
(string)($invoiceData['school_year'] ?? ''),
|
||||
(int)($invoiceData['parent_id'] ?? 0)
|
||||
);
|
||||
}
|
||||
$invoiceData['status'] = FinancialStatus::INVOICE_DRAFT;
|
||||
$invoiceData['total_amount'] = $invoiceData['total_amount'] ?? number_format($command->tuitionAmount + $command->eventAmount, 2, '.', '');
|
||||
$invoiceData['balance'] = $invoiceData['balance'] ?? $invoiceData['total_amount'];
|
||||
@@ -78,6 +84,54 @@ class InvoiceIssuanceService
|
||||
}
|
||||
}
|
||||
|
||||
public function generateInvoiceNumber(string $schoolYear, ?int $parentId = null): string
|
||||
{
|
||||
$prefix = $this->invoiceNumberPrefix($schoolYear, $parentId);
|
||||
do {
|
||||
$invoiceNumber = $prefix . '-' . uniqid();
|
||||
$exists = $this->db->table('invoices')
|
||||
->where('invoice_number', $invoiceNumber)
|
||||
->countAllResults() > 0;
|
||||
} while ($exists);
|
||||
|
||||
return $invoiceNumber;
|
||||
}
|
||||
|
||||
private function invoiceNumberPrefix(string $schoolYear, ?int $parentId = null): string
|
||||
{
|
||||
$year = date('y');
|
||||
if (preg_match('/^(\d{4})-\d{4}$/', $schoolYear, $matches) === 1) {
|
||||
$year = substr($matches[1], -2);
|
||||
}
|
||||
|
||||
$parentSuffix = $this->parentSchoolIdSuffix((int)($parentId ?? 0));
|
||||
if ($parentSuffix !== '') {
|
||||
return 'INV-' . $year . $parentSuffix;
|
||||
}
|
||||
|
||||
return 'INV-' . $year . str_pad((string) max(0, (int)($parentId ?? 0)), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function parentSchoolIdSuffix(int $parentId): string
|
||||
{
|
||||
if ($parentId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$row = $this->db->table('users')
|
||||
->select('school_id')
|
||||
->where('id', $parentId)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
$schoolId = trim((string)($row['school_id'] ?? ''));
|
||||
if (preg_match('/^\d{2}(\d{5})$/', $schoolId, $matches) === 1) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function requireWrite($result, string $code, $model = null): void
|
||||
{
|
||||
if ($result === false || $result === null || $result === 0) {
|
||||
|
||||
Reference in New Issue
Block a user