diff --git a/app/Controllers/View/RefundController.php b/app/Controllers/View/RefundController.php index e85ff80..2f28175 100644 --- a/app/Controllers/View/RefundController.php +++ b/app/Controllers/View/RefundController.php @@ -661,6 +661,7 @@ class RefundController extends BaseController $payoutId = $this->refundPayoutModel->insert([ 'refund_id' => $refundId, + 'school_year' => (string)($lockedRefund['school_year'] ?? ''), 'amount_cents' => $newPaidCents, 'currency' => (string)($lockedRefund['currency'] ?? 'USD') ?: 'USD', 'payout_type' => 'cash_out', @@ -830,6 +831,7 @@ class RefundController extends BaseController $reversalId = $this->refundPayoutModel->insert([ 'refund_id' => $refundId, + 'school_year' => (string)($refund['school_year'] ?? ''), 'amount_cents' => $reverseCents, 'currency' => (string)($payout['currency'] ?? $refund['currency'] ?? 'USD') ?: 'USD', 'payout_type' => 'reversal', diff --git a/app/Database/Migrations/2026-07-19-000100_CreateInvoiceLines.php b/app/Database/Migrations/2026-07-19-000100_CreateInvoiceLines.php index f44c764..001fbeb 100644 --- a/app/Database/Migrations/2026-07-19-000100_CreateInvoiceLines.php +++ b/app/Database/Migrations/2026-07-19-000100_CreateInvoiceLines.php @@ -22,6 +22,11 @@ class CreateInvoiceLines extends Migration 'unsigned' => true, 'null' => false, ], + 'school_year' => [ + 'type' => 'VARCHAR', + 'constraint' => 9, + 'null' => false, + ], 'line_type' => [ 'type' => 'VARCHAR', 'constraint' => 50, @@ -101,6 +106,7 @@ class CreateInvoiceLines extends Migration $this->forge->createTable('invoice_lines', true); } + $this->ensureSchoolYearColumn(); $this->backfillLegacyInvoiceLines(); } @@ -125,7 +131,7 @@ class CreateInvoiceLines extends Migration $existing = array_fill_keys(array_map(static fn ($row) => (int) ($row['invoice_id'] ?? 0), $existingRows), true); $invoices = $this->db->table('invoices') - ->select('id, total_amount, invoice_number, created_at, updated_at') + ->select('id, total_amount, invoice_number, school_year, created_at, updated_at') ->orderBy('id', 'ASC') ->get() ->getResultArray(); @@ -143,6 +149,7 @@ class CreateInvoiceLines extends Migration $this->db->table('invoice_lines')->insert([ 'invoice_id' => $invoiceId, + 'school_year' => (string)($invoice['school_year'] ?? ''), 'line_type' => 'legacy_invoice_total', 'source_type' => 'legacy_invoice', 'source_id' => $invoiceId, @@ -164,4 +171,33 @@ class CreateInvoiceLines extends Migration ]); } } + + private function ensureSchoolYearColumn(): void + { + if (!$this->db->tableExists('invoice_lines')) { + return; + } + + if (!$this->db->fieldExists('school_year', 'invoice_lines')) { + $this->forge->addColumn('invoice_lines', [ + 'school_year' => [ + 'type' => 'VARCHAR', + 'constraint' => 9, + 'null' => true, + 'after' => 'invoice_id', + ], + ]); + } + + if ($this->db->tableExists('invoices')) { + $this->db->query( + "UPDATE invoice_lines il + INNER JOIN invoices i ON i.id = il.invoice_id + SET il.school_year = i.school_year + WHERE il.school_year IS NULL OR TRIM(il.school_year) = ''" + ); + } + + $this->db->query("ALTER TABLE invoice_lines MODIFY school_year VARCHAR(9) NOT NULL"); + } } diff --git a/app/Database/Migrations/2026-07-19-000200_AddRefundSourcesAndPayouts.php b/app/Database/Migrations/2026-07-19-000200_AddRefundSourcesAndPayouts.php index 782457f..e6b3cba 100644 --- a/app/Database/Migrations/2026-07-19-000200_AddRefundSourcesAndPayouts.php +++ b/app/Database/Migrations/2026-07-19-000200_AddRefundSourcesAndPayouts.php @@ -10,6 +10,7 @@ class AddRefundSourcesAndPayouts extends Migration { $this->ensureRefundSourceColumns(); $this->ensureRefundPayoutsTable(); + $this->ensureRefundPayoutSchoolYearColumn(); $this->ensureRefundPayoutFingerprintColumns(); $this->backfillRefundSources(); $this->backfillLegacyPayouts(); @@ -67,6 +68,7 @@ class AddRefundSourcesAndPayouts extends Migration $this->forge->addField([ 'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true], 'refund_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => false], + 'school_year' => ['type' => 'VARCHAR', 'constraint' => 9, 'null' => false], 'amount_cents' => ['type' => 'INT', 'constraint' => 11, 'null' => false], 'currency' => ['type' => 'CHAR', 'constraint' => 3, 'null' => false, 'default' => 'USD'], 'payout_type' => ['type' => 'VARCHAR', 'constraint' => 30, 'null' => false, 'default' => 'cash_out'], @@ -94,6 +96,35 @@ class AddRefundSourcesAndPayouts extends Migration $this->forge->createTable('refund_payouts', true); } + private function ensureRefundPayoutSchoolYearColumn(): void + { + if (!$this->db->tableExists('refund_payouts')) { + return; + } + + if (!$this->db->fieldExists('school_year', 'refund_payouts')) { + $this->forge->addColumn('refund_payouts', [ + 'school_year' => [ + 'type' => 'VARCHAR', + 'constraint' => 9, + 'null' => true, + 'after' => 'refund_id', + ], + ]); + } + + if ($this->db->tableExists('refunds')) { + $this->db->query( + "UPDATE refund_payouts rp + INNER JOIN refunds r ON r.id = rp.refund_id + SET rp.school_year = r.school_year + WHERE rp.school_year IS NULL OR TRIM(rp.school_year) = ''" + ); + } + + $this->db->query("ALTER TABLE refund_payouts MODIFY school_year VARCHAR(9) NOT NULL"); + } + private function ensureRefundPayoutFingerprintColumns(): void { if (!$this->db->tableExists('refund_payouts')) { @@ -161,7 +192,7 @@ class AddRefundSourcesAndPayouts extends Migration } $refunds = $this->db->table('refunds') - ->select('id, refund_paid_amount, currency, refund_method, check_nbr, check_file, refunded_at, updated_by, created_at, updated_at') + ->select('id, refund_paid_amount, currency, refund_method, check_nbr, check_file, school_year, refunded_at, updated_by, created_at, updated_at') ->where('refund_paid_amount >', 0) ->get() ->getResultArray(); @@ -182,6 +213,7 @@ class AddRefundSourcesAndPayouts extends Migration $this->db->table('refund_payouts')->insert([ 'refund_id' => $refundId, + 'school_year' => (string)($refund['school_year'] ?? ''), 'amount_cents' => (int) round(((float) ($refund['refund_paid_amount'] ?? 0)) * 100), 'currency' => (string) ($refund['currency'] ?? 'USD') ?: 'USD', 'payout_type' => 'cash_out', diff --git a/app/Database/Migrations/2026-07-19-000500_FinancialReimbursementPoHardening.php b/app/Database/Migrations/2026-07-19-000500_FinancialReimbursementPoHardening.php index 9c9caf3..adb4a8e 100644 --- a/app/Database/Migrations/2026-07-19-000500_FinancialReimbursementPoHardening.php +++ b/app/Database/Migrations/2026-07-19-000500_FinancialReimbursementPoHardening.php @@ -69,6 +69,8 @@ class FinancialReimbursementPoHardening extends Migration private function ensurePurchaseOrderInvariants(): void { + $this->ensurePurchaseOrderSchoolYearColumns(); + if (!$this->db->tableExists('purchase_order_items')) { return; } @@ -108,6 +110,63 @@ class FinancialReimbursementPoHardening extends Migration } } + private function ensurePurchaseOrderSchoolYearColumns(): void + { + foreach (['purchase_orders' => 'status', 'purchase_order_items' => 'purchase_order_id'] as $table => $after) { + if (!$this->db->tableExists($table)) { + continue; + } + if (!$this->db->fieldExists('school_year', $table)) { + $this->forge->addColumn($table, [ + 'school_year' => [ + 'type' => 'VARCHAR', + 'constraint' => 9, + 'null' => true, + 'after' => $after, + ], + ]); + } + } + + if ($this->db->tableExists('purchase_orders')) { + $currentYear = $this->currentSchoolYear(); + $this->db->query( + 'UPDATE purchase_orders SET school_year = ? WHERE school_year IS NULL OR TRIM(school_year) = ?', + [$currentYear, ''] + ); + $this->db->query('ALTER TABLE purchase_orders MODIFY school_year VARCHAR(9) NOT NULL'); + } + + if ($this->db->tableExists('purchase_orders') && $this->db->tableExists('purchase_order_items')) { + $this->db->query( + "UPDATE purchase_order_items poi + INNER JOIN purchase_orders po ON po.id = poi.purchase_order_id + SET poi.school_year = po.school_year + WHERE poi.school_year IS NULL OR TRIM(poi.school_year) = ''" + ); + $this->db->query('ALTER TABLE purchase_order_items MODIFY school_year VARCHAR(9) NOT NULL'); + } + } + + private function currentSchoolYear(): string + { + if ($this->db->tableExists('school_years')) { + $row = $this->db->table('school_years') + ->select('name') + ->where('status', 'active') + ->orderBy('id', 'DESC') + ->get(1) + ->getRowArray(); + $name = trim((string)($row['name'] ?? '')); + if (preg_match('/^\d{4}-\d{4}$/', $name) === 1) { + return $name; + } + } + + $year = (int)date('Y'); + return $year . '-' . ($year + 1); + } + private function createIndexIfMissing(string $table, string $indexName, string $sql): void { if (!$this->db->tableExists($table)) { diff --git a/app/Database/Migrations/2026-07-19-000600_CreatePaymentCorrections.php b/app/Database/Migrations/2026-07-19-000600_CreatePaymentCorrections.php index ca6e136..d51ed92 100644 --- a/app/Database/Migrations/2026-07-19-000600_CreatePaymentCorrections.php +++ b/app/Database/Migrations/2026-07-19-000600_CreatePaymentCorrections.php @@ -9,6 +9,7 @@ class CreatePaymentCorrections extends Migration public function up() { if ($this->db->tableExists('payment_corrections')) { + $this->ensureSchoolYearColumn(); return; } @@ -17,6 +18,7 @@ class CreatePaymentCorrections extends Migration 'payment_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => false], 'invoice_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => false], 'parent_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => false], + 'school_year' => ['type' => 'VARCHAR', 'constraint' => 9, 'null' => false], 'correction_type' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => false], 'approved_refundable_cents' => ['type' => 'INT', 'constraint' => 11, 'null' => false], 'status' => ['type' => 'VARCHAR', 'constraint' => 30, 'null' => false], @@ -31,7 +33,9 @@ class CreatePaymentCorrections extends Migration $this->db->query('CREATE INDEX idx_payment_corrections_payment_status ON payment_corrections (payment_id, status)'); $this->db->query('CREATE INDEX idx_payment_corrections_invoice_status ON payment_corrections (invoice_id, status)'); + $this->db->query('CREATE INDEX idx_payment_corrections_year_status ON payment_corrections (school_year, status)'); $this->assertIndexExists('payment_corrections', 'idx_payment_corrections_payment_status'); + $this->assertIndexExists('payment_corrections', 'idx_payment_corrections_year_status'); } public function down() @@ -52,4 +56,45 @@ class CreatePaymentCorrections extends Migration throw new \RuntimeException("Required index {$indexName} was not created on {$table}."); } + + private function ensureSchoolYearColumn(): void + { + if (!$this->db->fieldExists('school_year', 'payment_corrections')) { + $this->forge->addColumn('payment_corrections', [ + 'school_year' => [ + 'type' => 'VARCHAR', + 'constraint' => 9, + 'null' => true, + 'after' => 'parent_id', + ], + ]); + } + + if ($this->db->tableExists('invoices')) { + $this->db->query( + "UPDATE payment_corrections pc + INNER JOIN invoices i ON i.id = pc.invoice_id + SET pc.school_year = i.school_year + WHERE pc.school_year IS NULL OR TRIM(pc.school_year) = ''" + ); + } + + $this->db->query("ALTER TABLE payment_corrections MODIFY school_year VARCHAR(9) NOT NULL"); + if (!$this->indexExists('payment_corrections', 'idx_payment_corrections_year_status')) { + $this->db->query('CREATE INDEX idx_payment_corrections_year_status ON payment_corrections (school_year, status)'); + } + $this->assertIndexExists('payment_corrections', 'idx_payment_corrections_year_status'); + } + + private function indexExists(string $table, string $indexName): bool + { + $indexes = $this->db->query('SHOW INDEX FROM ' . $this->db->escapeIdentifiers($table))->getResultArray(); + foreach ($indexes as $index) { + if (($index['Key_name'] ?? '') === $indexName) { + return true; + } + } + + return false; + } } diff --git a/app/Database/Migrations/2026-07-19-000900_EnsureFinancialModelSchoolYears.php b/app/Database/Migrations/2026-07-19-000900_EnsureFinancialModelSchoolYears.php new file mode 100644 index 0000000..0573728 --- /dev/null +++ b/app/Database/Migrations/2026-07-19-000900_EnsureFinancialModelSchoolYears.php @@ -0,0 +1,141 @@ +ensureColumn('invoice_lines', 'invoice_id'); + $this->backfillFromParent('invoice_lines', 'invoices', 'invoice_id'); + + $this->ensureColumn('refund_payouts', 'refund_id'); + $this->backfillFromParent('refund_payouts', 'refunds', 'refund_id'); + + $this->ensureColumn('payment_corrections', 'parent_id'); + $this->backfillFromParent('payment_corrections', 'invoices', 'invoice_id'); + + $this->ensureColumn('reimbursement_batch_admin_files', 'batch_id'); + $this->backfillFromParent('reimbursement_batch_admin_files', 'reimbursement_batches', 'batch_id'); + + $this->ensureColumn('purchase_orders', 'status'); + $this->backfillCurrentYear('purchase_orders'); + + $this->ensureColumn('purchase_order_items', 'purchase_order_id'); + $this->backfillFromParent('purchase_order_items', 'purchase_orders', 'purchase_order_id'); + } + + public function down() + { + // school_year is now part of the financial model contract; do not drop it. + } + + private function ensureColumn(string $table, string $after): void + { + if (!$this->db->tableExists($table)) { + return; + } + + if (!$this->db->fieldExists('school_year', $table)) { + $this->forge->addColumn($table, [ + 'school_year' => [ + 'type' => 'VARCHAR', + 'constraint' => 9, + 'null' => true, + 'after' => $after, + ], + ]); + } + } + + private function backfillFromParent(string $childTable, string $parentTable, string $foreignKey): void + { + if ( + !$this->db->tableExists($childTable) + || !$this->db->tableExists($parentTable) + || !$this->db->fieldExists('school_year', $childTable) + || !$this->db->fieldExists('school_year', $parentTable) + || !$this->db->fieldExists($foreignKey, $childTable) + ) { + return; + } + + $this->db->query(sprintf( + "UPDATE %s child + INNER JOIN %s parent ON parent.id = child.%s + SET child.school_year = parent.school_year + WHERE child.school_year IS NULL OR TRIM(child.school_year) = ''", + $this->db->escapeIdentifiers($childTable), + $this->db->escapeIdentifiers($parentTable), + $this->db->escapeIdentifiers($foreignKey) + )); + + $this->backfillCurrentYear($childTable); + } + + private function backfillCurrentYear(string $table): void + { + if (!$this->db->tableExists($table) || !$this->db->fieldExists('school_year', $table)) { + return; + } + + $this->db->query( + sprintf( + 'UPDATE %s SET school_year = ? WHERE school_year IS NULL OR TRIM(school_year) = ?', + $this->db->escapeIdentifiers($table) + ), + [$this->currentSchoolYear(), ''] + ); + + $this->db->query(sprintf( + 'ALTER TABLE %s MODIFY school_year VARCHAR(9) NOT NULL', + $this->db->escapeIdentifiers($table) + )); + + $this->ensureIndex($table); + } + + private function ensureIndex(string $table): void + { + $indexName = 'idx_' . $table . '_school_year'; + if (strlen($indexName) > 64) { + $indexName = 'idx_' . substr(hash('sha256', $table . '_school_year'), 0, 24); + } + + $indexes = $this->db->query('SHOW INDEX FROM ' . $this->db->escapeIdentifiers($table))->getResultArray(); + foreach ($indexes as $index) { + if (($index['Key_name'] ?? '') === $indexName) { + return; + } + } + + $this->db->query(sprintf( + 'CREATE INDEX %s ON %s (school_year)', + $this->db->escapeIdentifiers($indexName), + $this->db->escapeIdentifiers($table) + )); + } + + private function currentSchoolYear(): string + { + if ($this->db->tableExists('school_years')) { + $row = $this->db->table('school_years') + ->select('name') + ->where('status', 'active') + ->orderBy('id', 'DESC') + ->get(1) + ->getRowArray(); + + $name = trim((string)($row['name'] ?? '')); + if (preg_match('/^\d{4}-\d{4}$/', $name) === 1) { + return $name; + } + } + + $year = (int)date('Y'); + + return $year . '-' . ($year + 1); + } +} diff --git a/app/Libraries/InvoiceAdjustmentService.php b/app/Libraries/InvoiceAdjustmentService.php index d5d66b2..3e80e8f 100644 --- a/app/Libraries/InvoiceAdjustmentService.php +++ b/app/Libraries/InvoiceAdjustmentService.php @@ -56,6 +56,7 @@ class InvoiceAdjustmentService $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, @@ -139,6 +140,7 @@ class InvoiceAdjustmentService $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, diff --git a/app/Libraries/InvoiceLedgerService.php b/app/Libraries/InvoiceLedgerService.php index 48c5e26..6024598 100644 --- a/app/Libraries/InvoiceLedgerService.php +++ b/app/Libraries/InvoiceLedgerService.php @@ -388,6 +388,7 @@ class InvoiceLedgerService ): array { return [ 'invoice_id' => $invoiceId, + 'school_year' => (string)($metadata['school_year'] ?? ''), 'line_type' => $lineType, 'source_type' => $sourceType, 'source_id' => $sourceId, diff --git a/app/Models/InvoiceLineModel.php b/app/Models/InvoiceLineModel.php index 98f216a..18b3ec3 100644 --- a/app/Models/InvoiceLineModel.php +++ b/app/Models/InvoiceLineModel.php @@ -3,9 +3,12 @@ namespace App\Models; use CodeIgniter\Model; +use App\Models\Concerns\SchoolYearAutoFillTrait; class InvoiceLineModel extends Model { + use SchoolYearAutoFillTrait; + protected $table = 'invoice_lines'; protected $primaryKey = 'id'; protected $returnType = 'array'; @@ -13,6 +16,7 @@ class InvoiceLineModel extends Model protected $allowedFields = [ 'invoice_id', + 'school_year', 'line_type', 'source_type', 'source_id', @@ -31,6 +35,7 @@ class InvoiceLineModel extends Model protected $validationRules = [ 'invoice_id' => 'required|integer', + 'school_year' => 'required|string|max_length[9]', 'line_type' => 'required|max_length[50]', 'description' => 'required|max_length[255]', 'quantity' => 'required|decimal', diff --git a/app/Models/PaymentCorrectionModel.php b/app/Models/PaymentCorrectionModel.php index 33cc98c..5cf48fa 100644 --- a/app/Models/PaymentCorrectionModel.php +++ b/app/Models/PaymentCorrectionModel.php @@ -3,9 +3,12 @@ namespace App\Models; use CodeIgniter\Model; +use App\Models\Concerns\SchoolYearAutoFillTrait; class PaymentCorrectionModel extends Model { + use SchoolYearAutoFillTrait; + protected $table = 'payment_corrections'; protected $primaryKey = 'id'; protected $returnType = 'array'; @@ -15,6 +18,7 @@ class PaymentCorrectionModel extends Model 'payment_id', 'invoice_id', 'parent_id', + 'school_year', 'correction_type', 'approved_refundable_cents', 'status', @@ -24,4 +28,14 @@ class PaymentCorrectionModel extends Model 'created_at', 'updated_at', ]; + + protected $validationRules = [ + 'payment_id' => 'required|integer', + 'invoice_id' => 'required|integer', + 'parent_id' => 'required|integer', + 'school_year' => 'required|string|max_length[9]', + 'correction_type' => 'required|max_length[50]', + 'approved_refundable_cents' => 'required|integer|greater_than[0]', + 'status' => 'required|max_length[30]', + ]; } diff --git a/app/Models/PurchaseOrderItemModel.php b/app/Models/PurchaseOrderItemModel.php index 9938636..30095bd 100644 --- a/app/Models/PurchaseOrderItemModel.php +++ b/app/Models/PurchaseOrderItemModel.php @@ -3,17 +3,21 @@ namespace App\Models; use CodeIgniter\Model; +use App\Models\Concerns\SchoolYearAutoFillTrait; class PurchaseOrderItemModel extends Model { + use SchoolYearAutoFillTrait; + protected $table = 'purchase_order_items'; protected $primaryKey = 'id'; - protected $allowedFields = ['purchase_order_id','supply_id','description','quantity','received_qty','unit_cost']; + protected $allowedFields = ['purchase_order_id','supply_id','school_year','description','quantity','received_qty','unit_cost']; protected $useTimestamps = true; protected $validationRules = [ 'purchase_order_id' => 'required|is_natural_no_zero', 'supply_id' => 'required|is_natural_no_zero', + 'school_year' => 'required|string|max_length[9]', 'quantity' => 'required|is_natural_no_zero', 'received_qty' => 'if_exist|integer|greater_than_equal_to[0]', 'unit_cost' => 'required|decimal|greater_than_equal_to[0]', diff --git a/app/Models/PurchaseOrderModel.php b/app/Models/PurchaseOrderModel.php index 2946a34..724a260 100644 --- a/app/Models/PurchaseOrderModel.php +++ b/app/Models/PurchaseOrderModel.php @@ -3,18 +3,22 @@ namespace App\Models; use CodeIgniter\Model; +use App\Models\Concerns\SchoolYearAutoFillTrait; class PurchaseOrderModel extends Model { + use SchoolYearAutoFillTrait; + protected $table = 'purchase_orders'; protected $primaryKey = 'id'; - protected $allowedFields = ['po_number','supplier_id','status','order_date','expected_date','subtotal','tax','total','notes']; + protected $allowedFields = ['po_number','supplier_id','status','school_year','order_date','expected_date','subtotal','tax','total','notes']; protected $useTimestamps = true; protected $useSoftDeletes = true; protected $validationRules = [ 'po_number' => 'required|max_length[60]|is_unique[purchase_orders.po_number,id,{id}]', 'supplier_id' => 'required|is_natural_no_zero', + 'school_year' => 'required|string|max_length[9]', 'status' => 'in_list[draft,ordered,received,canceled]', ]; } diff --git a/app/Models/RefundPayoutModel.php b/app/Models/RefundPayoutModel.php index cf02f59..978c065 100644 --- a/app/Models/RefundPayoutModel.php +++ b/app/Models/RefundPayoutModel.php @@ -3,9 +3,12 @@ namespace App\Models; use CodeIgniter\Model; +use App\Models\Concerns\SchoolYearAutoFillTrait; class RefundPayoutModel extends Model { + use SchoolYearAutoFillTrait; + protected $table = 'refund_payouts'; protected $primaryKey = 'id'; protected $returnType = 'array'; @@ -13,6 +16,7 @@ class RefundPayoutModel extends Model protected $allowedFields = [ 'refund_id', + 'school_year', 'amount_cents', 'currency', 'payout_type', @@ -36,6 +40,7 @@ class RefundPayoutModel extends Model protected $validationRules = [ 'refund_id' => 'required|integer', + 'school_year' => 'required|string|max_length[9]', 'amount_cents' => 'required|integer|greater_than[0]', 'currency' => 'required|exact_length[3]', 'payout_type' => 'required|in_list[cash_out,account_credit,reversal]', diff --git a/scripts/financial-formula-baseline.txt b/scripts/financial-formula-baseline.txt index bb8b2b0..fc58760 100644 --- a/scripts/financial-formula-baseline.txt +++ b/scripts/financial-formula-baseline.txt @@ -301,12 +301,12 @@ /app/Controllers/View/PaymentNotificationController.php:333 matches /Paid/i /app/Controllers/View/PaymentNotificationController.php:333 matches /Unpaid/i /app/Controllers/View/PaymentNotificationController.php:93 matches /balance\s*=/i -/app/Controllers/View/RefundController.php:1007 matches /Paid/i -/app/Controllers/View/RefundController.php:1014 matches /Paid/i -/app/Controllers/View/RefundController.php:1015 matches /Paid/i -/app/Controllers/View/RefundController.php:1028 matches /Paid/i -/app/Controllers/View/RefundController.php:1028 matches /refund_paid_amount/i -/app/Controllers/View/RefundController.php:1214 matches /Paid/i +/app/Controllers/View/RefundController.php:1009 matches /Paid/i +/app/Controllers/View/RefundController.php:1016 matches /Paid/i +/app/Controllers/View/RefundController.php:1017 matches /Paid/i +/app/Controllers/View/RefundController.php:1030 matches /Paid/i +/app/Controllers/View/RefundController.php:1030 matches /refund_paid_amount/i +/app/Controllers/View/RefundController.php:1216 matches /Paid/i /app/Controllers/View/RefundController.php:164 matches /Paid/i /app/Controllers/View/RefundController.php:168 matches /Paid/i /app/Controllers/View/RefundController.php:178 matches /Paid/i @@ -333,14 +333,14 @@ /app/Controllers/View/RefundController.php:631 matches /Paid/i /app/Controllers/View/RefundController.php:636 matches /Paid/i /app/Controllers/View/RefundController.php:660 matches /Paid/i -/app/Controllers/View/RefundController.php:664 matches /Paid/i -/app/Controllers/View/RefundController.php:698 matches /Paid/i -/app/Controllers/View/RefundController.php:698 matches /refund_paid_amount/i -/app/Controllers/View/RefundController.php:858 matches /Paid/i -/app/Controllers/View/RefundController.php:862 matches /Paid/i -/app/Controllers/View/RefundController.php:865 matches /Paid/i -/app/Controllers/View/RefundController.php:865 matches /refund_paid_amount/i +/app/Controllers/View/RefundController.php:665 matches /Paid/i +/app/Controllers/View/RefundController.php:699 matches /Paid/i +/app/Controllers/View/RefundController.php:699 matches /refund_paid_amount/i +/app/Controllers/View/RefundController.php:860 matches /Paid/i +/app/Controllers/View/RefundController.php:864 matches /Paid/i /app/Controllers/View/RefundController.php:867 matches /Paid/i +/app/Controllers/View/RefundController.php:867 matches /refund_paid_amount/i +/app/Controllers/View/RefundController.php:869 matches /Paid/i /app/Controllers/View/ReimbursementController.php:1204 matches /Paid/i /app/Controllers/View/ReimbursementController.php:1288 matches /Paid/i /app/Controllers/View/ReimbursementController.php:2038 matches /Paid/i diff --git a/tests/app/Models/PaymentModelTest.php b/tests/app/Models/PaymentModelTest.php index 36d027a..05444b6 100644 --- a/tests/app/Models/PaymentModelTest.php +++ b/tests/app/Models/PaymentModelTest.php @@ -14,6 +14,7 @@ class PaymentModelTest extends ModelCrudTestCase if ($model instanceof PaymentModel) { $overrides['transaction_id'] = uniqid('PAY-TEST-', true); + $overrides['request_fingerprint_hash'] = hash('sha256', uniqid('payment-model-test-', true)); $overrides['payment_method'] = 'cash'; $overrides['status'] = 'recorded'; }