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:
@@ -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;
|
||||
@@ -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(
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user