import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { createRequire } from 'node:module' import ts from 'typescript' const view = readFileSync(new URL('./WarehouseServiceView.vue', import.meta.url), 'utf8') const { default: Schema } = createRequire(import.meta.resolve('element-plus'))('async-validator') test('新增废弃逐字段校验仓库、来源单和原因,备注不影响提交资格', async () => { const source = view.slice(view.indexOf('const disposalFormRules:'), view.indexOf('const labelingFormRef =')) const code = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText const rules = new Function(code + '\nreturn disposalFormRules')() const validator = new Schema(rules) await assert.rejects(validator.validate({ warehouse: '', sourceOrder: '', reason: '' }), error => { assert.deepEqual(Object.keys(error.fields).sort(), ['reason', 'sourceOrder', 'warehouse']) return true }) const form = { warehouse: 'WH-A', sourceOrder: 'document:IN-A', reason: '其他', remark: '' } await validator.validate(form) await assert.rejects(validator.validate({ ...form, sourceOrder: '' }), error => Boolean(error.fields.sourceOrder)) await assert.rejects(validator.validate({ ...form, reason: ' ' }), error => Boolean(error.fields.reason)) }) test('新增废弃校验失败不进入库存提交判断,其他页面不调用废弃表单校验', async () => { const guard = view.match(/ if \(isDisposalCreate\.value && !await labelingFormRef[^\n]+/)[0] let validations = 0 const submit = new Function('isDisposalCreate', 'labelingFormRef', 'return async () => {' + guard + '\nreturn "continue"\n}') const formRef = { value: { validate: async () => { validations++; throw new Error('required') } } } assert.equal(await submit({ value: true }, formRef)(), undefined) assert.equal(validations, 1) assert.equal(await submit({ value: false }, formRef)(), 'continue') assert.equal(validations, 1) formRef.value.validate = async () => true assert.equal(await submit({ value: true }, formRef)(), 'continue') }) test('货物废弃先选择海外操作仓,再加载并选择该仓入库单', () => { assert.match(view, /props\.service === 'disposal'.*操作仓.*v-model="form\.warehouse".*@change="selectDiscardWarehouse"/s) assert.match(view, /props\.service === 'disposal'.*选择入库单.*:disabled="!form\.warehouse".*@change="selectDiscardInbound"/s) assert.match(view, /async function selectDiscardWarehouse\(\).*selectedDiscardOrderId\.value = ''.*cargoRows\.value = \[\].*listDiscardCandidates\(\)/s) assert.match(view, /\|\| !primaryWarehouse \|\| invalidCargo/) }) test('无箱唛按数量废弃时备注必填,选择箱唛废弃时备注可选', () => { assert.match(view, /props\.service === 'disposal' && discardQuantityMode\.value && !form\.remark\.trim\(\)/) assert.match(view, /请填写废弃备注/) assert.match(view, /props\.service === 'disposal' && discardQuantityMode \? '备注 \*' : '备注'/) }) test('废弃来源包含通用仓位,选择后带出全部库存且不把选择键提交为订单ID', () => { const body = view.match(/function selectDiscardInbound\(orderId: string\) \{([\s\S]*?)\n\}/)[1] const select = new Function('discardRequestedPackages', 'discardSelectedBoxKeys', 'discardBoxSearch', 'selectAllDiscardBoxes', 'discardDocuments', 'cargoRows', 'form', 'cargoSequence', 'orderId', body) const cargoRows = { value: [] }; const form = {} const source = { orderId: 'inventory:42', sourceType: 'INVENTORY', inventoryPositionId: '42', overseasWarehouseId: '99', inboundCode: 'WT-1', skus: [{ skuCode: 'A', productName: '商品', availablePackages: 100 }] } select({ value: 9 }, { value: [] }, { value: '' }, () => {}, { value: [{ ...source, orderId: 'document:WT-1', sources: [source] }] }, cargoRows, form, 0, 'document:WT-1') assert.equal(cargoRows.value[0].quantity, 100) assert.equal(cargoRows.value[0].sourceInventoryPositionId, '42') assert.equal(form.inboundNo, 'WT-1') assert.match(view, /orderId: item.sourceType === 'DROPSHIP' \? item.sourceOrderId : undefined/) assert.match(view, /inventoryPositionId: item.sourceType === 'INVENTORY'/) }) function selectionRows(boxes, selectedKeys) { const body = view.match(/function syncDiscardSelection\(\) \{([\s\S]*?)\n\}/)[1].replace('new Map()', 'new Map()') const rows = { value: [] } new Function('discardSourceBoxes', 'discardSelectedBoxKeys', 'cargoRows', 'createCargoRow', body)({ value: boxes }, { value: selectedKeys }, rows, () => ({})) return rows.value } test('部分废弃只提交勾选的箱子,数量由箱子汇总,清空后无明细', () => { const source = { orderId: 'inventory:42', inventoryPositionId: '42', sourceType: 'INVENTORY', availablePackages: 3, skus: [{ skuCode: 'SKU', productName: '商品' }] } const boxes = ['1', '2', '3'].map(id => ({ key: `INVENTORY:${id}`, cartonId: id, cartonMark: `BOX-${id}`, source, skus: source.skus })) const rows = selectionRows(boxes, ['INVENTORY:2']) assert.equal(rows.length, 1); assert.equal(rows[0].quantity, 1) assert.deepEqual(rows[0].discardCartonIds, ['2']); assert.equal(rows[0].sourceInventoryPositionId, '42') assert.equal(selectionRows(boxes, []).length, 0) }) test('混合SKU箱按实物箱只计一次,退货箱仍整箱携带全部SKU', () => { const skus = [{ skuCode: 'A', availablePackages: 2 }, { skuCode: 'B', availablePackages: 3 }] const dropship = { orderId: '42', sourceType: 'DROPSHIP', availablePackages: 1, skus } const rows = selectionRows([{ key: 'DROPSHIP:1', cartonId: '1', source: dropship, skus }], ['DROPSHIP:1']) assert.equal(rows.length, 1); assert.equal(rows[0].quantity, 1); assert.deepEqual(rows[0].discardCartonIds, ['1']) const returned = { orderId: 'return:2', returnCartonId: '2', sourceType: 'RETURN_CARTON', availablePackages: 5, skus } const returnRows = selectionRows([{ key: 'RETURN_CARTON:2', cartonId: '2', source: returned, skus }], ['RETURN_CARTON:2']) assert.deepEqual(returnRows.map(row => row.quantity), [2, 3]); assert.ok(returnRows.every(row => row.returnCartonId === '2' && row.discardCartonIds === undefined)) }) test('通用混箱同仓位保留全部SKU,跨仓位相同SKU保持独立来源', () => { const skus = [{ skuCode: 'A', availablePackages: 1 }, { skuCode: 'B', availablePackages: 1 }] const source = { orderId: null, inventoryPositionId: '42', sourceType: 'INVENTORY', availablePackages: 1, skus } const boxes = [{ key: 'INVENTORY:1', cartonId: '1', source, skus }, { key: 'INVENTORY:2', cartonId: '2', source: { ...source, inventoryPositionId: '43' }, skus: [skus[0]] }] const rows = selectionRows(boxes, ['INVENTORY:1', 'INVENTORY:2']) assert.equal(rows.length, 3) assert.deepEqual(rows.map(row => [row.sourceInventoryPositionId, row.sku, row.quantity, row.discardCartonIds]), [['42', 'A', 1, ['1']], ['42', 'B', 1, ['1']], ['43', 'A', 1, ['2']]]) })