fix apply the plan docs/alrahma_api_fix_plan_school_year_only_v7
API CI/CD / Validate (composer + pint) (push) Successful in 3m10s
API CI/CD / Test (PHPUnit) (push) Failing after 6m49s
API CI/CD / Build frontend assets (push) Successful in 1m3s
API CI/CD / Security audit (push) Failing after 1m0s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
API CI/CD / Validate (composer + pint) (push) Successful in 3m10s
API CI/CD / Test (PHPUnit) (push) Failing after 6m49s
API CI/CD / Build frontend assets (push) Successful in 1m3s
API CI/CD / Security audit (push) Failing after 1m0s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
This commit is contained in:
@@ -145,12 +145,22 @@ class SchoolYearClosureService
|
||||
'totals' => [
|
||||
'students_considered' => $promotions['summary']['total_students'],
|
||||
'students_blocked' => $promotions['summary']['hold'],
|
||||
'parents_with_balances' => $balances['summary']['parents_with_non_zero_balance'],
|
||||
'net_balance_to_transfer' => $balances['summary']['net_balance_to_transfer'],
|
||||
'parents_with_balances' => $balances['summary']['parents_with_positive_balance'],
|
||||
'parents_with_positive_balance' => $balances['summary']['parents_with_positive_balance'],
|
||||
'parents_with_credit_balance' => $balances['summary']['parents_with_credit_balance'],
|
||||
'total_positive_unpaid_balance' => $balances['summary']['total_positive_unpaid_balance'],
|
||||
'total_credit_balance' => $balances['summary']['total_credit_balance'],
|
||||
'net_balance_to_transfer' => $balances['summary']['net_balance_impact'],
|
||||
'net_balance_impact' => $balances['summary']['net_balance_impact'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function summaryByYearName(string $schoolYear): array
|
||||
{
|
||||
return $this->summary((int) $this->findByNameOrFail($schoolYear)->id);
|
||||
}
|
||||
|
||||
public function closingReport(int $id): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -200,12 +210,23 @@ class SchoolYearClosureService
|
||||
'parents_with_unpaid_balances' => (int) ($balanceCounts['transferred_parents'] ?? $transferTotals['parents_with_unpaid_balances']),
|
||||
'total_unpaid_balance_transferred' => round((float) ($balanceCounts['transferred_amount'] ?? $transferTotals['total_unpaid_balance_transferred']), 2),
|
||||
'parents_with_credit_balances' => (int) ($balanceCounts['credit_parents'] ?? $transferTotals['parents_with_credit_balances']),
|
||||
'total_credit_transferred' => round((float) ($balanceCounts['credit_amount'] ?? $transferTotals['total_credit_transferred']), 2),
|
||||
'total_credit_carried_or_reported' => round((float) ($balanceCounts['credit_amount'] ?? $transferTotals['total_credit_carried_or_reported']), 2),
|
||||
'total_credit_transferred' => round((float) ($balanceCounts['credit_amount'] ?? $transferTotals['total_credit_carried_or_reported']), 2),
|
||||
'net_balance_impact' => round(
|
||||
(float) ($balanceCounts['transferred_amount'] ?? $transferTotals['total_unpaid_balance_transferred'])
|
||||
- (float) ($balanceCounts['credit_amount'] ?? $transferTotals['total_credit_carried_or_reported']),
|
||||
2
|
||||
),
|
||||
],
|
||||
'warnings' => $warnings,
|
||||
];
|
||||
}
|
||||
|
||||
public function closingReportByYearName(string $schoolYear): array
|
||||
{
|
||||
return $this->closingReport((int) $this->findByNameOrFail($schoolYear)->id);
|
||||
}
|
||||
|
||||
public function validateClose(int $id, array $payload): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -270,6 +291,11 @@ class SchoolYearClosureService
|
||||
];
|
||||
}
|
||||
|
||||
public function validateCloseByYearName(string $schoolYear, array $payload): array
|
||||
{
|
||||
return $this->validateClose((int) $this->findByNameOrFail($schoolYear)->id, $payload);
|
||||
}
|
||||
|
||||
public function previewClose(int $id, array $payload): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -284,6 +310,11 @@ class SchoolYearClosureService
|
||||
];
|
||||
}
|
||||
|
||||
public function previewCloseByYearName(string $schoolYear, array $payload): array
|
||||
{
|
||||
return $this->previewClose((int) $this->findByNameOrFail($schoolYear)->id, $payload);
|
||||
}
|
||||
|
||||
public function close(int $id, array $payload, ?int $actorId): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -376,6 +407,11 @@ class SchoolYearClosureService
|
||||
});
|
||||
}
|
||||
|
||||
public function closeByYearName(string $schoolYear, array $payload, ?int $actorId): array
|
||||
{
|
||||
return $this->close((int) $this->findByNameOrFail($schoolYear)->id, $payload, $actorId);
|
||||
}
|
||||
|
||||
public function reopen(int $id, ?int $actorId): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -411,6 +447,11 @@ class SchoolYearClosureService
|
||||
});
|
||||
}
|
||||
|
||||
public function reopenByYearName(string $schoolYear, ?int $actorId): array
|
||||
{
|
||||
return $this->reopen((int) $this->findByNameOrFail($schoolYear)->id, $actorId);
|
||||
}
|
||||
|
||||
public function parentBalances(int $id, ?string $toSchoolYear = null): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -421,6 +462,11 @@ class SchoolYearClosureService
|
||||
);
|
||||
}
|
||||
|
||||
public function parentBalancesByYearName(string $schoolYear, ?string $toSchoolYear = null): array
|
||||
{
|
||||
return $this->parentBalances((int) $this->findByNameOrFail($schoolYear)->id, $toSchoolYear);
|
||||
}
|
||||
|
||||
public function promotionPreview(int $id, ?string $toSchoolYear = null): array
|
||||
{
|
||||
$year = $this->findOrFail($id);
|
||||
@@ -431,6 +477,11 @@ class SchoolYearClosureService
|
||||
);
|
||||
}
|
||||
|
||||
public function promotionPreviewByYearName(string $schoolYear, ?string $toSchoolYear = null): array
|
||||
{
|
||||
return $this->promotionPreview((int) $this->findByNameOrFail($schoolYear)->id, $toSchoolYear);
|
||||
}
|
||||
|
||||
public function findOrFail(int $id): SchoolYear
|
||||
{
|
||||
$this->resolver->ensureCurrentTracked();
|
||||
@@ -438,6 +489,13 @@ class SchoolYearClosureService
|
||||
return SchoolYear::query()->findOrFail($id);
|
||||
}
|
||||
|
||||
public function findByNameOrFail(string $schoolYear): SchoolYear
|
||||
{
|
||||
$this->resolver->ensureCurrentTracked();
|
||||
|
||||
return SchoolYear::query()->where('name', trim($schoolYear))->firstOrFail();
|
||||
}
|
||||
|
||||
private function validateNewYearPayload(SchoolYear $currentYear, array $newYear): array
|
||||
{
|
||||
$errors = [];
|
||||
@@ -683,85 +741,110 @@ class SchoolYearClosureService
|
||||
|
||||
private function buildParentBalanceRows(string $fromSchoolYear, string $toSchoolYear): array
|
||||
{
|
||||
$invoiceBalances = DB::table('invoices')
|
||||
->where('school_year', $fromSchoolYear)
|
||||
->whereNotIn(DB::raw("LOWER(COALESCE(status, ''))"), ['void', 'voided', 'cancelled', 'canceled'])
|
||||
->select('parent_id')
|
||||
->selectRaw('ROUND(SUM(COALESCE(balance, 0)), 2) as net_balance')
|
||||
->selectRaw('COUNT(CASE WHEN COALESCE(balance, 0) > 0.01 THEN 1 END) as unpaid_invoice_count')
|
||||
->groupBy('parent_id')
|
||||
->havingRaw('ABS(SUM(COALESCE(balance, 0))) > 0.01')
|
||||
->get();
|
||||
$invoices = $this->eligibleCarryoverInvoiceQuery($fromSchoolYear)
|
||||
->whereRaw('ABS(COALESCE(balance, 0)) > 0.01')
|
||||
->get(['id', 'parent_id', 'balance']);
|
||||
|
||||
$rows = [];
|
||||
$summary = [
|
||||
'parents_with_non_zero_balance' => 0,
|
||||
'parents_with_credit_balance' => 0,
|
||||
'existing_transfer_conflicts' => 0,
|
||||
'total_old_unpaid_balance' => 0.0,
|
||||
'total_credit_balance' => 0.0,
|
||||
'net_balance_to_transfer' => 0.0,
|
||||
];
|
||||
|
||||
foreach ($invoiceBalances as $balanceRow) {
|
||||
$parentId = (int) ($balanceRow->parent_id ?? 0);
|
||||
$balancesByParent = [];
|
||||
foreach ($invoices as $invoice) {
|
||||
$parentId = (int) ($invoice->parent_id ?? 0);
|
||||
if ($parentId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$net = round((float) ($balanceRow->net_balance ?? 0), 2);
|
||||
if (abs($net) < 0.01) {
|
||||
$balance = round((float) ($invoice->balance ?? 0), 2);
|
||||
$balancesByParent[$parentId] ??= [
|
||||
'positive_unpaid_balance' => 0.0,
|
||||
'credit_balance' => 0.0,
|
||||
'positive_invoice_ids' => [],
|
||||
'credit_invoice_ids' => [],
|
||||
];
|
||||
|
||||
if ($balance > 0.01) {
|
||||
$balancesByParent[$parentId]['positive_unpaid_balance'] += $balance;
|
||||
$balancesByParent[$parentId]['positive_invoice_ids'][] = (int) $invoice->id;
|
||||
} elseif ($balance < -0.01) {
|
||||
$balancesByParent[$parentId]['credit_balance'] += abs($balance);
|
||||
$balancesByParent[$parentId]['credit_invoice_ids'][] = (int) $invoice->id;
|
||||
}
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$summary = [
|
||||
'parents_with_positive_balance' => 0,
|
||||
'parents_with_credit_balance' => 0,
|
||||
'parents_with_net_non_zero_balance' => 0,
|
||||
'existing_transfer_conflicts' => 0,
|
||||
'total_positive_unpaid_balance' => 0.0,
|
||||
'total_credit_balance' => 0.0,
|
||||
'net_balance_impact' => 0.0,
|
||||
// Deprecated aliases retained for older UI/tests during the migration.
|
||||
'parents_with_non_zero_balance' => 0,
|
||||
'total_old_unpaid_balance' => 0.0,
|
||||
'net_balance_to_transfer' => 0.0,
|
||||
];
|
||||
|
||||
foreach ($balancesByParent as $parentId => $balanceRow) {
|
||||
$positive = round((float) $balanceRow['positive_unpaid_balance'], 2);
|
||||
$credit = round((float) $balanceRow['credit_balance'], 2);
|
||||
$net = round($positive - $credit, 2);
|
||||
|
||||
if ($positive < 0.01 && $credit < 0.01) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = ParentBalanceTransfer::query()
|
||||
->where('parent_id', $parentId)
|
||||
->where('from_school_year', $fromSchoolYear)
|
||||
->where('to_school_year', $toSchoolYear)
|
||||
->first();
|
||||
|
||||
$sourceInvoiceIds = DB::table('invoices')
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $fromSchoolYear)
|
||||
->whereRaw('ABS(COALESCE(balance, 0)) > 0.01')
|
||||
->whereNotIn(DB::raw("LOWER(COALESCE(status, ''))"), ['void', 'voided', 'cancelled', 'canceled'])
|
||||
->pluck('id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->all();
|
||||
$existing = $positive > 0.01
|
||||
? ParentBalanceTransfer::query()
|
||||
->where('parent_id', $parentId)
|
||||
->where('from_school_year', $fromSchoolYear)
|
||||
->where('to_school_year', $toSchoolYear)
|
||||
->first()
|
||||
: null;
|
||||
|
||||
$row = [
|
||||
'parent_id' => $parentId,
|
||||
'parent_name' => $this->resolveParentName($parentId),
|
||||
'student_names' => $this->resolveStudentNames($parentId, $fromSchoolYear),
|
||||
'unpaid_invoice_count' => (int) ($balanceRow->unpaid_invoice_count ?? 0),
|
||||
'old_unpaid_balance' => $net > 0 ? $net : 0.0,
|
||||
'credit_balance' => $net < 0 ? abs($net) : 0.0,
|
||||
'unpaid_invoice_count' => count($balanceRow['positive_invoice_ids']),
|
||||
'positive_unpaid_balance' => $positive,
|
||||
'old_unpaid_balance' => $positive,
|
||||
'credit_balance' => $credit,
|
||||
'net_balance' => $net,
|
||||
'amount_to_transfer' => $net,
|
||||
'transfer_status' => $existing?->status ?? ($net > 0 ? 'pending' : 'credit_pending'),
|
||||
'amount_to_transfer' => $positive,
|
||||
'transfer_status' => $existing?->status ?? ($positive > 0.01 ? 'pending' : 'credit_reported'),
|
||||
'existing_transfer_id' => $existing?->id,
|
||||
'source_invoice_ids' => $sourceInvoiceIds,
|
||||
'existing_transfer_conflict' => $existing !== null,
|
||||
'positive_invoice_ids' => $balanceRow['positive_invoice_ids'],
|
||||
'credit_invoice_ids' => $balanceRow['credit_invoice_ids'],
|
||||
'source_invoice_ids' => $balanceRow['positive_invoice_ids'],
|
||||
];
|
||||
|
||||
$rows[] = $row;
|
||||
|
||||
$summary['parents_with_non_zero_balance']++;
|
||||
$summary['net_balance_to_transfer'] += $net;
|
||||
$summary['total_old_unpaid_balance'] += $row['old_unpaid_balance'];
|
||||
$summary['total_credit_balance'] += $row['credit_balance'];
|
||||
if ($row['credit_balance'] > 0) {
|
||||
$summary['parents_with_credit_balance']++;
|
||||
if ($positive > 0.01) {
|
||||
$summary['parents_with_positive_balance']++;
|
||||
$summary['total_positive_unpaid_balance'] += $positive;
|
||||
}
|
||||
if ($row['existing_transfer_id']) {
|
||||
if ($credit > 0.01) {
|
||||
$summary['parents_with_credit_balance']++;
|
||||
$summary['total_credit_balance'] += $credit;
|
||||
}
|
||||
if (abs($net) > 0.01) {
|
||||
$summary['parents_with_net_non_zero_balance']++;
|
||||
}
|
||||
if ($row['existing_transfer_conflict']) {
|
||||
$summary['existing_transfer_conflicts']++;
|
||||
}
|
||||
}
|
||||
|
||||
usort($rows, static fn (array $a, array $b) => $b['amount_to_transfer'] <=> $a['amount_to_transfer']);
|
||||
|
||||
$summary['net_balance_to_transfer'] = round($summary['net_balance_to_transfer'], 2);
|
||||
$summary['total_old_unpaid_balance'] = round($summary['total_old_unpaid_balance'], 2);
|
||||
$summary['total_positive_unpaid_balance'] = round($summary['total_positive_unpaid_balance'], 2);
|
||||
$summary['total_credit_balance'] = round($summary['total_credit_balance'], 2);
|
||||
$summary['net_balance_impact'] = round($summary['total_positive_unpaid_balance'] - $summary['total_credit_balance'], 2);
|
||||
$summary['parents_with_non_zero_balance'] = $summary['parents_with_net_non_zero_balance'];
|
||||
$summary['total_old_unpaid_balance'] = $summary['total_positive_unpaid_balance'];
|
||||
$summary['net_balance_to_transfer'] = $summary['net_balance_impact'];
|
||||
|
||||
return [
|
||||
'from_school_year' => $fromSchoolYear,
|
||||
@@ -771,6 +854,13 @@ class SchoolYearClosureService
|
||||
];
|
||||
}
|
||||
|
||||
private function eligibleCarryoverInvoiceQuery(string $schoolYear)
|
||||
{
|
||||
return DB::table('invoices')
|
||||
->where('school_year', $schoolYear)
|
||||
->whereNotIn(DB::raw("LOWER(COALESCE(status, ''))"), ['void', 'voided', 'cancelled', 'canceled', 'draft', 'pending']);
|
||||
}
|
||||
|
||||
private function resolveTargetSection(?int $targetLevelId, string $schoolYear): array
|
||||
{
|
||||
if ($targetLevelId === null || $targetLevelId <= 0) {
|
||||
@@ -905,7 +995,12 @@ class SchoolYearClosureService
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$amount = round((float) $row['amount_to_transfer'], 2);
|
||||
if (abs($amount) < 0.01) {
|
||||
$credit = round((float) ($row['credit_balance'] ?? 0), 2);
|
||||
if ($amount < 0.01) {
|
||||
if ($credit > 0.01) {
|
||||
$counts['credit_parents']++;
|
||||
$counts['credit_amount'] += $credit;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -924,36 +1019,51 @@ class SchoolYearClosureService
|
||||
));
|
||||
}
|
||||
|
||||
ParentAccount::query()->updateOrCreate(
|
||||
['parent_id' => $row['parent_id'], 'school_year' => $fromSchoolYear],
|
||||
['opening_balance' => 0, 'current_balance' => $amount]
|
||||
);
|
||||
|
||||
$newAccount = ParentAccount::query()->firstOrCreate(
|
||||
['parent_id' => $row['parent_id'], 'school_year' => $toSchoolYear],
|
||||
['opening_balance' => 0, 'current_balance' => 0]
|
||||
);
|
||||
if (Schema::hasColumn('parent_accounts', 'school_year_id') && $newAccount->school_year_id === null) {
|
||||
$newAccount->school_year_id = $this->schoolYearIdForName($toSchoolYear);
|
||||
}
|
||||
$newAccount->opening_balance = round((float) $newAccount->opening_balance + $amount, 2);
|
||||
$newAccount->current_balance = round((float) $newAccount->current_balance + $amount, 2);
|
||||
$newAccount->save();
|
||||
|
||||
$transfer = ParentBalanceTransfer::query()->create([
|
||||
$transferPayload = [
|
||||
'parent_id' => $row['parent_id'],
|
||||
'from_school_year' => $fromSchoolYear,
|
||||
'to_school_year' => $toSchoolYear,
|
||||
'amount' => $amount,
|
||||
'status' => 'transferred',
|
||||
'source_summary_json' => [
|
||||
'source_invoice_ids' => $row['source_invoice_ids'],
|
||||
'old_unpaid_balance' => $row['old_unpaid_balance'],
|
||||
'credit_balance' => $row['credit_balance'],
|
||||
'source_invoice_ids' => $row['positive_invoice_ids'] ?? $row['source_invoice_ids'] ?? [],
|
||||
'positive_invoice_ids' => $row['positive_invoice_ids'] ?? $row['source_invoice_ids'] ?? [],
|
||||
'credit_invoice_ids' => $row['credit_invoice_ids'] ?? [],
|
||||
'old_unpaid_balance' => $row['old_unpaid_balance'] ?? $amount,
|
||||
'positive_unpaid_balance' => $row['positive_unpaid_balance'] ?? $amount,
|
||||
'credit_balance' => $credit,
|
||||
'net_balance' => $row['net_balance'] ?? ($amount - $credit),
|
||||
],
|
||||
'created_by' => $actorId,
|
||||
]);
|
||||
];
|
||||
if (Schema::hasColumn('parent_balance_transfers', 'source_summary')) {
|
||||
$transferPayload['source_summary'] = $transferPayload['source_summary_json'];
|
||||
}
|
||||
$fromYearId = $this->schoolYearIdForName($fromSchoolYear);
|
||||
$toYearId = $this->schoolYearIdForName($toSchoolYear);
|
||||
if (Schema::hasColumn('parent_balance_transfers', 'from_school_year_id')) {
|
||||
$transferPayload['from_school_year_id'] = $fromYearId;
|
||||
}
|
||||
if (Schema::hasColumn('parent_balance_transfers', 'to_school_year_id')) {
|
||||
$transferPayload['to_school_year_id'] = $toYearId;
|
||||
}
|
||||
|
||||
$transfer = ParentBalanceTransfer::query()->create($transferPayload);
|
||||
|
||||
$newInvoiceId = null;
|
||||
if ($amount > 0) {
|
||||
$newInvoiceId = DB::table('invoices')->insertGetId([
|
||||
$invoicePayload = [
|
||||
'parent_id' => $row['parent_id'],
|
||||
'invoice_number' => sprintf('OB-%s-%d', str_replace('-', '', $toSchoolYear), $transfer->id),
|
||||
'total_amount' => $amount,
|
||||
@@ -970,15 +1080,23 @@ class SchoolYearClosureService
|
||||
'updated_by' => $actorId,
|
||||
'semester' => Configuration::getConfig('semester'),
|
||||
'balance_transfer_id' => $transfer->id,
|
||||
]);
|
||||
];
|
||||
if (Schema::hasColumn('invoices', 'invoice_type')) {
|
||||
$invoicePayload['invoice_type'] = 'opening_balance';
|
||||
}
|
||||
if (Schema::hasColumn('invoices', 'school_year_id')) {
|
||||
$invoicePayload['school_year_id'] = $toYearId;
|
||||
}
|
||||
$newInvoiceId = DB::table('invoices')->insertGetId($invoicePayload);
|
||||
$transfer->new_invoice_id = $newInvoiceId;
|
||||
$transfer->save();
|
||||
|
||||
$counts['transferred_parents']++;
|
||||
$counts['transferred_amount'] += $amount;
|
||||
} else {
|
||||
$counts['credit_parents']++;
|
||||
$counts['credit_amount'] += abs($amount);
|
||||
if ($credit > 0.01) {
|
||||
$counts['credit_parents']++;
|
||||
$counts['credit_amount'] += $credit;
|
||||
}
|
||||
}
|
||||
|
||||
$this->logAudit(
|
||||
@@ -1018,6 +1136,17 @@ class SchoolYearClosureService
|
||||
->count();
|
||||
}
|
||||
|
||||
private function schoolYearIdForName(string $schoolYear): ?int
|
||||
{
|
||||
if (! Schema::hasTable('school_years')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$id = SchoolYear::query()->where('name', $schoolYear)->value('id');
|
||||
|
||||
return $id ? (int) $id : null;
|
||||
}
|
||||
|
||||
private function countFinancialInconsistencies(string $schoolYear): int
|
||||
{
|
||||
return DB::table('invoices')
|
||||
@@ -1114,7 +1243,9 @@ class SchoolYearClosureService
|
||||
'parents_with_unpaid_balances' => 0,
|
||||
'total_unpaid_balance_transferred' => 0.0,
|
||||
'parents_with_credit_balances' => 0,
|
||||
'total_credit_carried_or_reported' => 0.0,
|
||||
'total_credit_transferred' => 0.0,
|
||||
'net_balance_impact' => 0.0,
|
||||
];
|
||||
|
||||
if (! Schema::hasTable('parent_balance_transfers')) {
|
||||
@@ -1124,25 +1255,49 @@ class SchoolYearClosureService
|
||||
$rows = DB::table('parent_balance_transfers')
|
||||
->where('from_school_year', $fromSchoolYear)
|
||||
->when($toSchoolYear !== null && trim($toSchoolYear) !== '', fn ($query) => $query->where('to_school_year', $toSchoolYear))
|
||||
->get(['amount']);
|
||||
->get(['amount', 'source_summary_json', 'source_summary']);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$amount = round((float) ($row->amount ?? 0), 2);
|
||||
if ($amount > 0.01) {
|
||||
$totals['parents_with_unpaid_balances']++;
|
||||
$totals['total_unpaid_balance_transferred'] += $amount;
|
||||
} elseif ($amount < -0.01) {
|
||||
}
|
||||
|
||||
$sourceSummary = $this->decodeTransferSourceSummary($row);
|
||||
$credit = round((float) ($sourceSummary['credit_balance'] ?? 0), 2);
|
||||
if ($credit > 0.01) {
|
||||
$totals['parents_with_credit_balances']++;
|
||||
$totals['total_credit_transferred'] += abs($amount);
|
||||
$totals['total_credit_carried_or_reported'] += $credit;
|
||||
}
|
||||
}
|
||||
|
||||
$totals['total_unpaid_balance_transferred'] = round($totals['total_unpaid_balance_transferred'], 2);
|
||||
$totals['total_credit_transferred'] = round($totals['total_credit_transferred'], 2);
|
||||
$totals['total_credit_carried_or_reported'] = round($totals['total_credit_carried_or_reported'], 2);
|
||||
$totals['total_credit_transferred'] = $totals['total_credit_carried_or_reported'];
|
||||
$totals['net_balance_impact'] = round(
|
||||
$totals['total_unpaid_balance_transferred'] - $totals['total_credit_carried_or_reported'],
|
||||
2
|
||||
);
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
private function decodeTransferSourceSummary(object $row): array
|
||||
{
|
||||
$raw = $row->source_summary_json ?? $row->source_summary ?? null;
|
||||
if (is_array($raw)) {
|
||||
return $raw;
|
||||
}
|
||||
if (! is_string($raw) || trim($raw) === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
private function resolveUserName(int $userId): string|int|null
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
@@ -1196,6 +1351,11 @@ class SchoolYearClosureService
|
||||
return $this->presentSchoolYear($year->fresh());
|
||||
}
|
||||
|
||||
public function archiveByYearName(string $schoolYear, ?int $actorId): array
|
||||
{
|
||||
return $this->archive((int) $this->findByNameOrFail($schoolYear)->id, $actorId);
|
||||
}
|
||||
|
||||
private function presentSchoolYear(?SchoolYear $year): ?array
|
||||
{
|
||||
if (! $year) {
|
||||
|
||||
Reference in New Issue
Block a user