From e1fb300b8714e3823384f55fbe5f025ef25e43cc Mon Sep 17 00:00:00 2001
From: parsa aghaei
Date: Wed, 29 Jul 2026 15:21:41 +0330
Subject: [PATCH] fix(seed): handle duplicate product barcodes safely with
try-catch in seed-products.ts
---
backend/prisma/seed-products.ts | 126 +++++++++++++++++++-------------
1 file changed, 77 insertions(+), 49 deletions(-)
diff --git a/backend/prisma/seed-products.ts b/backend/prisma/seed-products.ts
index 57d64bf..3755a66 100644
--- a/backend/prisma/seed-products.ts
+++ b/backend/prisma/seed-products.ts
@@ -204,56 +204,84 @@ export async function main() {
const RX_PRODUCTS = ['canina-canhydrox-gag', 'canina-petvital-gag', 'canina-herz-vital', 'canina-petvital-arthro-tabletten', 'canina-immun-booster', 'canina-petvital-bio-aktivator', 'canina-rinderblut-pulver', 'canina-velox-gelenkenergie'];
const requiresRx = RX_PRODUCTS.includes(baseSlug);
- const product = await prisma.product.upsert({
- where: { artNo },
- update: {
- barcode,
- slug,
- productGroup: baseSlug,
- nameFa,
- nameEn,
- scientificTagline: PRODUCT_TAGLINES_MAP[baseSlug] || null,
- description: cleanDesc,
- shortDescription: cleanShortDesc,
- ingredients: cleanIngredients,
- categoryId: category.id,
- categorySlug: category.slug,
- priceValue: sellPrice,
- wholesalePrice: Math.round(sellPrice * 0.7),
- requiresRx,
- buyPrice,
- priceDisplay,
- unit,
- packageSize,
- dosageLogic: cleanDosage,
- suitableFor,
- imageUrl: getProductImageUrl(baseSlug),
- },
- create: {
- artNo,
- barcode,
- slug,
- productGroup: baseSlug,
- nameFa,
- nameEn,
- scientificTagline: PRODUCT_TAGLINES_MAP[baseSlug] || null,
- description: cleanDesc,
- shortDescription: cleanShortDesc,
- ingredients: cleanIngredients,
- categoryId: category.id,
- categorySlug: category.slug,
- priceValue: sellPrice,
- wholesalePrice: Math.round(sellPrice * 0.7),
- requiresRx,
- buyPrice,
- priceDisplay,
- unit,
- packageSize,
- dosageLogic: cleanDosage,
- suitableFor,
- imageUrl: getProductImageUrl(baseSlug),
+ try {
+ const product = await prisma.product.upsert({
+ where: { artNo },
+ update: {
+ barcode,
+ slug,
+ productGroup: baseSlug,
+ nameFa,
+ nameEn,
+ scientificTagline: PRODUCT_TAGLINES_MAP[baseSlug] || null,
+ description: cleanDesc,
+ shortDescription: cleanShortDesc,
+ ingredients: cleanIngredients,
+ categoryId: category.id,
+ categorySlug: category.slug,
+ priceValue: sellPrice,
+ wholesalePrice: Math.round(sellPrice * 0.7),
+ requiresRx,
+ buyPrice,
+ priceDisplay,
+ unit,
+ packageSize,
+ dosageLogic: cleanDosage,
+ suitableFor,
+ imageUrl: getProductImageUrl(baseSlug),
+ },
+ create: {
+ artNo,
+ barcode,
+ slug,
+ productGroup: baseSlug,
+ nameFa,
+ nameEn,
+ scientificTagline: PRODUCT_TAGLINES_MAP[baseSlug] || null,
+ description: cleanDesc,
+ shortDescription: cleanShortDesc,
+ ingredients: cleanIngredients,
+ categoryId: category.id,
+ categorySlug: category.slug,
+ priceValue: sellPrice,
+ wholesalePrice: Math.round(sellPrice * 0.7),
+ requiresRx,
+ buyPrice,
+ priceDisplay,
+ unit,
+ packageSize,
+ dosageLogic: cleanDosage,
+ suitableFor,
+ imageUrl: getProductImageUrl(baseSlug),
+ }
+ });
+
+ // Seed ingredients
+ await prisma.productIngredient.deleteMany({ where: { productId: product.id } });
+ if (cleanIngredients) {
+ const parts = cleanIngredients.split(/[،,]/).map((s: string) => s.trim()).filter(Boolean);
+ const meaningful = parts.slice(0, 20);
+ for (const ing of meaningful) {
+ const truncated = ing.substring(0, 150);
+ if (truncated.length > 0) {
+ await prisma.productIngredient.create({
+ data: { productId: product.id, ingredient: truncated }
+ });
+ }
+ }
}
- });
+
+ // Seed symptoms
+ await prisma.productSymptom.deleteMany({ where: { productId: product.id } });
+ const symptomsList = PRODUCT_SYMPTOMS_MAP[baseSlug] || [];
+ for (const sym of symptomsList) {
+ await prisma.productSymptom.create({
+ data: { productId: product.id, symptom: sym }
+ });
+ }
+ } catch (err: any) {
+ console.warn(`[Seed] Warning upserting product ${artNo}:`, err.message || err);
+ }
// Seed ingredients
await prisma.productIngredient.deleteMany({ where: { productId: product.id } });