fix(rule-engine): merge duplicate Sebeta yards, block dupes

Two active Sebeta yards (LEGACY_DEST/'Sebeta' and SEBETA/'sebeta')
split rates and routes across different yard ids, so route-scoped rate
lookups missed. Migration repoints every yard reference to the survivor,
retires the duplicate, and adds partial unique indexes on active label
and code. Service now rejects case-insensitive duplicate labels on
create/update - the old guard only compared generated codes, which
missed labels whose existing code differs (LEGACY_DEST).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Marshal
2026-07-30 09:25:02 +00:00
parent 9241ce01d2
commit b671f07ce6
5 changed files with 125 additions and 0 deletions

View File

@@ -22,6 +22,15 @@ export class YardsRepository implements IYardsRepository {
return this.repo.findOne({ where: { code } });
}
/** Case/whitespace-insensitive label lookup — backs the duplicate-yard guard. */
findByLabelInsensitive(label: string): Promise<Yard | null> {
return this.repo
.createQueryBuilder('yard')
.where('LOWER(TRIM(yard.label)) = LOWER(TRIM(:label))', { label })
.andWhere('yard.deleted_at IS NULL')
.getOne();
}
findAll(options?: FindManyOptions<Yard>): Promise<Yard[]> {
return this.repo.find(options);
}