fix: correct Product-Constructeur join table orientation

The _ProductConstructeurs table was created with wrong column order:
- Before: A=product, B=constructeur
- After: A=constructeur, B=product (alphabetical order expected by Prisma)

This caused Prisma to fail loading constructeurs relations, resulting in empty constructeurs arrays in API responses.

Changes:
- Added migration to swap A/B columns and recreate foreign keys
- Added debug logs in products.service.ts and constructeur-link.util.ts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-01-22 11:48:06 +01:00
parent 8cfe48e0f5
commit ff278f5549
3 changed files with 56 additions and 1 deletions

View File

@@ -0,0 +1,29 @@
-- Fix _ProductConstructeurs table orientation
-- Issue: Table was created with A=product, B=constructeur
-- But Prisma expects alphabetical order: A=constructeur, B=product
-- Step 1: Save existing data to temp table
CREATE TEMP TABLE _ProductConstructeurs_backup AS
SELECT "A" as old_A, "B" as old_B FROM "_ProductConstructeurs";
-- Step 2: Drop foreign key constraints
ALTER TABLE "_ProductConstructeurs" DROP CONSTRAINT IF EXISTS "_ProductConstructeurs_A_fkey";
ALTER TABLE "_ProductConstructeurs" DROP CONSTRAINT IF EXISTS "_ProductConstructeurs_B_fkey";
-- Step 3: Clear the table
TRUNCATE TABLE "_ProductConstructeurs";
-- Step 4: Reinsert data with swapped columns (A and B inverted)
INSERT INTO "_ProductConstructeurs" ("A", "B")
SELECT old_B, old_A FROM _ProductConstructeurs_backup;
-- Step 5: Recreate foreign key constraints with correct orientation
ALTER TABLE "_ProductConstructeurs"
ADD CONSTRAINT "_ProductConstructeurs_A_fkey"
FOREIGN KEY ("A") REFERENCES "constructeurs"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "_ProductConstructeurs"
ADD CONSTRAINT "_ProductConstructeurs_B_fkey"
FOREIGN KEY ("B") REFERENCES "products"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -168,6 +168,8 @@ export async function syncConstructeurLinks(
),
);
let targetConstructeurIds = uniqueConstructeurIds;
console.log('[syncConstructeurLinks] Table:', tableName, 'ParentId:', parentId);
console.log('[syncConstructeurLinks] Unique IDs:', uniqueConstructeurIds);
const constructeurDelegate = (executor as any)?.constructeur as
| {
@@ -183,13 +185,16 @@ export async function syncConstructeurLinks(
where: { id: { in: targetConstructeurIds } },
select: { id: true },
});
console.log('[syncConstructeurLinks] Existing constructeurs in DB:', existing.map(c => c.id));
const existingIds = new Set(existing.map(({ id }) => id));
targetConstructeurIds = targetConstructeurIds.filter((id) =>
existingIds.has(id),
);
console.log('[syncConstructeurLinks] Filtered target IDs:', targetConstructeurIds);
}
const orientation = await resolveOrientation(executor, tableName);
console.log('[syncConstructeurLinks] Orientation:', orientation);
if (typeof executor.__syncConstructeurLinks === 'function') {
await executor.__syncConstructeurLinks(
@@ -200,6 +205,7 @@ export async function syncConstructeurLinks(
return targetConstructeurIds;
}
console.log('[syncConstructeurLinks] Deleting old links...');
await prisma.$executeRaw(
Prisma.sql`DELETE FROM ${table} WHERE ${Prisma.raw(
`"${orientation.parentColumn}"`,
@@ -207,6 +213,7 @@ export async function syncConstructeurLinks(
);
if (targetConstructeurIds.length === 0) {
console.log('[syncConstructeurLinks] No IDs to insert, returning empty array');
return [];
}
@@ -214,6 +221,7 @@ export async function syncConstructeurLinks(
(constructeurId) => Prisma.sql`(${parentId}, ${constructeurId})`,
);
console.log('[syncConstructeurLinks] Inserting', targetConstructeurIds.length, 'new links...');
await prisma.$executeRaw(
Prisma.sql`
INSERT INTO ${table} (
@@ -224,7 +232,10 @@ export async function syncConstructeurLinks(
ON CONFLICT DO NOTHING
`,
);
return fetchConstructeurIds(executor, tableName, parentId, orientation);
console.log('[syncConstructeurLinks] Links inserted, fetching final IDs...');
const finalIds = await fetchConstructeurIds(executor, tableName, parentId, orientation);
console.log('[syncConstructeurLinks] Final fetched IDs:', finalIds);
return finalIds;
}
export async function fetchConstructeurIds(

View File

@@ -124,22 +124,30 @@ export class ProductsService {
const constructeurIds = this.normalizeConstructeurIds(
createProductDto.constructeurIds,
);
console.log('[CREATE] Normalized constructeurIds:', constructeurIds);
const resolvedConstructeurIds =
await this.resolveExistingConstructeurIds(constructeurIds);
console.log('[CREATE] Resolved constructeurIds:', resolvedConstructeurIds);
const created = await this.prisma.product.create({
data,
include: PRODUCT_WITH_RELATIONS_INCLUDE,
});
console.log('[CREATE] Product created:', created.id);
let syncedConstructeurIds: string[] = [];
if (resolvedConstructeurIds.length > 0) {
console.log('[CREATE] Calling syncConstructeurLinks with:', resolvedConstructeurIds);
syncedConstructeurIds = await syncConstructeurLinks(
this.prisma,
'_ProductConstructeurs',
created.id,
resolvedConstructeurIds,
);
console.log('[CREATE] Synced constructeurIds:', syncedConstructeurIds);
} else {
console.log('[CREATE] No constructeurIds to sync');
}
const refreshed = await this.prisma.product.findUnique({
@@ -192,8 +200,10 @@ export class ProductsService {
const constructeurIds = this.normalizeConstructeurIds(
updateProductDto.constructeurIds,
);
console.log('[UPDATE] Normalized constructeurIds:', constructeurIds);
resolvedConstructeurIds =
await this.resolveExistingConstructeurIds(constructeurIds);
console.log('[UPDATE] Resolved constructeurIds:', resolvedConstructeurIds);
}
let syncedConstructeurIds: string[] | undefined;
@@ -203,14 +213,19 @@ export class ProductsService {
where: { id },
data,
});
console.log('[UPDATE] Product updated:', id);
if (resolvedConstructeurIds !== undefined) {
console.log('[UPDATE] Calling syncConstructeurLinks with:', resolvedConstructeurIds);
syncedConstructeurIds = await syncConstructeurLinks(
tx,
'_ProductConstructeurs',
id,
resolvedConstructeurIds,
);
console.log('[UPDATE] Synced constructeurIds:', syncedConstructeurIds);
} else {
console.log('[UPDATE] No constructeurIds to sync');
}
});