fix deployment db issue and widthrawal administration page
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 47s
Tests / PHPUnit (push) Failing after 1m19s

This commit is contained in:
root
2026-08-20 20:18:00 -04:00
parent 889c037660
commit d3da699e55
19 changed files with 1170 additions and 194 deletions
+115 -21
View File
@@ -514,9 +514,16 @@ class InvoiceController extends ResourceController
bool $recalculateDiscounts = true
)
{
$isAjax = $this->request->isAJAX() || str_contains(strtolower($this->request->getHeaderLine('Accept')), 'application/json');
$request = $this->request ?? service('request');
$isAjax = $request !== null && (
$request->isAJAX()
|| str_contains(strtolower($request->getHeaderLine('Accept')), 'application/json')
);
// Programmatic callers (new InvoiceController() without initController) have no response object.
$hasHttpResponse = $this->response !== null;
if ($parentId == null) {
$parentId = (int)$this->request->getPost('parent_id');
$parentId = (int) ($request?->getPost('parent_id') ?? 0);
}
$schoolYear = (string) ($schoolYearOverride ?: $this->schoolYear);
$semester = (string) ($semesterOverride ?: $this->semester);
@@ -528,10 +535,13 @@ class InvoiceController extends ResourceController
->findAll();
if (empty($enrollments)) {
if ($isAjax) {
return $this->response->setJSON(['ok' => false, 'message' => 'No enrollment records found.']);
}
return redirect()->back()->with('error', 'No enrollment records found.');
return $this->invoiceGenerationResult(
$hasHttpResponse,
$isAjax,
['ok' => false, 'message' => 'No enrollment records found.'],
422,
'No enrollment records found.'
);
}
$registeredKids = [];
@@ -605,6 +615,24 @@ class InvoiceController extends ResourceController
log_message('info', "Updated invoice ID {$invoice['id']} for parent ID {$parentId}.");
$updated = true;
} else {
$hasNonZeroTuitionOrEvents = abs((float) $tuitionFee) > 0.00001
|| abs((float) $eventchargeTotal) > 0.00001;
$hasApprovedAdjustments = $this->parentHasApprovedInvoiceAdjustments(
(int) $parentId,
$schoolYear,
$semester
);
if (! $hasNonZeroTuitionOrEvents && ! $hasApprovedAdjustments) {
return $this->invoiceGenerationResult(
$hasHttpResponse,
$isAjax,
['ok' => false, 'message' => 'Invoice requires at least one non-zero line.'],
422,
'Invoice requires at least one non-zero line.'
);
}
$issueUtc = (new DateTime('now', new DateTimeZone('UTC')))->format('Y-m-d H:i:s');
// Due date: interpret the date in configured/user local TZ,
@@ -642,29 +670,95 @@ class InvoiceController extends ResourceController
log_message('info', "Invoice created successfully. Insert ID: {$insertId}");
} catch (\Throwable $e) {
log_message('error', 'Invoice issuance failed: ' . $e->getMessage() . ' errors=' . json_encode($this->invoiceModel->errors()));
if ($isAjax) {
return $this->response->setJSON(['ok' => false, 'message' => 'Failed to create invoice.']);
}
return redirect()->back()->with('error', 'Failed to create invoice. Please check input values.');
$message = str_contains($e->getMessage(), 'non-zero invoice line')
? 'Invoice requires at least one non-zero line.'
: 'Failed to create invoice.';
return $this->invoiceGenerationResult(
$hasHttpResponse,
$isAjax,
['ok' => false, 'message' => $message],
422,
$message === 'Invoice requires at least one non-zero line.'
? $message
: 'Failed to create invoice. Please check input values.'
);
}
$updated = false;
}
// Success response
$successPayload = [
'ok' => true,
'updated' => $updated,
'updated_ids' => $updatedIds,
'insert_id' => isset($insertId) ? (int)$insertId : null,
csrf_token() => csrf_hash(),
'csrfTokenName' => csrf_token(),
'csrfHash' => csrf_hash(),
];
return $this->invoiceGenerationResult(
$hasHttpResponse,
$isAjax,
$successPayload,
200,
null,
$updated ? 'Invoice updated.' : 'Invoice created.'
);
}
/**
* Safe invoice response helper for both HTTP and programmatic callers.
*
* @param array<string, mixed> $payload
*/
private function invoiceGenerationResult(
bool $hasHttpResponse,
bool $isAjax,
array $payload,
int $statusCode = 200,
?string $errorFlash = null,
?string $successFlash = null
) {
if (! $hasHttpResponse || $this->response === null) {
return $payload;
}
if ($isAjax) {
return $this->response->setJSON([
'ok' => true,
'updated' => $updated,
'updated_ids' => $updatedIds,
'insert_id' => isset($insertId) ? (int)$insertId : null,
csrf_token() => csrf_hash(),
'csrfTokenName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
return $this->response
->setStatusCode($statusCode)
->setJSON($payload);
}
if ($errorFlash !== null) {
return redirect()->back()->with('error', $errorFlash);
}
return redirect()->to(route_to('InvoiceController::index'))
->with('success', $updated ? 'Invoice updated.' : 'Invoice created.');
->with('success', $successFlash ?? 'Invoice saved.');
}
private function parentHasApprovedInvoiceAdjustments(int $parentId, string $schoolYear, string $semester): bool
{
if ($parentId <= 0 || $schoolYear === '' || $semester === '') {
return false;
}
try {
return $this->additionalChargeModel
->where('parent_id', $parentId)
->where('school_year', $schoolYear)
->where('semester', $semester)
->where('status', FinancialStatus::ADDITIONAL_CHARGE_APPROVED)
->where('amount !=', 0)
->countAllResults() > 0;
} catch (\Throwable $e) {
log_message('warning', 'Unable to check approved invoice adjustments: {message}', [
'message' => $e->getMessage(),
]);
return false;
}
}
private function selectActiveInvoiceForParentYear(int $parentId, string $schoolYear): ?array