fix enrollment logic, add financial aid, fix class distribution
Tests / PHPUnit (push) Failing after 1m6s

This commit is contained in:
root
2026-08-15 15:07:16 -04:00
parent c12bb59372
commit 4603d9ced2
95 changed files with 6892 additions and 1295 deletions
@@ -37,4 +37,17 @@ final class GradeLevelParser
return 999;
}
public static function isYouth($grade): bool
{
if (! is_string($grade) && ! is_numeric($grade)) {
return false;
}
$value = strtoupper(trim((string) $grade));
$value = preg_replace('/\s+/', ' ', $value) ?? $value;
$value = str_replace(['.', '_', '-'], ['', '', ' '], $value);
return (bool) preg_match('/^Y(?:OUTH)?\s*\d*$/', $value);
}
}
@@ -9,11 +9,8 @@ final class NewTuitionCalculatorService implements TuitionCalculatorInterface
public function calculateFamilyTuition(array $students, array $config): array
{
$gradeFee = (int) ($config['grade_fee'] ?? 9);
$fullAmountCents = $this->toCents($config['new_tuition_full_amount'] ?? 370);
$youthAmountCents = $this->toCents($config['new_tuition_youth_amount'] ?? $config['youth_fee'] ?? 200);
$secondDiscountCents = $this->toCents($config['new_tuition_second_student_discount'] ?? 50);
$thirdDiscountCents = $this->toCents($config['new_tuition_third_student_discount'] ?? 50);
$fourthPlusDiscountCents = $this->toCents($config['new_tuition_fourth_plus_discount'] ?? 100);
$fullAmountCents = $this->toCents($config['new_tuition_full_amount'] ?? $config['first_student_fee'] ?? 380);
$additionalDiscountCents = $this->additionalDiscountCents($config, $fullAmountCents);
usort($students, function (array $left, array $right) use ($gradeFee): int {
$leftLevel = GradeLevelParser::parse($left['grade_level'] ?? null, $gradeFee);
@@ -23,45 +20,20 @@ final class NewTuitionCalculatorService implements TuitionCalculatorInterface
});
$details = [];
$regularPosition = 0;
$familyPosition = 0;
foreach (array_values($students) as $student) {
$level = GradeLevelParser::parse($student['grade_level'] ?? null, $gradeFee);
if ($level > $gradeFee) {
$position = null;
$discountCents = 0;
$rule = 'new_youth_unit_price';
$baseAmountCents = $youthAmountCents;
} else {
$regularPosition++;
$position = $regularPosition;
if ($position === 1) {
$discountCents = 0;
$rule = 'new_first_student_full_amount';
} elseif ($position === 2) {
$discountCents = $secondDiscountCents;
$rule = 'new_second_student_discount';
} elseif ($position === 3) {
$discountCents = $thirdDiscountCents;
$rule = 'new_third_student_discount';
} else {
$discountCents = $fourthPlusDiscountCents;
$rule = 'new_fourth_plus_student_discount';
}
$baseAmountCents = $fullAmountCents;
}
$amountCents = max(0, $baseAmountCents - $discountCents);
$familyPosition++;
$discountCents = $familyPosition === 1 ? 0 : $additionalDiscountCents;
$rule = $familyPosition === 1 ? 'new_first_student_full_amount' : 'new_additional_student_discount';
$amountCents = max(0, $fullAmountCents - $discountCents);
$details[] = [
'student_id' => (int) ($student['student_id'] ?? 0),
'student_name' => (string) ($student['student_name'] ?? ''),
'grade_level' => $student['grade_level'] ?? null,
'family_position' => $position,
'full_amount' => $this->fromCents($baseAmountCents),
'family_position' => $familyPosition,
'full_amount' => $this->fromCents($fullAmountCents),
'discount' => $this->fromCents($discountCents),
'rule' => $rule,
'amount' => $this->fromCents($amountCents),
@@ -77,6 +49,19 @@ final class NewTuitionCalculatorService implements TuitionCalculatorInterface
];
}
private function additionalDiscountCents(array $config, int $fullAmountCents): int
{
if (array_key_exists('new_tuition_second_student_discount', $config) && $config['new_tuition_second_student_discount'] !== null && $config['new_tuition_second_student_discount'] !== '') {
return $this->toCents($config['new_tuition_second_student_discount']);
}
if (array_key_exists('second_student_fee', $config) && $config['second_student_fee'] !== null && $config['second_student_fee'] !== '') {
return max(0, $fullAmountCents - $this->toCents($config['second_student_fee']));
}
return $this->toCents(100);
}
private function toCents($amount): int
{
return (int) round(((float) $amount) * 100);
@@ -9,9 +9,10 @@ final class OldTuitionCalculatorService implements TuitionCalculatorInterface
public function calculateFamilyTuition(array $students, array $config): array
{
$gradeFee = (int) ($config['grade_fee'] ?? 9);
$firstStudentFee = $this->toCents($config['first_student_fee'] ?? 370);
$secondStudentFee = $this->toCents($config['second_student_fee'] ?? 200);
$youthFee = $this->toCents($config['youth_fee'] ?? 200);
$firstStudentFee = $this->toCents($config['first_student_fee'] ?? $config['new_tuition_full_amount'] ?? 380);
$secondStudentFee = isset($config['second_student_fee']) && $config['second_student_fee'] !== '' && $config['second_student_fee'] !== null
? $this->toCents($config['second_student_fee'])
: max(0, $firstStudentFee - 10000);
usort($students, function (array $left, array $right) use ($gradeFee): int {
$leftLevel = GradeLevelParser::parse($left['grade_level'] ?? null, $gradeFee);
@@ -20,20 +21,13 @@ final class OldTuitionCalculatorService implements TuitionCalculatorInterface
return [$leftLevel, (int) ($left['student_id'] ?? 0)] <=> [$rightLevel, (int) ($right['student_id'] ?? 0)];
});
$regularCount = 0;
$familyPosition = 0;
$details = [];
foreach ($students as $student) {
$level = GradeLevelParser::parse($student['grade_level'] ?? null, $gradeFee);
if ($level > $gradeFee) {
$amountCents = $youthFee;
$rule = 'old_youth_fee';
} else {
$regularCount++;
$amountCents = $regularCount === 1 ? $firstStudentFee : $secondStudentFee;
$rule = $regularCount === 1 ? 'old_first_student_fee' : 'old_second_student_fee';
}
$familyPosition++;
$amountCents = $familyPosition === 1 ? $firstStudentFee : $secondStudentFee;
$rule = $familyPosition === 1 ? 'old_first_student_fee' : 'old_additional_student_fee';
$details[] = [
'student_id' => (int) ($student['student_id'] ?? 0),
@@ -43,7 +43,6 @@ class TuitionForecastService
$mode = $this->normalizeMode($mode);
$options = $this->normalizeOptions($options);
$this->unitPriceOverride = $options['unit_price'];
$this->youthUnitPriceOverride = $options['youth_unit_price'];
$tuitionConfig = $this->getTuitionConfig();
$familyRows = [];
$summary = [
@@ -119,7 +118,7 @@ class TuitionForecastService
$summary['projected_tuition'] = $mode === 'old' ? $summary['old_projected_tuition'] : $summary['new_projected_tuition'];
$summary['projected_income'] = $mode === 'old' ? $summary['old_projected_income'] : $summary['new_projected_income'];
$summary['unit_price'] = number_format((float) ($tuitionConfig['new_tuition_full_amount'] ?? 0), 2, '.', '');
$summary['youth_unit_price'] = number_format((float) ($tuitionConfig['new_tuition_youth_amount'] ?? 0), 2, '.', '');
$summary['youth_unit_price'] = $summary['unit_price'];
return [
'school_year' => $schoolYear,
@@ -545,32 +544,18 @@ class TuitionForecastService
'grade_fee' => $this->configurationModel->getConfig('grade_fee'),
'first_student_fee' => $this->configurationModel->getConfig('first_student_fee'),
'second_student_fee' => $this->configurationModel->getConfig('second_student_fee'),
'youth_fee' => $this->configurationModel->getConfig('youth_fee') ?? '200.00',
'youth_fee' => $this->configurationModel->getConfig('first_student_fee') ?? '380.00',
'new_tuition_full_amount' => $this->normalizedUnitPriceOverride(),
'new_tuition_youth_amount' => $this->normalizedYouthUnitPriceOverride(),
'new_tuition_second_student_discount' => $this->configurationModel->getConfig('new_tuition_second_student_discount'),
'new_tuition_third_student_discount' => $this->configurationModel->getConfig('new_tuition_third_student_discount'),
'new_tuition_fourth_plus_discount' => $this->configurationModel->getConfig('new_tuition_fourth_plus_discount'),
'new_tuition_second_student_discount' => $this->configurationModel->getConfig('new_tuition_second_student_discount') ?? '100.00',
];
}
protected ?string $unitPriceOverride = null;
protected ?string $youthUnitPriceOverride = null;
protected function normalizedUnitPriceOverride(): string
{
return $this->unitPriceOverride
?? (string) ($this->configurationModel->getConfig('new_tuition_full_amount') ?? '0.00');
}
protected function normalizedYouthUnitPriceOverride(): string
{
return $this->youthUnitPriceOverride
?? (string) (
$this->configurationModel->getConfig('new_tuition_youth_amount')
?? $this->configurationModel->getConfig('youth_fee')
?? '200.00'
);
?? (string) ($this->configurationModel->getConfig('new_tuition_full_amount') ?? '380.00');
}
protected function normalizeMoney($value): ?string