fix financial issues
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Libraries\InvoiceLedgerService;
|
||||
use App\Models\InvoiceModel;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
class RecalculateInvoices extends BaseCommand
|
||||
{
|
||||
protected $group = 'Financial';
|
||||
protected $name = 'financial:recalculate-invoices';
|
||||
protected $description = 'Audits invoice totals against the centralized ledger and optionally writes the corrected values.';
|
||||
protected $usage = 'financial:recalculate-invoices [--commit] [--invoice-id=123] [--parent-id=456] [--school-year=2025-2026] [--semester=Fall]';
|
||||
protected $options = [
|
||||
'--commit' => 'Persist recalculated totals. Without this flag the command runs in dry-run mode.',
|
||||
'--invoice-id' => 'Only process a single invoice ID.',
|
||||
'--parent-id' => 'Only process invoices for one parent.',
|
||||
'--school-year' => 'Only process invoices for the specified school year.',
|
||||
'--semester' => 'Only process invoices for the specified semester.',
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
$options = $this->parseOptions($params);
|
||||
$invoiceModel = new InvoiceModel();
|
||||
$ledgerService = new InvoiceLedgerService();
|
||||
$invoiceIds = $this->loadInvoiceIds($invoiceModel, $options);
|
||||
|
||||
if ($invoiceIds === []) {
|
||||
CLI::write('No invoices matched the provided filters.', 'yellow');
|
||||
return;
|
||||
}
|
||||
|
||||
$commit = !empty($options['commit']);
|
||||
CLI::write(($commit ? 'Applying' : 'Dry-run auditing') . ' ' . count($invoiceIds) . ' invoice(s)...', 'yellow');
|
||||
|
||||
$changed = 0;
|
||||
$unchanged = 0;
|
||||
$errors = 0;
|
||||
|
||||
foreach ($invoiceIds as $invoiceId) {
|
||||
$invoice = $invoiceModel->find($invoiceId);
|
||||
if (!$invoice) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$calculation = $ledgerService->calculateInvoice($invoiceId);
|
||||
$diffs = $this->diffInvoice($invoice, $calculation);
|
||||
|
||||
if ($diffs === []) {
|
||||
$unchanged++;
|
||||
CLI::write('Invoice #' . $invoiceId . ' unchanged.', 'green');
|
||||
continue;
|
||||
}
|
||||
|
||||
$changed++;
|
||||
CLI::write('Invoice #' . $invoiceId . ' requires recalculation:', 'light_yellow');
|
||||
foreach ($diffs as $label => $values) {
|
||||
CLI::write(sprintf(
|
||||
' %s: %s -> %s',
|
||||
$label,
|
||||
(string) $values['from'],
|
||||
(string) $values['to']
|
||||
));
|
||||
}
|
||||
|
||||
if ($commit) {
|
||||
$ledgerService->recalculateInvoice($invoiceId);
|
||||
CLI::write(' saved', 'blue');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$errors++;
|
||||
CLI::error('Invoice #' . $invoiceId . ' failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write('Processed: ' . count($invoiceIds), 'white');
|
||||
CLI::write('Changed: ' . $changed, $changed > 0 ? 'yellow' : 'white');
|
||||
CLI::write('Unchanged: ' . $unchanged, 'green');
|
||||
CLI::write('Errors: ' . $errors, $errors > 0 ? 'red' : 'white');
|
||||
|
||||
if (!$commit) {
|
||||
CLI::write('Dry-run complete. Re-run with --commit to persist corrections.', 'light_blue');
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseOptions(array $params): array
|
||||
{
|
||||
$options = [
|
||||
'commit' => false,
|
||||
'invoice-id' => null,
|
||||
'parent-id' => null,
|
||||
'school-year' => null,
|
||||
'semester' => null,
|
||||
];
|
||||
|
||||
foreach ($params as $param) {
|
||||
$value = trim((string) $param);
|
||||
if ($value === '--commit') {
|
||||
$options['commit'] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!str_starts_with($value, '--') || !str_contains($value, '=')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[$key, $raw] = explode('=', substr($value, 2), 2);
|
||||
if (array_key_exists($key, $options)) {
|
||||
$options[$key] = $raw;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function loadInvoiceIds(InvoiceModel $invoiceModel, array $options): array
|
||||
{
|
||||
$builder = $invoiceModel->select('id')->orderBy('id', 'ASC');
|
||||
|
||||
if (!empty($options['invoice-id'])) {
|
||||
$builder->where('id', (int) $options['invoice-id']);
|
||||
}
|
||||
|
||||
if (!empty($options['parent-id'])) {
|
||||
$builder->where('parent_id', (int) $options['parent-id']);
|
||||
}
|
||||
|
||||
if (!empty($options['school-year'])) {
|
||||
$builder->where('school_year', (string) $options['school-year']);
|
||||
}
|
||||
|
||||
if (!empty($options['semester'])) {
|
||||
$builder->where('semester', (string) $options['semester']);
|
||||
}
|
||||
|
||||
return array_map(
|
||||
static fn (array $row): int => (int) ($row['id'] ?? 0),
|
||||
$builder->findAll()
|
||||
);
|
||||
}
|
||||
|
||||
protected function diffInvoice(array $invoice, array $calculation): array
|
||||
{
|
||||
$fields = [
|
||||
'total_amount' => 'total_amount',
|
||||
'paid_amount' => 'paid_amount',
|
||||
'balance' => 'balance',
|
||||
'status' => 'status',
|
||||
'has_discount' => 'has_discount',
|
||||
];
|
||||
$diffs = [];
|
||||
|
||||
foreach ($fields as $invoiceKey => $calcKey) {
|
||||
$current = (string) ($invoice[$invoiceKey] ?? '');
|
||||
$recalculated = (string) ($calculation[$calcKey] ?? '');
|
||||
if ($current !== $recalculated) {
|
||||
$diffs[$invoiceKey] = [
|
||||
'from' => $current,
|
||||
'to' => $recalculated,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $diffs;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user