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
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('purchases', function (Blueprint $t) {
|
// Idempotent: MariaDB has no transactional DDL, so a half-applied prior
|
||||||
$t->foreignId('warehouse_id')->nullable()->after('supplier_id')
|
// run can leave columns/tables behind without recording the migration.
|
||||||
->constrained('warehouses')->nullOnDelete();
|
// 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) {
|
if (! Schema::hasColumn('purchase_items', 'qty_received')) {
|
||||||
$t->decimal('qty_received', 10, 2)->default(0)->after('qty');
|
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.
|
// Backfill: items previously marked `received=true` were fully received.
|
||||||
DB::statement('UPDATE purchase_items SET qty_received = qty WHERE received = 1');
|
DB::statement('UPDATE purchase_items SET qty_received = qty WHERE received = 1');
|
||||||
|
|
||||||
Schema::create('supplier_part_prices', function (Blueprint $t) {
|
if (! Schema::hasTable('supplier_part_prices')) {
|
||||||
$t->id();
|
Schema::create('supplier_part_prices', function (Blueprint $t) {
|
||||||
$t->foreignId('company_id')->constrained()->cascadeOnDelete();
|
$t->id();
|
||||||
$t->foreignId('supplier_id')->constrained()->cascadeOnDelete();
|
$t->foreignId('company_id')->constrained()->cascadeOnDelete();
|
||||||
$t->foreignId('part_id')->constrained()->cascadeOnDelete();
|
$t->foreignId('supplier_id')->constrained()->cascadeOnDelete();
|
||||||
$t->foreignId('purchase_id')->nullable()->constrained()->nullOnDelete();
|
$t->foreignId('part_id')->constrained()->cascadeOnDelete();
|
||||||
$t->decimal('price', 12, 2);
|
$t->foreignId('purchase_id')->nullable()->constrained()->nullOnDelete();
|
||||||
$t->string('currency', 6)->default('MDL');
|
$t->decimal('price', 12, 2);
|
||||||
$t->dateTime('observed_at');
|
$t->string('currency', 6)->default('MDL');
|
||||||
$t->timestamps();
|
$t->dateTime('observed_at');
|
||||||
|
$t->timestamps();
|
||||||
|
|
||||||
$t->index(['company_id', 'supplier_id', 'part_id', 'observed_at']);
|
$t->index(['company_id', 'supplier_id', 'part_id', 'observed_at']);
|
||||||
$t->index(['company_id', 'part_id', 'observed_at']);
|
$t->index(['company_id', 'part_id', 'observed_at']);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
|
|||||||
Reference in New Issue
Block a user