Add manual SKU planning overrides
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m18s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m18s
This commit is contained in:
@@ -136,6 +136,8 @@ const initDB = async () => {
|
||||
family_key VARCHAR(20),
|
||||
color VARCHAR(100),
|
||||
size VARCHAR(40),
|
||||
product_type VARCHAR(40),
|
||||
planning_notes TEXT,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`);
|
||||
@@ -270,6 +272,12 @@ const initDB = async () => {
|
||||
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
||||
`).catch(() => {});
|
||||
|
||||
await pool.query(`
|
||||
ALTER TABLE cutting_product_overrides
|
||||
ADD COLUMN IF NOT EXISTS product_type VARCHAR(40),
|
||||
ADD COLUMN IF NOT EXISTS planning_notes TEXT;
|
||||
`).catch(() => {});
|
||||
|
||||
await pool.query(`
|
||||
ALTER TABLE catalog_categories
|
||||
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
||||
|
||||
@@ -2,6 +2,21 @@ const { pool } = require('../db');
|
||||
|
||||
const FAMILY_KEYS = ['BLCS', 'BLOS', 'BLMC', 'BLPM'];
|
||||
const FAMILY_KEY_SET = new Set(FAMILY_KEYS);
|
||||
const PRODUCT_TYPE_KEYS = [
|
||||
'finished_apparel',
|
||||
'finished_accessory',
|
||||
'raw_material',
|
||||
'packaging',
|
||||
'dtf_input',
|
||||
'dtf_service',
|
||||
'kit_bundle',
|
||||
'service',
|
||||
'machine_part',
|
||||
'equipment',
|
||||
'ignore_from_planning',
|
||||
'unknown'
|
||||
];
|
||||
const PRODUCT_TYPE_KEY_SET = new Set(PRODUCT_TYPE_KEYS);
|
||||
|
||||
const normalizeFamilyKey = (value) => {
|
||||
const familyKey = String(value || '').trim().toUpperCase();
|
||||
@@ -15,6 +30,11 @@ const normalizeNumber = (value) => {
|
||||
|
||||
const normalizeText = (value) => String(value || '').replace(/\s+/g, ' ').trim();
|
||||
|
||||
const normalizeProductType = (value) => {
|
||||
const productType = normalizeText(value);
|
||||
return PRODUCT_TYPE_KEY_SET.has(productType) ? productType : '';
|
||||
};
|
||||
|
||||
const normalizeFamilyYields = (familyYields = {}) => {
|
||||
return FAMILY_KEYS.reduce((normalized, familyKey) => {
|
||||
const unitsPerRoll = normalizeNumber(familyYields[familyKey]);
|
||||
@@ -31,13 +51,17 @@ const normalizeProductOverrides = (productOverrides = {}) => {
|
||||
const familyKey = normalizeFamilyKey(override.familyKey);
|
||||
const color = normalizeText(override.color);
|
||||
const size = normalizeText(override.size).toUpperCase();
|
||||
const productType = normalizeProductType(override.productType);
|
||||
const planningNotes = normalizeText(override.planningNotes);
|
||||
|
||||
if (!familyKey && !color && !size) return normalized;
|
||||
if (!familyKey && !color && !size && !productType && !planningNotes) return normalized;
|
||||
|
||||
normalized[normalizedProductId] = {
|
||||
familyKey,
|
||||
color,
|
||||
size
|
||||
size,
|
||||
productType,
|
||||
planningNotes
|
||||
};
|
||||
|
||||
return normalized;
|
||||
@@ -53,7 +77,7 @@ const listCuttingSettings = async () => {
|
||||
ORDER BY family_key
|
||||
`),
|
||||
pool.query(`
|
||||
SELECT product_id, family_key, color, size
|
||||
SELECT product_id, family_key, color, size, product_type, planning_notes
|
||||
FROM cutting_product_overrides
|
||||
ORDER BY product_id
|
||||
`)
|
||||
@@ -68,7 +92,9 @@ const listCuttingSettings = async () => {
|
||||
settings[row.product_id] = {
|
||||
familyKey: row.family_key || '',
|
||||
color: row.color || '',
|
||||
size: row.size || ''
|
||||
size: row.size || '',
|
||||
productType: row.product_type || '',
|
||||
planningNotes: row.planning_notes || ''
|
||||
};
|
||||
return settings;
|
||||
}, {})
|
||||
@@ -94,13 +120,15 @@ const saveCuttingSettings = async ({ familyYields = {}, productOverrides = {} })
|
||||
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)
|
||||
INSERT INTO cutting_product_overrides (product_id, family_key, color, size, product_type, planning_notes, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP)
|
||||
`, [
|
||||
productId,
|
||||
override.familyKey || null,
|
||||
override.color || null,
|
||||
override.size || null
|
||||
override.size || null,
|
||||
override.productType || null,
|
||||
override.planningNotes || null
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -123,6 +151,7 @@ module.exports = {
|
||||
listCuttingSettings,
|
||||
normalizeFamilyKey,
|
||||
normalizeFamilyYields,
|
||||
normalizeProductType,
|
||||
normalizeProductOverrides,
|
||||
saveCuttingSettings
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ const test = require('node:test');
|
||||
const {
|
||||
normalizeFamilyKey,
|
||||
normalizeFamilyYields,
|
||||
normalizeProductType,
|
||||
normalizeProductOverrides
|
||||
} = require('../services/cuttingSettingsService');
|
||||
|
||||
@@ -27,14 +28,23 @@ test('normalizeFamilyYields keeps positive numeric yield rules', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('normalizeProductType accepts only known planning product types', () => {
|
||||
assert.equal(normalizeProductType('finished_apparel'), 'finished_apparel');
|
||||
assert.equal(normalizeProductType('raw_material'), 'raw_material');
|
||||
assert.equal(normalizeProductType('finished_product'), '');
|
||||
assert.equal(normalizeProductType('unknown type'), '');
|
||||
});
|
||||
|
||||
test('normalizeProductOverrides trims and removes empty overrides', () => {
|
||||
assert.deepEqual(normalizeProductOverrides({
|
||||
' SKU-1 ': { familyKey: 'blcs', color: ' Preto ', size: ' m ' },
|
||||
' SKU-1 ': { familyKey: 'blcs', color: ' Preto ', size: ' m ', productType: 'finished_apparel', planningNotes: ' revisar corte ' },
|
||||
'SKU-2': { familyKey: 'OUTROS', color: '', size: '' },
|
||||
'SKU-3': { familyKey: '', color: ' Branco ', size: '' },
|
||||
'SKU-4': null
|
||||
'SKU-4': { productType: 'raw_material' },
|
||||
'SKU-5': null
|
||||
}), {
|
||||
'SKU-1': { familyKey: 'BLCS', color: 'Preto', size: 'M' },
|
||||
'SKU-3': { familyKey: '', color: 'Branco', size: '' }
|
||||
'SKU-1': { familyKey: 'BLCS', color: 'Preto', size: 'M', productType: 'finished_apparel', planningNotes: 'revisar corte' },
|
||||
'SKU-3': { familyKey: '', color: 'Branco', size: '', productType: '', planningNotes: '' },
|
||||
'SKU-4': { familyKey: '', color: '', size: '', productType: 'raw_material', planningNotes: '' }
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user