update api and add more features
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Billing;
|
||||
|
||||
use App\Models\DiscountUsage;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Refund;
|
||||
use App\Services\Fees\FeeConfigService;
|
||||
use App\Services\Fees\TuitionCalculationService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* BalanceCalculationService (plan §6, §13 Phase 2)
|
||||
*
|
||||
* Combines tuition, extra charges, and event charges, then nets payments,
|
||||
* credits, and refunds to produce family- and student-level balances.
|
||||
*/
|
||||
class BalanceCalculationService
|
||||
{
|
||||
public function __construct(
|
||||
private FeeConfigService $config,
|
||||
private TuitionCalculationService $tuition,
|
||||
private BillingTotalsService $billingTotals
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $students
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function calculateFamilyAccount(array $students, int $parentId, ?string $schoolYear = null): array
|
||||
{
|
||||
$schoolYear = $schoolYear ?? $this->config->getSchoolYear();
|
||||
|
||||
$tuitionBreakdown = $this->tuition->calculate($students);
|
||||
$tuitionTotal = (float) ($tuitionBreakdown['family_total'] ?? 0);
|
||||
$extraCharges = $this->billingTotals->additionalSubtotalByParent($parentId, $schoolYear);
|
||||
$eventByStudent = $this->billingTotals->eventSubtotalsByStudent($parentId, $schoolYear);
|
||||
$eventCharges = round(array_sum($eventByStudent), 2);
|
||||
$lateFees = 0.0;
|
||||
|
||||
$totalCharges = round($tuitionTotal + $extraCharges + $eventCharges + $lateFees, 2);
|
||||
|
||||
$totalPayments = round(Payment::getTotalPaidByParentId($parentId, $schoolYear), 2);
|
||||
$totalCredits = round(
|
||||
DiscountUsage::getTotalDiscountByParentIdAndSchoolYear($parentId, $schoolYear),
|
||||
2
|
||||
);
|
||||
$refundsApplied = round(
|
||||
Refund::totalApprovedPaidOutForParentAndYear($parentId, $schoolYear),
|
||||
2
|
||||
);
|
||||
|
||||
$netting = round($totalPayments + $totalCredits + $refundsApplied, 2);
|
||||
$rawRemaining = round($totalCharges - $netting, 2);
|
||||
$remainingBalance = max(0.0, $rawRemaining);
|
||||
$accountCredit = $rawRemaining < 0 ? round(abs($rawRemaining), 2) : 0.0;
|
||||
|
||||
$studentRows = $this->buildStudentBalances(
|
||||
$tuitionBreakdown['students'] ?? [],
|
||||
$eventByStudent,
|
||||
$totalCharges,
|
||||
$totalPayments,
|
||||
$totalCredits,
|
||||
$refundsApplied
|
||||
);
|
||||
|
||||
Log::info('Family account balance calculated', [
|
||||
'parent_id' => $parentId,
|
||||
'school_year' => $schoolYear,
|
||||
'total_charges' => $totalCharges,
|
||||
'remaining_balance' => $remainingBalance,
|
||||
'account_credit' => $accountCredit,
|
||||
]);
|
||||
|
||||
return [
|
||||
'parent_id' => $parentId,
|
||||
'school_year' => $schoolYear,
|
||||
'charges' => [
|
||||
'tuition' => $tuitionTotal,
|
||||
'extra_charges' => $extraCharges,
|
||||
'event_charges' => $eventCharges,
|
||||
'late_fees' => $lateFees,
|
||||
'total' => $totalCharges,
|
||||
],
|
||||
'credits' => [
|
||||
'discounts' => $totalCredits,
|
||||
'total' => $totalCredits,
|
||||
],
|
||||
'payments' => $totalPayments,
|
||||
'refunds_applied' => $refundsApplied,
|
||||
'remaining_balance' => $remainingBalance,
|
||||
'account_credit' => $accountCredit,
|
||||
'tuition' => $tuitionBreakdown,
|
||||
'students' => $studentRows,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $tuitionStudents
|
||||
* @param array<int, float> $eventByStudent
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function buildStudentBalances(
|
||||
array $tuitionStudents,
|
||||
array $eventByStudent,
|
||||
float $familyTotalCharges,
|
||||
float $totalPayments,
|
||||
float $totalCredits,
|
||||
float $refundsApplied
|
||||
): array {
|
||||
$familyNetting = $totalPayments + $totalCredits + $refundsApplied;
|
||||
$rows = [];
|
||||
|
||||
foreach ($tuitionStudents as $student) {
|
||||
$studentId = (int) ($student['student_id'] ?? 0);
|
||||
$tuitionAmount = (float) ($student['final_tuition_amount'] ?? $student['tuition_fee'] ?? 0);
|
||||
$eventAmount = (float) ($eventByStudent[$studentId] ?? 0);
|
||||
$extraAmount = 0.0;
|
||||
$studentTotalCharges = round($tuitionAmount + $eventAmount + $extraAmount, 2);
|
||||
|
||||
$share = $familyTotalCharges > 0
|
||||
? ($studentTotalCharges / $familyTotalCharges)
|
||||
: 0.0;
|
||||
|
||||
$paymentsApplied = round($totalPayments * $share, 2);
|
||||
$creditsApplied = round($totalCredits * $share, 2);
|
||||
$refundsAppliedShare = round($refundsApplied * $share, 2);
|
||||
$allocatedNetting = round($familyNetting * $share, 2);
|
||||
$rawRemaining = round($studentTotalCharges - $allocatedNetting, 2);
|
||||
|
||||
$rows[] = [
|
||||
'student_id' => $studentId > 0 ? $studentId : ($student['student_id'] ?? null),
|
||||
'student_name' => $student['student_name'] ?? null,
|
||||
'grade' => $student['grade'] ?? null,
|
||||
'enrollment_status' => $student['enrollment_status'] ?? null,
|
||||
'tuition' => round($tuitionAmount, 2),
|
||||
'extra_charges' => $extraAmount,
|
||||
'event_charges' => $eventAmount,
|
||||
'total_charges' => $studentTotalCharges,
|
||||
'payments_applied' => $paymentsApplied,
|
||||
'credits_applied' => $creditsApplied,
|
||||
'refunds_applied' => $refundsAppliedShare,
|
||||
'remaining_balance' => max(0.0, $rawRemaining),
|
||||
'account_credit' => $rawRemaining < 0 ? round(abs($rawRemaining), 2) : 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
$unassignedEvent = (float) ($eventByStudent[0] ?? 0);
|
||||
if ($unassignedEvent > 0) {
|
||||
$rows[] = [
|
||||
'student_id' => null,
|
||||
'student_name' => null,
|
||||
'grade' => null,
|
||||
'enrollment_status' => null,
|
||||
'tuition' => 0.0,
|
||||
'extra_charges' => 0.0,
|
||||
'event_charges' => $unassignedEvent,
|
||||
'total_charges' => $unassignedEvent,
|
||||
'payments_applied' => 0.0,
|
||||
'credits_applied' => 0.0,
|
||||
'refunds_applied' => 0.0,
|
||||
'remaining_balance' => $unassignedEvent,
|
||||
'account_credit' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user