diff --git a/database/migrations/2026_09_26_150000_add_upgrade_columns_to_invoice_tables.php b/database/migrations/2026_09_26_150000_add_upgrade_columns_to_invoice_tables.php index c4e7daf..979df71 100644 --- a/database/migrations/2026_09_26_150000_add_upgrade_columns_to_invoice_tables.php +++ b/database/migrations/2026_09_26_150000_add_upgrade_columns_to_invoice_tables.php @@ -2,25 +2,18 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { - Schema::table('invoices', function (Blueprint $table) { - $table->unsignedBigInteger('service_id')->nullable()->after('order_id')->index() - ->comment('Service this invoice upgrades; set for invoice_type=3 (upgrade)'); + $this->addForeignColumn('invoices', 'service_id', 'order_id', 'services', + 'Service this invoice upgrades; set for invoice_type=3 (upgrade)'); - $table->foreign('service_id')->references('id')->on('services')->onDelete('set null'); - }); - - Schema::table('invoice_items', function (Blueprint $table) { - $table->unsignedBigInteger('addon_id')->nullable()->after('product_id')->index() - ->comment('Addon purchased on this line; set for type=upgrade'); - - $table->foreign('addon_id')->references('id')->on('product_addon')->onDelete('set null'); - }); + $this->addForeignColumn('invoice_items', 'addon_id', 'product_id', 'product_addon', + 'Addon purchased on this line; set for type=upgrade'); } public function down(): void @@ -35,4 +28,39 @@ return new class extends Migration $table->dropColumn('service_id'); }); } + + private function addForeignColumn(string $table, string $column, string $after, string $references, string $comment): void + { + if (! Schema::hasColumn($table, $column)) { + Schema::table($table, function (Blueprint $blueprint) use ($column, $after, $comment) { + $blueprint->unsignedBigInteger($column)->nullable()->after($after)->comment($comment); + }); + } + + if (! Schema::hasIndex($table, [$column])) { + Schema::table($table, fn (Blueprint $blueprint) => $blueprint->index($column)); + } + + if (collect(Schema::getForeignKeys($table))->contains(fn ($fk) => $fk['columns'] === [$column])) { + return; + } + + $this->nullZeroDates($table); + + Schema::table($table, function (Blueprint $blueprint) use ($column, $references) { + $blueprint->foreign($column)->references('id')->on($references)->nullOnDelete(); + }); + } + + private function nullZeroDates(string $table): void + { + $dateColumns = collect(Schema::getColumns($table)) + ->filter(fn ($c) => $c['nullable'] && in_array($c['type_name'], ['date', 'datetime', 'timestamp'], true)); + + foreach ($dateColumns as $c) { + DB::table($table) + ->whereRaw("CAST(`{$c['name']}` AS CHAR) LIKE '0000-%' OR CAST(`{$c['name']}` AS CHAR) LIKE '%-00%'") + ->update([$c['name'] => null]); + } + } };