import * as fs from "fs"; import * as path from "path"; const PROD_API = "https://api.homaroma.shop/api"; interface RawMaterialRef { id: number; name: string; pricePerUnit: number; unit: string; } interface VariantPlan { id: number; sku: string; option: string; price: number; hasBom: boolean; totalBomCost: number; } interface ProductPlan { index: number; productId: number; productName: string; scentName: string; category: string | null; bomStatus: string; configuredVariants: number; totalVariants: number; variants: VariantPlan[]; matchedFragrance: RawMaterialRef | null; packagingReady: boolean; } const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const BOTTLE_MAP: Record = { 30: 10, // ขวดพร้อมฝา Oilozone 30ml 60: 11, // ขวดพร้อมฝา Oilozone 60ml 85: 12, // ขวดพร้อมฝา Oilozone 85ml 120: 9, // ขวดพร้อมฝา Oilozone 120ml }; const FORMULA_MAP: Record = { 30: { fragQty: 2.4, dpgQty: 27.6 }, 60: { fragQty: 4.8, dpgQty: 55.2 }, 85: { fragQty: 6.8, dpgQty: 78.2 }, 120: { fragQty: 9.6, dpgQty: 110.4 }, }; function detectSize(variant: VariantPlan): number { const opt = (variant.option || "").toLowerCase(); const sku = (variant.sku || "").toLowerCase(); if (opt.includes("120") || sku.includes("120ml")) return 120; if (opt.includes("85") || sku.includes("85ml")) return 85; if (opt.includes("60") || sku.includes("60ml")) return 60; if (opt.includes("30") || sku.includes("30ml")) return 30; throw new Error(`Cannot detect size for variant ${variant.id} (${variant.sku} / ${variant.option})`); } async function main() { const jsonPath = path.resolve(process.cwd(), "oil_ozone_products.json"); if (!fs.existsSync(jsonPath)) { throw new Error(`File not found: ${jsonPath}`); } const products: ProductPlan[] = JSON.parse(fs.readFileSync(jsonPath, "utf-8")); console.log(`Loaded ${products.length} products from ${jsonPath}`); let successCount = 0; let skippedCount = 0; let failedCount = 0; for (const product of products) { console.log(`\n--------------------------------------------------`); console.log(`[${product.index}/${products.length}] Product #${product.productId}: ${product.productName}`); if (!product.matchedFragrance) { console.error(`❌ Skipped: No matching fragrance found for scent '${product.scentName}'`); failedCount++; continue; } if (product.bomStatus === "CONFIGURED" && product.configuredVariants === product.totalVariants) { console.log(`⚡ Already configured (${product.configuredVariants}/${product.totalVariants} variants). Skipping.`); skippedCount++; continue; } // Build variants payload const variantsPayload = product.variants.map((v) => { const size = detectSize(v); const formula = FORMULA_MAP[size]; const bottleId = BOTTLE_MAP[size]; return { variantId: v.id, sku: v.sku, bomItems: [ { rawMaterialId: product.matchedFragrance!.id, name: product.matchedFragrance!.name, quantity: formula.fragQty, }, { rawMaterialId: 2, // DPG name: "DPG", quantity: formula.dpgQty, }, { rawMaterialId: bottleId, quantity: 1, }, ], }; }); console.log(`Applying BOM for ${variantsPayload.length} variants with fragrance [ID ${product.matchedFragrance.id}] ${product.matchedFragrance.name}...`); try { const res = await fetch(`${PROD_API}/products/${product.productId}/bom`, { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ variants: variantsPayload }), }); if (!res.ok) { const errText = await res.text(); throw new Error(`HTTP ${res.status}: ${errText}`); } const resData = (await res.json()) as any; console.log(`✓ BOM saved successfully for #${product.productId}! Updated variants: ${resData.updatedVariants ?? variantsPayload.length}`); successCount++; } catch (err: any) { console.error(`❌ Failed to update BOM for #${product.productId}: ${err.message}`); failedCount++; } // Rate-limit pause await sleep(200); } console.log(`\n==================================================`); console.log(`BOM Application Summary:`); console.log(` - Newly configured: ${successCount}`); console.log(` - Already configured (skipped): ${skippedCount}`); console.log(` - Failed: ${failedCount}`); console.log(`==================================================\n`); } main().catch(console.error);