fix(vehicles): canonicalise brand casing (case-insensitive brand match)
Decode persist looked brands up with a case-sensitive eq(), so an
uppercase decode string ("FORD") missed canonical "Ford" → brand_id
NULL + raw uppercase stored as brand_name, splitting one brand across
casing variants in analytics/catalog. Now matches brands
case-insensitively and stores the canonical name. Migration
0013_fix_brand_casing backfills existing rows (60 on prod, 1 on dev).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -193,16 +193,22 @@ export class VehiclesService {
|
||||
|
||||
// 3. Brand access check
|
||||
let brandId: string | null = null;
|
||||
const brandName = resolved.brandName;
|
||||
// Match the brands table case-insensitively and adopt the CANONICAL name:
|
||||
// decode sources emit the brand with inconsistent casing ("FORD" vs "Ford"),
|
||||
// and a case-sensitive lookup left brand_id NULL + stored the raw uppercase
|
||||
// string, splitting one brand across casing variants. See migration
|
||||
// 0013_fix_brand_casing for the backfill of pre-existing rows.
|
||||
let brandName = resolved.brandName;
|
||||
if (brandName) {
|
||||
const [brand] = await this.db
|
||||
.select()
|
||||
.from(brands)
|
||||
.where(eq(brands.name, brandName))
|
||||
.where(sql`lower(${brands.name}) = lower(${brandName})`)
|
||||
.limit(1);
|
||||
|
||||
if (brand) {
|
||||
brandId = brand.id;
|
||||
brandName = brand.name;
|
||||
await this.checkBrandAccess(userId, brandId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user