fix: make purchases partial-receipt migration idempotent
On MariaDB (no transactional DDL) a half-applied prior run left purchases.warehouse_id committed without recording the migration. On retry it failed with "Duplicate column name 'warehouse_id'", which aborted the migrate run and blocked every later migration (notifications, push, online store, pricing, labor templates, tire, subcontractor, bodyshop) — so those tables were never created (e.g. bodyshop_jobs missing → 500 on tenant pages). Guard each step with Schema::hasColumn / hasTable so the migration completes on re-run and unblocks the rest of the batch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,32 +9,41 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('purchases', function (Blueprint $t) {
|
||||
$t->foreignId('warehouse_id')->nullable()->after('supplier_id')
|
||||
->constrained('warehouses')->nullOnDelete();
|
||||
});
|
||||
// Idempotent: MariaDB has no transactional DDL, so a half-applied prior
|
||||
// run can leave columns/tables behind without recording the migration.
|
||||
// Guard each step so a re-run completes instead of erroring on duplicates.
|
||||
if (! Schema::hasColumn('purchases', 'warehouse_id')) {
|
||||
Schema::table('purchases', function (Blueprint $t) {
|
||||
$t->foreignId('warehouse_id')->nullable()->after('supplier_id')
|
||||
->constrained('warehouses')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
Schema::table('purchase_items', function (Blueprint $t) {
|
||||
$t->decimal('qty_received', 10, 2)->default(0)->after('qty');
|
||||
});
|
||||
if (! Schema::hasColumn('purchase_items', 'qty_received')) {
|
||||
Schema::table('purchase_items', function (Blueprint $t) {
|
||||
$t->decimal('qty_received', 10, 2)->default(0)->after('qty');
|
||||
});
|
||||
}
|
||||
|
||||
// Backfill: items previously marked `received=true` were fully received.
|
||||
DB::statement('UPDATE purchase_items SET qty_received = qty WHERE received = 1');
|
||||
|
||||
Schema::create('supplier_part_prices', function (Blueprint $t) {
|
||||
$t->id();
|
||||
$t->foreignId('company_id')->constrained()->cascadeOnDelete();
|
||||
$t->foreignId('supplier_id')->constrained()->cascadeOnDelete();
|
||||
$t->foreignId('part_id')->constrained()->cascadeOnDelete();
|
||||
$t->foreignId('purchase_id')->nullable()->constrained()->nullOnDelete();
|
||||
$t->decimal('price', 12, 2);
|
||||
$t->string('currency', 6)->default('MDL');
|
||||
$t->dateTime('observed_at');
|
||||
$t->timestamps();
|
||||
if (! Schema::hasTable('supplier_part_prices')) {
|
||||
Schema::create('supplier_part_prices', function (Blueprint $t) {
|
||||
$t->id();
|
||||
$t->foreignId('company_id')->constrained()->cascadeOnDelete();
|
||||
$t->foreignId('supplier_id')->constrained()->cascadeOnDelete();
|
||||
$t->foreignId('part_id')->constrained()->cascadeOnDelete();
|
||||
$t->foreignId('purchase_id')->nullable()->constrained()->nullOnDelete();
|
||||
$t->decimal('price', 12, 2);
|
||||
$t->string('currency', 6)->default('MDL');
|
||||
$t->dateTime('observed_at');
|
||||
$t->timestamps();
|
||||
|
||||
$t->index(['company_id', 'supplier_id', 'part_id', 'observed_at']);
|
||||
$t->index(['company_id', 'part_id', 'observed_at']);
|
||||
});
|
||||
$t->index(['company_id', 'supplier_id', 'part_id', 'observed_at']);
|
||||
$t->index(['company_id', 'part_id', 'observed_at']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
|
||||
Reference in New Issue
Block a user