const { pool, initDB } = require('../db'); // This seed is deliberately isolated from Tiny/Olist data. It only creates or // refreshes records with LOCAL-DEMO / DEMONSTRAÇÃO identifiers, so it is safe // to run again in a local environment when the operational screens need data. const DEMO = 'DEMONSTRAÇÃO'; const normalizeMaterialKey = (value) => String(value || '') .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .toLowerCase() .trim() .replace(/\s+/g, ' '); const today = new Date().toISOString().slice(0, 10); const offsetDate = (days) => { const date = new Date(); date.setDate(date.getDate() + days); return date.toISOString().slice(0, 10); }; const getCategoryId = async (client, name) => { const result = await client.query('SELECT id FROM catalog_categories WHERE name = $1', [name]); return result.rows[0]?.id || null; }; const upsertCatalogProduct = async (client, product) => { const categoryId = await getCategoryId(client, product.category); const result = await client.query(` INSERT INTO catalog_products (type, sku, name, category_id, color, sizes, notes, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, CURRENT_TIMESTAMP) ON CONFLICT (sku) DO UPDATE SET type = EXCLUDED.type, name = EXCLUDED.name, category_id = EXCLUDED.category_id, color = EXCLUDED.color, sizes = EXCLUDED.sizes, notes = EXCLUDED.notes, updated_at = CURRENT_TIMESTAMP RETURNING id; `, [ product.type, product.sku, product.name, categoryId, product.color || null, product.sizes || [], `${DEMO} — registro local para visualizar o fluxo de Suprimentos.` ]); return Number(result.rows[0].id); }; const findCatalogProduct = async (client, sku) => { const result = await client.query('SELECT id FROM catalog_products WHERE sku = $1', [sku]); return result.rows[0] ? Number(result.rows[0].id) : null; }; const upsertConsumptionReference = async (client, reference) => { const existing = await client.query(` SELECT id FROM consumption_references WHERE product_id = $1 AND COALESCE(material_product_id, 0) = COALESCE($2, 0) AND COALESCE(color, '') = COALESCE($3, '') AND COALESCE(source, 'manual') = 'demonstration' LIMIT 1; `, [reference.productId, reference.materialProductId, reference.color || null]); const values = [ reference.productId, reference.materialProductId, reference.color || null, reference.generalYield || null, reference.consumptionQuantity || null, reference.consumptionUnit || null ]; if (existing.rows[0]) { await client.query(` UPDATE consumption_references SET general_yield = $4, consumption_quantity = $5, consumption_unit = $6, updated_at = CURRENT_TIMESTAMP WHERE id = $7; `, [...values, existing.rows[0].id]); return; } await client.query(` INSERT INTO consumption_references ( product_id, material_product_id, color, general_yield, consumption_quantity, consumption_unit, source ) VALUES ($1, $2, $3, $4, $5, $6, 'demonstration'); `, values); }; const upsertPurchaseOrder = async (client, order) => { const result = await client.query(` INSERT INTO supply_purchase_orders (number, supplier, status, expected_date, notes, updated_at) VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP) ON CONFLICT (number) DO UPDATE SET supplier = EXCLUDED.supplier, status = EXCLUDED.status, expected_date = EXCLUDED.expected_date, notes = EXCLUDED.notes, updated_at = CURRENT_TIMESTAMP RETURNING id; `, [order.number, order.supplier, order.status, order.expectedDate, order.notes]); const id = Number(result.rows[0].id); for (const item of order.items) { await client.query(` INSERT INTO supply_purchase_order_items (purchase_order_id, material, quantity, unit, unit_cost) SELECT $1, $2, $3, $4, $5 WHERE NOT EXISTS ( SELECT 1 FROM supply_purchase_order_items WHERE purchase_order_id = $1 AND material = $2 ); `, [id, item.material, item.quantity, item.unit, item.unitCost]); } return id; }; const upsertReceipt = async (client, receipt) => { const existing = await client.query('SELECT id FROM supply_receipts WHERE invoice = $1 LIMIT 1', [receipt.invoice]); if (existing.rows[0]) { await client.query(` UPDATE supply_receipts SET category = $1, product = $2, quantity = $3, unit = $4, supplier = $5, notes = $6, status = $7, purchase_order_id = $8, approved_at = CASE WHEN $7 = 'approved' THEN COALESCE(approved_at, CURRENT_TIMESTAMP) ELSE NULL END, updated_at = CURRENT_TIMESTAMP WHERE id = $9; `, [receipt.category, receipt.product, receipt.quantity, receipt.unit, receipt.supplier, receipt.notes, receipt.status, receipt.purchaseOrderId, existing.rows[0].id]); return Number(existing.rows[0].id); } const result = await client.query(` INSERT INTO supply_receipts ( category, product, quantity, unit, supplier, invoice, notes, status, purchase_order_id, approved_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, CASE WHEN $8 = 'approved' THEN CURRENT_TIMESTAMP ELSE NULL END) RETURNING id; `, [receipt.category, receipt.product, receipt.quantity, receipt.unit, receipt.supplier, receipt.invoice, receipt.notes, receipt.status, receipt.purchaseOrderId]); return Number(result.rows[0].id); }; const ensureLotAndMovement = async (client, receiptId) => { const receipt = await client.query(` SELECT id, category, product, quantity, unit, supplier, invoice FROM supply_receipts WHERE id = $1 AND status = 'approved'; `, [receiptId]); if (!receipt.rows[0]) return null; const row = receipt.rows[0]; const lot = await client.query(` INSERT INTO supply_stock_lots (receipt_id, category, product, quantity, unit, supplier, invoice, status) SELECT $1, $2, $3, $4, $5, $6, $7, 'active' WHERE NOT EXISTS (SELECT 1 FROM supply_stock_lots WHERE receipt_id = $1) RETURNING id; `, [row.id, row.category, row.product, row.quantity, row.unit, row.supplier, row.invoice]); const lotId = lot.rows[0]?.id || (await client.query('SELECT id FROM supply_stock_lots WHERE receipt_id = $1', [row.id])).rows[0]?.id; if (!lotId) return null; await client.query(` INSERT INTO supply_movements (receipt_id, lot_id, type, category, product, quantity, unit, reason) SELECT $1, $2, 'receipt', $3, $4, $5, $6, $7 WHERE NOT EXISTS ( SELECT 1 FROM supply_movements WHERE receipt_id = $1 AND type = 'receipt' ); `, [row.id, lotId, row.category, row.product, row.quantity, row.unit, `${DEMO} — entrada aprovada para visualização.`]); return Number(lotId); }; const upsertProductionPlan = async (client, plan, responsibleUserId) => { const result = await client.query(` INSERT INTO production_plans ( number, name, status, contractor, responsible_user_id, responsible_name, planned_start_date, expected_date, notes, source ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 'cut_plan') ON CONFLICT (number) DO UPDATE SET name = EXCLUDED.name, status = EXCLUDED.status, contractor = EXCLUDED.contractor, responsible_user_id = EXCLUDED.responsible_user_id, responsible_name = EXCLUDED.responsible_name, planned_start_date = EXCLUDED.planned_start_date, expected_date = EXCLUDED.expected_date, notes = EXCLUDED.notes, updated_at = CURRENT_TIMESTAMP RETURNING id; `, [ plan.number, plan.name, plan.status, plan.contractor, responsibleUserId, plan.responsibleName, plan.plannedStartDate, plan.expectedDate, plan.notes ]); return Number(result.rows[0].id); }; const upsertProductionOrder = async (client, order) => { const existing = await client.query('SELECT id FROM production_orders WHERE number = $1 LIMIT 1', [order.number]); const values = [ order.status, order.planId, order.contractor, order.orderReference, order.issueDate, order.expectedDate, order.productSku, order.productDescription, order.quantity, order.unit, order.notes, order.supplier, order.lotCode, order.rollQuantity, order.fabricKg, order.ribKg, order.yieldPiecesPerKg, order.actualCutQuantity, order.rejectedQuantity, order.finishedQuantity, order.qualityNotes, order.cutLayers, order.markerReference ]; if (existing.rows[0]) { await client.query(` UPDATE production_orders SET status = $1, production_plan_id = $2, contractor = $3, order_reference = $4, issue_date = $5, expected_date = $6, product_sku = $7, product_description = $8, quantity = $9, unit = $10, notes = $11, supplier = $12, lot_code = $13, roll_quantity = $14, fabric_kg = $15, rib_kg = $16, yield_pieces_per_kg = $17, actual_cut_quantity = $18, rejected_quantity = $19, finished_quantity = $20, quality_notes = $21, cut_layers = $22, marker_reference = $23, updated_at = CURRENT_TIMESTAMP WHERE id = $24; `, [...values, existing.rows[0].id]); return Number(existing.rows[0].id); } const result = await client.query(` INSERT INTO production_orders ( number, status, production_plan_id, contractor, order_reference, issue_date, expected_date, product_sku, product_description, quantity, unit, notes, supplier, lot_code, roll_quantity, fabric_kg, rib_kg, yield_pieces_per_kg, actual_cut_quantity, rejected_quantity, finished_quantity, quality_notes, cut_layers, marker_reference ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24) RETURNING id; `, [order.number, ...values]); return Number(result.rows[0].id); }; const ensureOrderDetails = async (client, orderId, details) => { for (const marker of details.markers) { await client.query(` INSERT INTO production_order_markers (production_order_id, label, color) SELECT $1, $2, $3 WHERE NOT EXISTS ( SELECT 1 FROM production_order_markers WHERE production_order_id = $1 AND label = $2 ); `, [orderId, marker.label, marker.color]); } for (const component of details.components) { await client.query(` INSERT INTO production_order_components (production_order_id, component_sku, component_name, quantity_per_unit, total_quantity, unit) SELECT $1, $2, $3, $4, $5, $6 WHERE NOT EXISTS ( SELECT 1 FROM production_order_components WHERE production_order_id = $1 AND component_name = $3 ); `, [orderId, component.sku, component.name, component.quantityPerUnit, component.totalQuantity, component.unit]); } for (const step of details.steps) { await client.query(` INSERT INTO production_order_steps (production_order_id, step_number, name, start_date, end_date, status, color) SELECT $1, $2, $3, $4, $5, $6, $7 WHERE NOT EXISTS ( SELECT 1 FROM production_order_steps WHERE production_order_id = $1 AND step_number = $2 ); `, [orderId, step.number, step.name, step.startDate, step.endDate, step.status, step.color]); } }; async function main() { await initDB(); const client = await pool.connect(); try { await client.query('BEGIN'); const materials = { fabric: await upsertCatalogProduct(client, { type: 'raw_material', sku: 'LOCAL-DEMO-MAT-MALHA-PRETA', category: 'Malha', name: 'DEMO-MALHA-300 MALHA 30/1 COR PRETO', color: 'Preto' }), rib: await upsertCatalogProduct(client, { type: 'raw_material', sku: 'LOCAL-DEMO-MAT-RIBANA-PRETA', category: 'Aviamentos', name: 'DEMO-0201 RIBANA 2X1 COR PRETO', color: 'Preto' }), hoodieFabric: await upsertCatalogProduct(client, { type: 'raw_material', sku: 'LOCAL-DEMO-MAT-MOLETOM-GRAFITE', category: 'Malha', name: 'DEMO-MOLETOM 3 CABOS COR GRAFITE', color: 'Grafite' }), labelM: await upsertCatalogProduct(client, { type: 'raw_material', sku: 'LOCAL-DEMO-MAT-ETIQUETA-M', category: 'Embalagens', name: 'DEMO-ETIQUETA TAMANHO M', sizes: ['M'] }), labelG: await upsertCatalogProduct(client, { type: 'raw_material', sku: 'LOCAL-DEMO-MAT-ETIQUETA-G', category: 'Embalagens', name: 'DEMO-ETIQUETA TAMANHO G', sizes: ['G'] }) }; const finishedProducts = { teeM: await findCatalogProduct(client, 'LOCAL-DEMO-UX-TEE-M'), teeG: await findCatalogProduct(client, 'LOCAL-DEMO-UX-TEE-G'), hoodieM: await findCatalogProduct(client, 'LOCAL-DEMO-UX-HOODIE-M') }; if (!Object.values(finishedProducts).every(Boolean)) { throw new Error('Os produtos locais de demonstração esperados não foram encontrados.'); } await Promise.all([ upsertConsumptionReference(client, { productId: finishedProducts.teeM, materialProductId: materials.fabric, color: 'Preto', consumptionQuantity: 0.185, consumptionUnit: 'kg' }), upsertConsumptionReference(client, { productId: finishedProducts.teeM, materialProductId: materials.rib, color: 'Preto', consumptionQuantity: 0.01, consumptionUnit: 'kg' }), upsertConsumptionReference(client, { productId: finishedProducts.teeM, materialProductId: materials.labelM, consumptionQuantity: 1, consumptionUnit: 'un.' }), upsertConsumptionReference(client, { productId: finishedProducts.teeG, materialProductId: materials.fabric, color: 'Preto', consumptionQuantity: 0.2, consumptionUnit: 'kg' }), upsertConsumptionReference(client, { productId: finishedProducts.teeG, materialProductId: materials.rib, color: 'Preto', consumptionQuantity: 0.011, consumptionUnit: 'kg' }), upsertConsumptionReference(client, { productId: finishedProducts.teeG, materialProductId: materials.labelG, consumptionQuantity: 1, consumptionUnit: 'un.' }), upsertConsumptionReference(client, { productId: finishedProducts.hoodieM, materialProductId: materials.hoodieFabric, color: 'Grafite', consumptionQuantity: 0.75, consumptionUnit: 'kg' }), upsertConsumptionReference(client, { productId: finishedProducts.hoodieM, materialProductId: materials.rib, color: 'Grafite', consumptionQuantity: 0.045, consumptionUnit: 'kg' }), upsertConsumptionReference(client, { productId: finishedProducts.hoodieM, materialProductId: materials.labelM, consumptionQuantity: 1, consumptionUnit: 'un.' }) ]); const supplier = `${DEMO} Têxteis`; const materialDetails = [ ['DEMO-MALHA-300 MALHA 30/1 COR PRETO', supplier, 'Malha reservada para a semana de demonstração.'], ['DEMO-0201 RIBANA 2X1 COR PRETO', supplier, 'Aviamento de gola e acabamento.'], ['DEMO-MOLETOM 3 CABOS COR GRAFITE', `${DEMO} Malharia`, 'Material para o plano de moletom.'] ]; for (const [material, preferredSupplier, notes] of materialDetails) { await client.query(` INSERT INTO supply_material_details (material_key, display_name, preferred_supplier, notes, updated_at) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) ON CONFLICT (material_key) DO UPDATE SET display_name = EXCLUDED.display_name, preferred_supplier = EXCLUDED.preferred_supplier, notes = EXCLUDED.notes, updated_at = CURRENT_TIMESTAMP; `, [normalizeMaterialKey(material), material, preferredSupplier, notes]); } const fabricPlans = [ ['DEMO-MALHA-300 MALHA 30/1 COR PRETO', 'Preto', 120, supplier, 'Crítico'], ['DEMO-0201 RIBANA 2X1 COR PRETO', 'Preto', 24, supplier, 'Atenção'], ['DEMO-MOLETOM 3 CABOS COR GRAFITE', 'Grafite', 90, `${DEMO} Malharia`, 'Normal'] ]; for (const [material, color, quantity, planSupplier, priority] of fabricPlans) { await client.query(` INSERT INTO supply_fabric_plans (material, color, quantity_kg, supplier, priority, status, updated_at) SELECT $1, $2, $3, $4, $5, 'active', CURRENT_TIMESTAMP WHERE NOT EXISTS ( SELECT 1 FROM supply_fabric_plans WHERE material = $1 AND COALESCE(color, '') = COALESCE($2, '') AND status = 'active' ); `, [material, color, quantity, planSupplier, priority]); } const poApproved = await upsertPurchaseOrder(client, { number: 'OC-DEMO-0001', supplier, status: 'approved', expectedDate: offsetDate(-2), notes: `${DEMO} — compra aprovada; o recebimento está disponível para conferência.`, items: [{ material: 'DEMO-MALHA-300 MALHA 30/1 COR PRETO', quantity: 120, unit: 'kg', unitCost: 29.9 }] }); const poSent = await upsertPurchaseOrder(client, { number: 'OC-DEMO-0002', supplier, status: 'sent', expectedDate: offsetDate(4), notes: `${DEMO} — pedido enviado ao fornecedor e aguardando chegada.`, items: [{ material: 'DEMO-0201 RIBANA 2X1 COR PRETO', quantity: 24, unit: 'kg', unitCost: 34.5 }] }); const poFinancial = await upsertPurchaseOrder(client, { number: 'OC-DEMO-0003', supplier: `${DEMO} Etiquetas`, status: 'pending_financial', expectedDate: offsetDate(6), notes: `${DEMO} — aguardando aprovação financeira; não libera saldo físico.`, items: [{ material: 'DEMO-ETIQUETA TAMANHO M', quantity: 500, unit: 'un.', unitCost: 0.28 }] }); const receiptIds = []; receiptIds.push(await upsertReceipt(client, { category: 'Malha', product: 'DEMO-MALHA-300 MALHA 30/1 COR PRETO', quantity: 120, unit: 'kg', supplier, invoice: 'DEMO-NF-004', status: 'approved', purchaseOrderId: poApproved, notes: `${DEMO} — recebimento conferido e saldo liberado.` })); receiptIds.push(await upsertReceipt(client, { category: 'Aviamentos', product: 'DEMO-0201 RIBANA 2X1 COR PRETO', quantity: 24, unit: 'kg', supplier, invoice: 'DEMO-NF-005', status: 'pending', purchaseOrderId: poSent, notes: `${DEMO} — aguardando conferência física e aprovação da compra.` })); receiptIds.push(await upsertReceipt(client, { category: 'Embalagens', product: 'DEMO-ETIQUETA TAMANHO M', quantity: 500, unit: 'un.', supplier: `${DEMO} Etiquetas`, invoice: 'DEMO-NF-006', status: 'pending', purchaseOrderId: poFinancial, notes: `${DEMO} — saldo bloqueado até a aprovação financeira da OC.` })); const lotIds = []; for (const receiptId of receiptIds) { const lotId = await ensureLotAndMovement(client, receiptId); if (lotId) lotIds.push(lotId); } const transformations = [ ['DEMO-LT-2408-01', 'tingimento', 'DEMO-MALHA-300 CRUA', 260, 'DEMO-MALHA-300 MALHA 30/1', 'Preto', 2, 254.8, `${DEMO} Tinturaria`, 'Atenção', 'in_progress', `${DEMO} — lote em tingimento.`], ['DEMO-LT-2408-02', 'acabamento', 'DEMO-MOLETOM 3 CABOS', 96, 'DEMO-MOLETOM 3 CABOS', 'Grafite', 1.5, 94.56, `${DEMO} Malharia`, 'Normal', 'planned', `${DEMO} — acabamento programado.`], ['DEMO-LT-2408-00', 'malharia', 'DEMO-FIO 30/1 PRETO', 180, 'DEMO-MALHA-300 MALHA 30/1', 'Preto', 3, 174.6, `${DEMO} Malharia`, 'Normal', 'completed', `${DEMO} — lote concluído para rastreabilidade.`] ]; for (const transformation of transformations) { await client.query(` INSERT INTO supply_material_transformations ( batch_code, stage, input_material, input_quantity_kg, output_material, output_color, expected_loss_percent, expected_output_kg, supplier, priority, status, notes, updated_at ) SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, CURRENT_TIMESTAMP WHERE NOT EXISTS ( SELECT 1 FROM supply_material_transformations WHERE batch_code = $1 AND stage = $2 ); `, transformation); } const user = await client.query(` SELECT id, name FROM app_users WHERE is_active = TRUE ORDER BY id LIMIT 1; `); const responsibleUserId = user.rows[0]?.id || null; const responsibleName = user.rows[0]?.name || 'Operação Graphs'; const planCut = await upsertProductionPlan(client, { number: 'PP-DEMO-0001', name: `${DEMO} | Corte preto — semana operacional`, status: 'cutting', contractor: `${DEMO} Confecção`, responsibleName, plannedStartDate: offsetDate(-1), expectedDate: offsetDate(3), notes: `${DEMO} — plano liberado com lote e risco definidos.` }, responsibleUserId); const planHoodie = await upsertProductionPlan(client, { number: 'PP-DEMO-0002', name: `${DEMO} | Moletom grafite — reposição`, status: 'open', contractor: `${DEMO} Confecção`, responsibleName, plannedStartDate: offsetDate(3), expectedDate: offsetDate(8), notes: `${DEMO} — aguardando liberação da etapa de malha.` }, responsibleUserId); await client.query(` INSERT INTO production_plan_reviews (production_plan_id, decision, checklist, notes, reviewed_by, reviewed_at) VALUES ($1, 'approved', $2::jsonb, $3, $4, CURRENT_TIMESTAMP) ON CONFLICT (production_plan_id) DO UPDATE SET decision = EXCLUDED.decision, checklist = EXCLUDED.checklist, notes = EXCLUDED.notes, reviewed_by = EXCLUDED.reviewed_by, reviewed_at = EXCLUDED.reviewed_at, updated_at = CURRENT_TIMESTAMP; `, [planCut, JSON.stringify({ material: true, yield: true, responsible: true, deadline: true }), `${DEMO} — checklist operacional concluído.`, responsibleName]); const orderM = await upsertProductionOrder(client, { number: 'OP-DEMO-0001', status: 'cutting', planId: planCut, contractor: `${DEMO} Confecção`, orderReference: 'DEMO-CORTE-35', issueDate: offsetDate(-1), expectedDate: offsetDate(2), productSku: 'LOCAL-DEMO-UX-TEE-M', productDescription: 'DEMO UX CAMISETA PRETA TAMANHO M', quantity: 180, unit: 'UN', notes: `${DEMO} — acompanhar aproveitamento de risco.`, supplier, lotCode: 'DEMO-NF-004', rollQuantity: 3, fabricKg: 33.3, ribKg: 1.8, yieldPiecesPerKg: 5.4, actualCutQuantity: 150, rejectedQuantity: 2, finishedQuantity: 0, qualityNotes: `${DEMO} — dois recortes separados para revisão de tecido.`, cutLayers: 12, markerReference: 'RISCO-DEMO-M-12' }); const orderG = await upsertProductionOrder(client, { number: 'OP-DEMO-0002', status: 'sewing', planId: planCut, contractor: `${DEMO} Confecção`, orderReference: 'DEMO-CORTE-35', issueDate: today, expectedDate: offsetDate(3), productSku: 'LOCAL-DEMO-UX-TEE-G', productDescription: 'DEMO UX CAMISETA PRETA TAMANHO G', quantity: 140, unit: 'UN', notes: `${DEMO} — lote encaminhado para costura.`, supplier, lotCode: 'DEMO-NF-004', rollQuantity: 2, fabricKg: 28, ribKg: 1.54, yieldPiecesPerKg: 5, actualCutQuantity: 140, rejectedQuantity: 1, finishedQuantity: 92, qualityNotes: `${DEMO} — conferir gola nas primeiras peças.`, cutLayers: 9, markerReference: 'RISCO-DEMO-G-09' }); const orderHoodie = await upsertProductionOrder(client, { number: 'OP-DEMO-0003', status: 'review', planId: planHoodie, contractor: `${DEMO} Confecção`, orderReference: 'DEMO-MOLETOM-35', issueDate: offsetDate(2), expectedDate: offsetDate(8), productSku: 'LOCAL-DEMO-UX-HOODIE-M', productDescription: 'DEMO UX MOLETOM GRAFITE TAMANHO M', quantity: 60, unit: 'UN', notes: `${DEMO} — amostra segue para revisão antes da liberação total.`, supplier: `${DEMO} Malharia`, lotCode: 'DEMO-LT-2408-02', rollQuantity: 2, fabricKg: 45, ribKg: 2.7, yieldPiecesPerKg: 1.33, actualCutQuantity: 60, rejectedQuantity: 2, finishedQuantity: 52, qualityNotes: `${DEMO} — registrar avarias por posto de trabalho.`, cutLayers: 6, markerReference: 'RISCO-DEMO-MOLETOM-06' }); await ensureOrderDetails(client, orderM, { markers: [{ label: 'Risco M — 12 camadas', color: '#fbbf24' }], components: [ { sku: 'LOCAL-DEMO-MAT-MALHA-PRETA', name: 'DEMO-MALHA-300 MALHA 30/1 COR PRETO', quantityPerUnit: 0.185, totalQuantity: 33.3, unit: 'kg' }, { sku: 'LOCAL-DEMO-MAT-RIBANA-PRETA', name: 'DEMO-0201 RIBANA 2X1 COR PRETO', quantityPerUnit: 0.01, totalQuantity: 1.8, unit: 'kg' } ], steps: [ { number: 1, name: 'Separação do lote', startDate: offsetDate(-1), endDate: offsetDate(-1), status: 'finished', color: '#22c55e' }, { number: 2, name: 'Corte', startDate: today, endDate: offsetDate(1), status: 'in_progress', color: '#fbbf24' }, { number: 3, name: 'Costura', startDate: offsetDate(1), endDate: offsetDate(2), status: 'pending', color: '#38bdf8' } ] }); await ensureOrderDetails(client, orderG, { markers: [{ label: 'Risco G — 9 camadas', color: '#38bdf8' }], components: [{ sku: 'LOCAL-DEMO-MAT-MALHA-PRETA', name: 'DEMO-MALHA-300 MALHA 30/1 COR PRETO', quantityPerUnit: 0.2, totalQuantity: 28, unit: 'kg' }], steps: [ { number: 1, name: 'Corte', startDate: offsetDate(-1), endDate: today, status: 'finished', color: '#22c55e' }, { number: 2, name: 'Costura', startDate: today, endDate: offsetDate(2), status: 'in_progress', color: '#38bdf8' }, { number: 3, name: 'Revisão', startDate: offsetDate(2), endDate: offsetDate(3), status: 'pending', color: '#a78bfa' } ] }); await ensureOrderDetails(client, orderHoodie, { markers: [{ label: 'Risco moletom — 6 camadas', color: '#a78bfa' }], components: [{ sku: 'LOCAL-DEMO-MAT-MOLETOM-GRAFITE', name: 'DEMO-MOLETOM 3 CABOS COR GRAFITE', quantityPerUnit: 0.75, totalQuantity: 45, unit: 'kg' }], steps: [ { number: 1, name: 'Corte', startDate: offsetDate(2), endDate: offsetDate(3), status: 'finished', color: '#22c55e' }, { number: 2, name: 'Costura', startDate: offsetDate(3), endDate: offsetDate(6), status: 'finished', color: '#38bdf8' }, { number: 3, name: 'Revisão', startDate: offsetDate(6), endDate: offsetDate(8), status: 'in_progress', color: '#a78bfa' } ] }); const lotForReservation = lotIds[0]; if (lotForReservation) { await client.query(` INSERT INTO cut_plan_lot_reservations ( cut_plan_reference, production_plan_id, supply_lot_id, material_key, quantity, consumed_quantity, status ) SELECT 'DEMO-CORTE-35', $1, $2, $3, 61.3, 0, 'active' WHERE NOT EXISTS ( SELECT 1 FROM cut_plan_lot_reservations WHERE cut_plan_reference = 'DEMO-CORTE-35' AND supply_lot_id = $2 ); `, [planCut, lotForReservation, normalizeMaterialKey('DEMO-MALHA-300 MALHA 30/1 COR PRETO')]); } const workstations = await client.query('SELECT id, code FROM production_workstations'); const workstationByCode = new Map(workstations.rows.map(row => [row.code, row.id])); const defects = [ [orderM, workstationByCode.get('cutting'), 'Furo na malha', 'Tecido', 2, 'DEMO-NF-004', `${DEMO} — conferir rolo antes de seguir o corte.`], [orderG, workstationByCode.get('sewing'), 'Costura desalinhada', 'Costura', 1, 'DEMO-NF-004', `${DEMO} — ajuste solicitado ao posto de costura.`], [orderHoodie, workstationByCode.get('review'), 'Mancha de acabamento', 'Acabamento', 2, 'DEMO-LT-2408-02', `${DEMO} — lote separado para avaliação de qualidade.`] ]; for (const defect of defects) { await client.query(` INSERT INTO production_order_defects ( production_order_id, workstation_id, defect_type, origin, quantity, lot_code, notes ) SELECT $1, $2, $3, $4, $5, $6, $7 WHERE NOT EXISTS ( SELECT 1 FROM production_order_defects WHERE production_order_id = $1 AND defect_type = $3 AND lot_code = $6 ); `, defect); } await client.query('COMMIT'); console.log('Demonstração de Suprimentos criada/atualizada com sucesso.'); } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); await pool.end(); } } main().catch((error) => { console.error('Não foi possível criar os dados de demonstração:', error.message); process.exitCode = 1; });