204 lines
7.0 KiB
PHP
204 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace App\Database\Migrations;
|
|
|
|
use CodeIgniter\Database\Migration;
|
|
|
|
class CreateInvoiceLines extends Migration
|
|
{
|
|
public function up()
|
|
{
|
|
if (!$this->db->tableExists('invoice_lines')) {
|
|
$this->forge->addField([
|
|
'id' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'unsigned' => true,
|
|
'auto_increment' => true,
|
|
],
|
|
'invoice_id' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'unsigned' => true,
|
|
'null' => false,
|
|
],
|
|
'school_year' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 9,
|
|
'null' => false,
|
|
],
|
|
'line_type' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 50,
|
|
'null' => false,
|
|
],
|
|
'source_type' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 50,
|
|
'null' => true,
|
|
],
|
|
'source_id' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'unsigned' => true,
|
|
'null' => true,
|
|
],
|
|
'active_source_key' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 120,
|
|
'null' => true,
|
|
],
|
|
'description' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 255,
|
|
'null' => false,
|
|
],
|
|
'quantity' => [
|
|
'type' => 'DECIMAL',
|
|
'constraint' => '10,2',
|
|
'null' => false,
|
|
'default' => '1.00',
|
|
],
|
|
'unit_amount_cents' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'null' => false,
|
|
'default' => 0,
|
|
],
|
|
'line_amount_cents' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'null' => false,
|
|
'default' => 0,
|
|
],
|
|
'discount_eligible' => [
|
|
'type' => 'TINYINT',
|
|
'constraint' => 1,
|
|
'null' => false,
|
|
'default' => 1,
|
|
],
|
|
'calculation_version' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 50,
|
|
'null' => true,
|
|
],
|
|
'metadata_json' => [
|
|
'type' => 'TEXT',
|
|
'null' => true,
|
|
],
|
|
'created_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => false,
|
|
],
|
|
'updated_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => false,
|
|
],
|
|
'voided_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => true,
|
|
],
|
|
]);
|
|
$this->forge->addKey('id', true);
|
|
$this->forge->addKey('invoice_id');
|
|
$this->forge->addKey(['source_type', 'source_id']);
|
|
$this->forge->addUniqueKey('active_source_key', 'uniq_invoice_lines_active_source_key');
|
|
$this->forge->createTable('invoice_lines', true);
|
|
}
|
|
|
|
$this->ensureSchoolYearColumn();
|
|
$this->backfillLegacyInvoiceLines();
|
|
}
|
|
|
|
public function down()
|
|
{
|
|
if ($this->db->tableExists('invoice_lines')) {
|
|
$this->forge->dropTable('invoice_lines', true);
|
|
}
|
|
}
|
|
|
|
private function backfillLegacyInvoiceLines(): void
|
|
{
|
|
if (!$this->db->tableExists('invoices') || !$this->db->tableExists('invoice_lines')) {
|
|
return;
|
|
}
|
|
|
|
$existingRows = $this->db->table('invoice_lines')
|
|
->select('invoice_id')
|
|
->groupBy('invoice_id')
|
|
->get()
|
|
->getResultArray();
|
|
$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, school_year, created_at, updated_at')
|
|
->orderBy('id', 'ASC')
|
|
->get()
|
|
->getResultArray();
|
|
|
|
$now = date('Y-m-d H:i:s');
|
|
foreach ($invoices as $invoice) {
|
|
$invoiceId = (int) ($invoice['id'] ?? 0);
|
|
if ($invoiceId <= 0 || isset($existing[$invoiceId])) {
|
|
continue;
|
|
}
|
|
|
|
$amountCents = (int) round(((float) ($invoice['total_amount'] ?? 0)) * 100);
|
|
$createdAt = $invoice['created_at'] ?? $now;
|
|
$updatedAt = $invoice['updated_at'] ?? $createdAt;
|
|
|
|
$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,
|
|
'active_source_key' => null,
|
|
'description' => 'Legacy invoice total preserved from invoice ' . (string) ($invoice['invoice_number'] ?? $invoiceId),
|
|
'quantity' => '1.00',
|
|
'unit_amount_cents' => $amountCents,
|
|
'line_amount_cents' => $amountCents,
|
|
'discount_eligible' => 0,
|
|
'calculation_version' => 'legacy_import',
|
|
'metadata_json' => json_encode([
|
|
'source' => 'invoices.total_amount',
|
|
'legacy_discount_eligible_base_cents' => 0,
|
|
'reconciliation_required' => true,
|
|
], JSON_UNESCAPED_SLASHES),
|
|
'created_at' => $createdAt ?: $now,
|
|
'updated_at' => $updatedAt ?: $now,
|
|
'voided_at' => null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|