104 lines
4.4 KiB
PHP
104 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace App\Commands;
|
|
|
|
use CodeIgniter\CLI\BaseCommand;
|
|
use CodeIgniter\CLI\CLI;
|
|
|
|
final class RepairSchoolYearCarryForward extends BaseCommand
|
|
{
|
|
protected $group = 'School Year';
|
|
protected $name = 'school-year:repair-carry-forward';
|
|
protected $description = 'Find or repair family balances omitted from an executed school-year closing batch.';
|
|
protected $usage = 'php spark school-year:repair-carry-forward --source-year-id=1 [--actor-id=1 --commit]';
|
|
protected $options = [
|
|
'--source-year-id' => 'Required source school-year record ID.',
|
|
'--actor-id' => 'Administrator user ID recorded in the repair audit log.',
|
|
'--commit' => 'Create missing closing items and target-year opening-balance invoices.',
|
|
'--json' => 'Print machine-readable output.',
|
|
];
|
|
|
|
public function run(array $params)
|
|
{
|
|
$sourceYearId = (int) (CLI::getOption('source-year-id') ?? 0);
|
|
if ($sourceYearId <= 0) {
|
|
CLI::error('--source-year-id must be a positive integer.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$closing = service('schoolYearClosing');
|
|
$batch = $closing->latestBatch($sourceYearId);
|
|
if ($batch === null) {
|
|
throw new \RuntimeException('No closing batch exists for the source school year.');
|
|
}
|
|
|
|
$preview = $closing->preview($sourceYearId, (int) ($batch['target_school_year_id'] ?? 0));
|
|
$db = \Config\Database::connect();
|
|
$existingRows = $db->table('school_year_closing_items')
|
|
->select('family_id')
|
|
->where('closing_batch_id', (int) $batch['id'])
|
|
->get()
|
|
->getResultArray();
|
|
$existing = array_fill_keys(array_map(
|
|
static fn (array $row): int => (int) ($row['family_id'] ?? 0),
|
|
$existingRows
|
|
), true);
|
|
$missing = array_values(array_filter(
|
|
$preview['carry_forward'] ?? [],
|
|
static fn (array $row): bool => ! isset($existing[(int) ($row['family_id'] ?? 0)])
|
|
));
|
|
|
|
$result = [
|
|
'mode' => CLI::getOption('commit') !== null ? 'commit' : 'dry-run',
|
|
'closing_batch_id' => (int) $batch['id'],
|
|
'missing_count' => count($missing),
|
|
'missing_amount' => round(array_sum(array_map(
|
|
static fn (array $row): float => (float) ($row['carry_forward_amount'] ?? 0),
|
|
$missing
|
|
)), 2),
|
|
'missing_items' => array_map(static fn (array $row): array => [
|
|
'family_id' => (int) ($row['family_id'] ?? 0),
|
|
'parent' => (string) ($row['parent'] ?? ''),
|
|
'carry_forward_amount' => round((float) ($row['carry_forward_amount'] ?? 0), 2),
|
|
], $missing),
|
|
];
|
|
|
|
if (CLI::getOption('commit') !== null) {
|
|
$actorId = (int) (CLI::getOption('actor-id') ?? 0);
|
|
if ($actorId <= 0) {
|
|
throw new \InvalidArgumentException('--actor-id must be a positive administrator user ID when using --commit.');
|
|
}
|
|
$result['repair'] = $closing->repairMissingCarryForward($sourceYearId, $actorId);
|
|
}
|
|
|
|
if (CLI::getOption('json') !== null) {
|
|
CLI::write(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
return;
|
|
}
|
|
|
|
CLI::write(sprintf(
|
|
'%s: %d missing family balance(s), $%0.2f total.',
|
|
strtoupper((string) $result['mode']),
|
|
(int) $result['missing_count'],
|
|
(float) $result['missing_amount']
|
|
));
|
|
foreach ($result['missing_items'] as $item) {
|
|
CLI::write(sprintf(
|
|
'Family #%d (%s): $%0.2f',
|
|
(int) $item['family_id'],
|
|
(string) $item['parent'],
|
|
(float) $item['carry_forward_amount']
|
|
));
|
|
}
|
|
if (isset($result['repair'])) {
|
|
CLI::write('Repair completed and audited.', 'green');
|
|
} elseif ($missing !== []) {
|
|
CLI::write('Run again with --actor-id=<admin id> --commit to apply the repair.', 'yellow');
|
|
}
|
|
} catch (\Throwable $e) {
|
|
CLI::error($e->getMessage());
|
|
}
|
|
}
|
|
}
|