52 lines
1.8 KiB
PHP
52 lines
1.8 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
/**
|
|
* PHP port of app/Controllers/financial_aid_formula.py
|
|
*/
|
|
class FinancialAidEstimateModel
|
|
{
|
|
private const FEDERAL_POVERTY_BASE = 15960.0;
|
|
private const FEDERAL_POVERTY_STEP = 5680.0;
|
|
private const MASSACHUSETTS_COST_FACTOR = 1.05757;
|
|
private const PROTECTED_INCOME_MULTIPLIER = 2.0;
|
|
private const DISCRETIONARY_CONTRIBUTION_RATE = 0.10;
|
|
|
|
public function estimatedAid(float $totalBalance, int $householdSize, float $householdIncome): float
|
|
{
|
|
if ($householdSize < 1) {
|
|
throw new InvalidArgumentException('household_size must be at least 1');
|
|
}
|
|
if ($totalBalance < 0 || $householdIncome < 0) {
|
|
throw new InvalidArgumentException('balance and income cannot be negative');
|
|
}
|
|
|
|
$federalPovertyLevel = self::FEDERAL_POVERTY_BASE + (self::FEDERAL_POVERTY_STEP * ($householdSize - 1));
|
|
$adjustedNeedThreshold = $federalPovertyLevel * self::MASSACHUSETTS_COST_FACTOR;
|
|
$protectedIncome = self::PROTECTED_INCOME_MULTIPLIER * $adjustedNeedThreshold;
|
|
$discretionaryIncome = max(0.0, $householdIncome - $protectedIncome);
|
|
$expectedContribution = self::DISCRETIONARY_CONTRIBUTION_RATE * $discretionaryIncome;
|
|
$estimated = $totalBalance - $expectedContribution;
|
|
|
|
return round(max(0.0, min($totalBalance, $estimated)), 2);
|
|
}
|
|
|
|
public function finalRebate(
|
|
float $totalBalance,
|
|
int $householdSize,
|
|
float $householdIncome,
|
|
float $amountRequested
|
|
): float {
|
|
if ($amountRequested < 0) {
|
|
throw new InvalidArgumentException('amount_requested cannot be negative');
|
|
}
|
|
|
|
$estimated = $this->estimatedAid($totalBalance, $householdSize, $householdIncome);
|
|
|
|
return min($estimated, $amountRequested, $totalBalance);
|
|
}
|
|
}
|