const { pool } = require('../db'); const FAMILY_KEYS = ['BLCS', 'BLOS', 'BLMC', 'BLPM']; const FAMILY_KEY_SET = new Set(FAMILY_KEYS); const normalizeFamilyKey = (value) => { const familyKey = String(value || '').trim().toUpperCase(); return FAMILY_KEY_SET.has(familyKey) ? familyKey : ''; }; const normalizeNumber = (value) => { const number = Number(value); return Number.isFinite(number) && number > 0 ? number : null; }; const normalizeText = (value) => String(value || '').replace(/\s+/g, ' ').trim(); const normalizeFamilyYields = (familyYields = {}) => { return FAMILY_KEYS.reduce((normalized, familyKey) => { const unitsPerRoll = normalizeNumber(familyYields[familyKey]); if (unitsPerRoll) normalized[familyKey] = unitsPerRoll; return normalized; }, {}); }; const normalizeProductOverrides = (productOverrides = {}) => { return Object.entries(productOverrides).reduce((normalized, [productId, override]) => { const normalizedProductId = normalizeText(productId); if (!normalizedProductId || !override || typeof override !== 'object') return normalized; const familyKey = normalizeFamilyKey(override.familyKey); const color = normalizeText(override.color); const size = normalizeText(override.size).toUpperCase(); if (!familyKey && !color && !size) return normalized; normalized[normalizedProductId] = { familyKey, color, size }; return normalized; }, {}); }; const listCuttingSettings = async () => { const [familyResult, overrideResult] = await Promise.all([ pool.query(` SELECT family_key, units_per_roll FROM cutting_family_rules WHERE units_per_roll IS NOT NULL AND units_per_roll > 0 ORDER BY family_key `), pool.query(` SELECT product_id, family_key, color, size FROM cutting_product_overrides ORDER BY product_id `) ]); return { familyYields: familyResult.rows.reduce((settings, row) => { settings[row.family_key] = Number(row.units_per_roll); return settings; }, {}), productOverrides: overrideResult.rows.reduce((settings, row) => { settings[row.product_id] = { familyKey: row.family_key || '', color: row.color || '', size: row.size || '' }; return settings; }, {}) }; }; const saveCuttingSettings = async ({ familyYields = {}, productOverrides = {} }) => { const normalizedFamilyYields = normalizeFamilyYields(familyYields); const normalizedProductOverrides = normalizeProductOverrides(productOverrides); const client = await pool.connect(); try { await client.query('BEGIN'); await client.query('DELETE FROM cutting_family_rules'); for (const [familyKey, unitsPerRoll] of Object.entries(normalizedFamilyYields)) { await client.query(` INSERT INTO cutting_family_rules (family_key, units_per_roll, updated_at) VALUES ($1, $2, CURRENT_TIMESTAMP) `, [familyKey, unitsPerRoll]); } await client.query('DELETE FROM cutting_product_overrides'); for (const [productId, override] of Object.entries(normalizedProductOverrides)) { await client.query(` INSERT INTO cutting_product_overrides (product_id, family_key, color, size, updated_at) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) `, [ productId, override.familyKey || null, override.color || null, override.size || null ]); } await client.query('COMMIT'); return { familyYields: normalizedFamilyYields, productOverrides: normalizedProductOverrides }; } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); } }; module.exports = { FAMILY_KEYS, listCuttingSettings, normalizeFamilyKey, normalizeFamilyYields, normalizeProductOverrides, saveCuttingSettings };