> $students * * @return array */ 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> $tuitionStudents * @param array $eventByStudent * * @return array> */ 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; } }