'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']); });