96 lines
3.2 KiB
JavaScript
96 lines
3.2 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const { _private } = require('../src/services/mkhBomService');
|
|
|
|
test('business MKH BOM rows are grouped for drill-down and match codes without separators', () => {
|
|
const bom = _private.groupBusinessBomRows([
|
|
{
|
|
comboSkuId: 'MKH-DNY004624N1015V01-1-DNZ038569N1280V01-1',
|
|
componentSkuId: 'DNY004624N1015V01',
|
|
componentName: '配件 A',
|
|
qty: 1,
|
|
},
|
|
{
|
|
comboSkuId: 'MKH-DNY004624N1015V01-1-DNZ038569N1280V01-1',
|
|
componentSkuId: 'DNZ038569N1280V01',
|
|
componentName: '配件 B',
|
|
qty: 2,
|
|
},
|
|
]);
|
|
|
|
const matched = _private.matchComponents(
|
|
bom,
|
|
'MKHDNY004624N1015V011DNZ038569N1280V011'
|
|
);
|
|
|
|
assert.equal(bom.entries.length, 1);
|
|
assert.equal(matched.matchType, 'sku');
|
|
assert.deepEqual(
|
|
matched.components.map((item) => [item.barcode, item.name, item.qty]),
|
|
[
|
|
['DNY004624N1015V01', '配件 A', 1],
|
|
['DNZ038569N1280V01', '配件 B', 2],
|
|
]
|
|
);
|
|
});
|
|
|
|
test('duplicate business BOM component rows are consolidated without losing quantity', () => {
|
|
const bom = _private.groupBusinessBomRows([
|
|
{ comboSkuId: 'MKH-A-1', componentSkuId: 'PART-1', qty: 1 },
|
|
{ comboSkuId: 'MKH-A-1', componentSkuId: 'PART-1', qty: 2 },
|
|
]);
|
|
|
|
const matched = _private.matchComponents(bom, 'MKHA1');
|
|
assert.equal(matched.components.length, 1);
|
|
assert.equal(matched.components[0].qty, 3);
|
|
});
|
|
|
|
test('business BOM takes precedence while product database BOM remains a fallback', () => {
|
|
const businessBom = _private.groupBusinessBomRows([
|
|
{ comboSkuId: 'MKH-A-1', componentSkuId: 'BUSINESS-PART', qty: 1 },
|
|
]);
|
|
const productBom = {
|
|
bySku: new Map([['MKHA1', [{ barcode: 'PRODUCT-PART', qty: 1 }]]]),
|
|
bySpuPrefix: [],
|
|
entries: [],
|
|
};
|
|
|
|
const matched = _private.matchBomSources(businessBom, productBom, 'MKHA1');
|
|
assert.equal(matched.dataSource, 'business_bom');
|
|
assert.equal(matched.components[0].barcode, 'BUSINESS-PART');
|
|
});
|
|
|
|
test('BOM-only MKH products remain visible as drill-down rows without a jst_sku record', () => {
|
|
const bom = _private.groupBusinessBomRows([
|
|
{ comboSkuId: 'MKH-A-1', componentSkuId: 'PART-1', qty: 2 },
|
|
{ comboSkuId: 'MKH-B-1', componentSkuId: 'PART-2', qty: 1 },
|
|
]);
|
|
bom.existingSkuKeys = new Set(['MKHA1']);
|
|
|
|
const extras = _private.buildBusinessBomExtraRows(bom);
|
|
assert.deepEqual(extras.map((row) => row.skuId), ['MKH-B-1']);
|
|
assert.equal(extras[0].source, 'business_bom');
|
|
assert.equal(extras[0].accessoryQtySum, 1);
|
|
});
|
|
|
|
test('extra rows from multiple BOM sources are de-duplicated by normalized MKH code', () => {
|
|
const rows = _private.mergeExtraRows(
|
|
[{ skuId: 'MKH-A-1', source: 'business_bom' }],
|
|
[{ skuId: 'MKHA1', source: 'product_db' }, { skuId: 'MKH-B-1', source: 'product_db' }]
|
|
);
|
|
|
|
assert.deepEqual(rows.map((row) => row.skuId), ['MKH-A-1', 'MKH-B-1']);
|
|
});
|
|
|
|
test('BOM-only rows honor the same keyword filter as jst_sku rows', () => {
|
|
const rows = _private.filterExtraRows([
|
|
{ skuId: 'MKH-A-1', productName: '组合 A' },
|
|
{ skuId: 'MKH-B-1', productName: '组合 B' },
|
|
], { keyword: 'b-1' });
|
|
|
|
assert.deepEqual(rows.map((row) => row.skuId), ['MKH-B-1']);
|
|
});
|