fix enrollment, invoice, payment and financila aid
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-24 21:20:58 -04:00
parent 576da3bd78
commit 608aca79b8
30 changed files with 3327 additions and 774 deletions
@@ -116,15 +116,31 @@ final class EnrollmentRegistrationEmailService
foreach ($this->recipientFamilies($schoolYear, null) as $family) {
$message = $this->buildMessage($schoolYear, $family);
$latest = $this->latestEmailRecord($schoolYearName, (int) $family['parent_user_id']);
$previousYear = $this->previousSchoolYearName($schoolYearName);
$studentSummaries = [];
foreach ($family['students'] as $student) {
$studentId = (int) ($student['id'] ?? 0);
$evaluation = $studentId > 0 && $previousYear !== null
? $this->transitionService->evaluateForParent(
(int) $family['parent_user_id'],
$studentId,
$previousYear,
$schoolYearName
)
: [];
$studentSummaries[] = [
'name' => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')) ?: 'Student #' . $studentId,
'adult_student' => ! empty($evaluation['adult_student']),
];
}
$examples[] = [
'parent_user_id' => (int) $family['parent_user_id'],
'parent_name' => (string) $family['name'],
'recipients' => $message['recipients'],
'student_names' => array_map(
static fn (array $student): string => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')) ?: 'Student #' . (int) ($student['id'] ?? 0),
$family['students']
),
'students' => $studentSummaries,
'student_names' => array_column($studentSummaries, 'name'),
'subject' => (string) $message['subject'],
'delivery_status' => (string) ($latest['delivery_status'] ?? 'not sent'),
'sent_at' => $latest['sent_at'] ?? null,
@@ -161,10 +177,15 @@ final class EnrollmentRegistrationEmailService
continue;
}
$evaluation = $this->transitionService->evaluate($studentId, $previousYear, $schoolYearName, 'parent');
$evaluation = $this->transitionService->evaluateForParent(
(int) $family['parent_user_id'],
$studentId,
$previousYear,
$schoolYearName
);
$studentRows[] = $this->studentRow($student, $evaluation, $opens, $deadline, $schoolYear);
$studentIds[] = $studentId;
$hasReEnrollmentEligibleStudent = $hasReEnrollmentEligibleStudent || $this->canCompleteParentReEnrollment($evaluation);
$hasReEnrollmentEligibleStudent = $hasReEnrollmentEligibleStudent || (bool) ($evaluation['can_enroll'] ?? false);
}
$financial = $this->financialSection((int) $family['parent_user_id'], $schoolYear, $previousYear);
@@ -192,7 +213,7 @@ final class EnrollmentRegistrationEmailService
private function canCompleteParentReEnrollment(array $evaluation): bool
{
return (bool) ($evaluation['parent_enrollment_allowed'] ?? false);
return (bool) ($evaluation['can_enroll'] ?? false);
}
private function registrationStepsSection(string $opens, string $deadline, bool $include): string
@@ -227,7 +248,7 @@ final class EnrollmentRegistrationEmailService
$requiredAction = $this->requiredAction($evaluation, $deadline, $name);
$message = $this->decisionMessage($name, $evaluation, $opens, $deadline, $schoolYear);
$nextStepHtml = $this->decisionMessageHtml($message, (string) ($evaluation['deliberation_decision'] ?? ''));
if ($requiredAction !== '') {
if ($this->shouldAppendRequiredAction($message, $requiredAction)) {
$nextStepHtml .= '<br><strong>' . esc($requiredAction) . '</strong>';
}
@@ -239,7 +260,7 @@ final class EnrollmentRegistrationEmailService
. '</td>'
. '</tr>'
. $this->studentDetailRow('Decision', esc($decision ?: 'Pending'))
. $this->studentDetailRow('Registration Status', '<strong>' . esc($status) . '</strong><br>' . esc($placement))
. $this->studentDetailRow('Registration Status', '<strong>' . esc($status) . '</strong>' . ($placement !== '' ? '<br>' . esc($placement) : ''))
. $this->studentDetailRow('Next Step', $nextStepHtml)
. '</tbody></table>';
}
@@ -272,14 +293,15 @@ final class EnrollmentRegistrationEmailService
private function decisionMessage(string $name, array $evaluation, string $opens, string $deadline, array $schoolYear = []): string
{
if ((string) ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::PASSED) {
$grade = $this->currentGradeText($evaluation);
return $name . ' has successfully passed ' . $grade . '.';
if (($evaluation['can_enroll'] ?? false) === false && ! empty($evaluation['primary_parent_message'])) {
return (string) $evaluation['primary_parent_message'];
}
if (($evaluation['blockers'] ?? []) !== []) {
return implode(' ', array_map('strval', $evaluation['blockers']));
return implode(' ', array_values(array_unique(array_filter(array_map(
static fn ($blocker): string => trim((string) $blocker),
$evaluation['blockers']
)))));
}
return match ((string) ($evaluation['deliberation_decision'] ?? '')) {
@@ -318,11 +340,13 @@ final class EnrollmentRegistrationEmailService
private function financialSection(int $parentId, array $schoolYear, ?string $previousYear): string
{
$carry = $previousYear !== null ? $this->invoiceBalance($parentId, $previousYear) : 0.0;
$registrationFee = (float) ($schoolYear['registration_fee'] ?? 0);
$tuition = (float) ($schoolYear['tuition_due_at_registration'] ?? 0);
$mandatory = (float) ($schoolYear['mandatory_fees'] ?? 0);
$total = max(0, $carry) + $registrationFee + $tuition + $mandatory;
$schoolYearName = (string) ($schoolYear['name'] ?? '');
$summary = $this->transitionService->getEnrollmentFinancialSummary(
$parentId,
$previousYear ?? '',
$schoolYearName
);
$total = (float) ($summary['total_enrollment_due'] ?? $summary['amount_due'] ?? 0);
if ($total <= 0.0) {
return '';
}
@@ -333,7 +357,7 @@ final class EnrollmentRegistrationEmailService
}
return '<h3>Family Account Information</h3>'
. '<p><strong>Carry-over balance:</strong> $' . number_format($carry, 2) . '<br>'
. '<p><strong>Carry-over balance:</strong> $' . number_format((float) ($summary['carry_forward_balance'] ?? 0), 2) . '<br>'
. '<strong>Total currently due:</strong> $' . number_format($total, 2) . '</p>'
. '<p>' . esc($message) . '</p>';
}
@@ -488,40 +512,73 @@ final class EnrollmentRegistrationEmailService
private function registrationStatus(array $evaluation): string
{
if ((string) ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::PASSED) {
return 'Eligible';
if (($evaluation['can_enroll'] ?? false) === true) {
return ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::MAKE_UP_EXAM
? 'Eligible with pending placement'
: 'Eligible';
}
if (! empty($evaluation['adult_student'])) {
return 'Not Eligible';
}
if (($evaluation['blockers'] ?? []) !== []) {
return ($evaluation['adult_student'] ?? false) ? 'Student Action Required' : 'Not Eligible';
return 'Not Eligible';
}
return ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::MAKE_UP_EXAM ? 'Eligible with pending placement' : 'Eligible';
return 'Not Eligible';
}
private function placementText(array $evaluation): string
{
return match ((string) ($evaluation['placement_status'] ?? '')) {
$placement = match ((string) ($evaluation['placement_status'] ?? '')) {
'automatic_distribution_pending' => $this->assignedGradeText($evaluation),
'same_class_assigned' => 'Same grade and class',
'temporary_same_grade' => 'Same grade initially',
'manual_class_required' => 'Same grade',
'exit_required' => 'Completion or exit process required',
default => 'Pending',
default => '',
};
if ($placement !== '') {
return $placement;
}
return ($evaluation['can_enroll'] ?? false) === true ? 'Pending' : '';
}
private function requiredAction(array $evaluation, string $deadline, string $name = 'The student'): string
{
if ((string) ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::PASSED) {
return 'Complete re-enrollment before ' . $deadline . '.';
if (($evaluation['can_enroll'] ?? false) === true) {
return ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::MAKE_UP_EXAM
? ''
: 'Complete re-enrollment before ' . $deadline . '.';
}
if (($evaluation['blockers'] ?? []) !== []) {
return ($evaluation['adult_student'] ?? false) ? 'Student must complete the authorized adult-student process or contact administration.' : 'Contact the school administration.';
if (! empty($evaluation['primary_parent_message'])) {
return (string) $evaluation['primary_parent_message'];
}
return ($evaluation['deliberation_decision'] ?? '') === DeliberationDecision::MAKE_UP_EXAM
? ''
: 'Complete re-enrollment before ' . $deadline . '.';
return 'Contact the school administration.';
}
private function shouldAppendRequiredAction(string $message, string $requiredAction): bool
{
$message = trim($message);
$requiredAction = trim($requiredAction);
if ($requiredAction === '') {
return false;
}
if ($message === '') {
return true;
}
$normalizedMessage = preg_replace('/\s+/', ' ', strtolower($message)) ?? $message;
$normalizedAction = preg_replace('/\s+/', ' ', strtolower($requiredAction)) ?? $requiredAction;
return $normalizedMessage !== $normalizedAction
&& ! str_contains($normalizedMessage, $normalizedAction)
&& ! str_contains($normalizedAction, $normalizedMessage);
}
private function previousSchoolYearName(string $schoolYear): ?string