fix(migration): case-insensitive dedupe on units backfill + use insertOrIgnore

MariaDB default collation is case-insensitive on VARCHAR unique indexes,
so 'L' and 'l' clash on units_company_id_code_unique. When a tenant had
'L' in parts.unit (uppercase) and the migration seeds 'l' (lowercase),
the extras loop tried to re-insert 'L' and errored out — blocking all
subsequent migrations (including the new injector_protocols tables).

Fix: lowercase the comparison + switch the extras insert to
insertOrIgnore so a residual race is a no-op.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-26 11:39:34 +00:00
parent 47eb4f62c1
commit f484167b51
@@ -81,11 +81,14 @@ return new class extends Migration
} }
$extraCodes = $extraCodes->unique(); $extraCodes = $extraCodes->unique();
$existingCodes = DB::table('units')->where('company_id', $companyId)->pluck('code')->all(); // Case-insensitive dedupe: MariaDB default collation treats 'L' == 'l'
// as the same value on the unique index, so we compare lowercased.
$existingCodes = DB::table('units')->where('company_id', $companyId)
->pluck('code')->map(fn ($c) => mb_strtolower((string) $c))->all();
foreach ($extraCodes as $code) { foreach ($extraCodes as $code) {
if (in_array($code, $existingCodes, true)) continue; if (in_array(mb_strtolower($code), $existingCodes, true)) continue;
if (mb_strlen($code) > 16) continue; if (mb_strlen($code) > 16) continue;
DB::table('units')->insert([ DB::table('units')->insertOrIgnore([
'company_id' => $companyId, 'company_id' => $companyId,
'code' => $code, 'code' => $code,
'name_ro' => $code, 'name_ro' => $code,