fix invoice and enrollment fees
This commit is contained in:
@@ -3,24 +3,21 @@
|
||||
namespace App\Libraries;
|
||||
|
||||
use App\Models\AdditionalChargeModel;
|
||||
use App\Models\InvoiceLineModel;
|
||||
|
||||
class InvoiceAdjustmentService
|
||||
{
|
||||
private \CodeIgniter\Database\BaseConnection $db;
|
||||
private AdditionalChargeModel $additionalChargeModel;
|
||||
private InvoiceLineModel $invoiceLineModel;
|
||||
private InvoiceLedgerService $invoiceLedgerService;
|
||||
|
||||
public function __construct(
|
||||
?\CodeIgniter\Database\BaseConnection $db = null,
|
||||
?AdditionalChargeModel $additionalChargeModel = null,
|
||||
?InvoiceLineModel $invoiceLineModel = null,
|
||||
$invoiceLineModel = null,
|
||||
?InvoiceLedgerService $invoiceLedgerService = null
|
||||
) {
|
||||
$this->db = $db ?? db_connect();
|
||||
$this->additionalChargeModel = $additionalChargeModel ?? new AdditionalChargeModel();
|
||||
$this->invoiceLineModel = $invoiceLineModel ?? new InvoiceLineModel();
|
||||
$this->invoiceLedgerService = $invoiceLedgerService ?? new InvoiceLedgerService();
|
||||
}
|
||||
|
||||
@@ -43,47 +40,14 @@ class InvoiceAdjustmentService
|
||||
throw new \RuntimeException('Zero amount additional charges cannot be applied.');
|
||||
}
|
||||
|
||||
$activeSourceKey = $this->activeSourceKey($chargeId);
|
||||
$existing = $this->db->table('invoice_lines')
|
||||
->where('active_source_key', $activeSourceKey)
|
||||
->where('voided_at IS NULL', null, false)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if ($existing) {
|
||||
throw new \RuntimeException('Additional charge already has an active invoice line.');
|
||||
}
|
||||
|
||||
$now = utc_now();
|
||||
$lineId = $this->invoiceLineModel->insert([
|
||||
'invoice_id' => $invoiceId,
|
||||
'school_year' => (string)$invoice['school_year'],
|
||||
'line_type' => $amountCents > 0 ? 'additional_charge' : 'additional_deduction',
|
||||
'source_type' => 'additional_charge',
|
||||
'source_id' => $chargeId,
|
||||
'active_source_key' => $activeSourceKey,
|
||||
'description' => $this->chargeDescription($charge),
|
||||
'quantity' => '1.00',
|
||||
'unit_amount_cents' => $amountCents,
|
||||
'line_amount_cents' => $amountCents,
|
||||
'discount_eligible' => 0,
|
||||
'calculation_version' => 'invoice_adjustment_v1',
|
||||
'metadata_json' => json_encode([
|
||||
'charge_type' => (string)($charge['charge_type'] ?? ''),
|
||||
'applied_by' => $actorId,
|
||||
], JSON_UNESCAPED_SLASHES),
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
'voided_at' => null,
|
||||
]);
|
||||
$this->requireWrite($lineId, 'INVOICE_ADJUSTMENT_LINE_INSERT_FAILED', $this->invoiceLineModel);
|
||||
|
||||
$this->requireWrite($this->additionalChargeModel->update($chargeId, [
|
||||
'status' => FinancialStatus::ADDITIONAL_CHARGE_APPLIED,
|
||||
'invoice_id' => $invoiceId,
|
||||
'parent_id' => (int)$invoice['parent_id'],
|
||||
'school_year' => (string)$invoice['school_year'],
|
||||
'semester' => (string)($invoice['semester'] ?? ''),
|
||||
'applied_invoice_line_id' => (int)$lineId,
|
||||
'applied_invoice_line_id' => null,
|
||||
'applied_by' => $actorId > 0 ? $actorId : null,
|
||||
'applied_at' => $now,
|
||||
]), 'ADDITIONAL_CHARGE_APPLY_UPDATE_FAILED', $this->additionalChargeModel);
|
||||
@@ -92,7 +56,7 @@ class InvoiceAdjustmentService
|
||||
$this->requireTransactionStatus();
|
||||
$this->db->transCommit();
|
||||
|
||||
return new InvoiceLedgerResult($invoiceId, (int)$lineId, $ledger);
|
||||
return new InvoiceLedgerResult($invoiceId, 0, $ledger);
|
||||
} catch (\Throwable $e) {
|
||||
$this->db->transRollback();
|
||||
throw $e;
|
||||
@@ -118,50 +82,11 @@ class InvoiceAdjustmentService
|
||||
$invoice = $this->lockInvoice($invoiceId);
|
||||
$this->assertInvoiceAcceptsAdjustment($invoice);
|
||||
|
||||
$originalLineId = (int)($charge['applied_invoice_line_id'] ?? 0);
|
||||
$originalLine = $originalLineId > 0
|
||||
? $this->db->query('SELECT * FROM invoice_lines WHERE id = ? FOR UPDATE', [$originalLineId])->getRowArray()
|
||||
: null;
|
||||
if (!$originalLine) {
|
||||
$originalLine = $this->db->query(
|
||||
'SELECT * FROM invoice_lines WHERE source_type = ? AND source_id = ? AND voided_at IS NULL ORDER BY id ASC LIMIT 1 FOR UPDATE',
|
||||
['additional_charge', $chargeId]
|
||||
)->getRowArray();
|
||||
}
|
||||
if (!$originalLine) {
|
||||
throw new \RuntimeException('Original invoice line not found.');
|
||||
}
|
||||
|
||||
$reverseAmountCents = -1 * (int)($originalLine['line_amount_cents'] ?? 0);
|
||||
if ($reverseAmountCents === 0) {
|
||||
if ($this->signedChargeAmountCents($charge) === 0) {
|
||||
throw new \RuntimeException('Zero amount additional charges cannot be reversed.');
|
||||
}
|
||||
|
||||
$now = utc_now();
|
||||
$lineId = $this->invoiceLineModel->insert([
|
||||
'invoice_id' => $invoiceId,
|
||||
'school_year' => (string)$invoice['school_year'],
|
||||
'line_type' => $reverseAmountCents > 0 ? 'additional_charge_reversal' : 'additional_deduction_reversal',
|
||||
'source_type' => 'additional_charge_reversal',
|
||||
'source_id' => $chargeId,
|
||||
'active_source_key' => null,
|
||||
'description' => 'Reversal: ' . $this->chargeDescription($charge),
|
||||
'quantity' => '1.00',
|
||||
'unit_amount_cents' => $reverseAmountCents,
|
||||
'line_amount_cents' => $reverseAmountCents,
|
||||
'discount_eligible' => 0,
|
||||
'calculation_version' => 'invoice_adjustment_v1',
|
||||
'metadata_json' => json_encode([
|
||||
'original_invoice_line_id' => (int)($originalLine['id'] ?? 0),
|
||||
'reason' => $reason,
|
||||
'reversed_by' => $actorId,
|
||||
], JSON_UNESCAPED_SLASHES),
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
'voided_at' => null,
|
||||
]);
|
||||
$this->requireWrite($lineId, 'INVOICE_ADJUSTMENT_REVERSAL_LINE_INSERT_FAILED', $this->invoiceLineModel);
|
||||
|
||||
$this->requireWrite($this->additionalChargeModel->update($chargeId, [
|
||||
'status' => 'reversed',
|
||||
'voided_by' => $actorId > 0 ? $actorId : null,
|
||||
@@ -173,7 +98,7 @@ class InvoiceAdjustmentService
|
||||
$this->requireTransactionStatus();
|
||||
$this->db->transCommit();
|
||||
|
||||
return new InvoiceLedgerResult($invoiceId, (int)$lineId, $ledger);
|
||||
return new InvoiceLedgerResult($invoiceId, 0, $ledger);
|
||||
} catch (\Throwable $e) {
|
||||
$this->db->transRollback();
|
||||
throw $e;
|
||||
@@ -226,11 +151,6 @@ class InvoiceAdjustmentService
|
||||
return (string)($charge['charge_type'] ?? 'add') === 'deduct' ? -1 * $amount : $amount;
|
||||
}
|
||||
|
||||
private function activeSourceKey(int $chargeId): string
|
||||
{
|
||||
return 'additional_charge:' . $chargeId;
|
||||
}
|
||||
|
||||
private function chargeDescription(array $charge): string
|
||||
{
|
||||
$title = trim((string)($charge['title'] ?? 'Additional charge'));
|
||||
|
||||
Reference in New Issue
Block a user