Files
pda-overseas-uniapp/tests/relabel-return-bulk.test.mjs
T
2026-10-12 18:01:55 +08:00

75 lines
7.0 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
const source = await readFile(new URL('../src/pages/relabel/relabel.vue', import.meta.url), 'utf8')
const helperSource = await readFile(new URL('../src/utils/return-relabel.js', import.meta.url), 'utf8')
const helpers = await import('data:text/javascript;base64,' + Buffer.from(helperSource).toString('base64'))
const body = source.split('<script>')[1].split('</script>')[0].replace(/^\s*import .*$/gm, '').replace('export default', 'return')
function setup(confirm = false) {
const calls = [], errors = []
const uni = { showModal(options) { options.success({ confirm }) } }
const component = new Function('PortalHeader', 'RelabelPalletWork', 'ScanTargetInput', 'ReturnRelabelPackWork', 'requiresReturnPack', 'returnPackFacts', 'completeReturnRelabelSku', 'warehouseIdempotencyKey', 'releaseWarehouseKey', 'uni', body)({}, {}, {}, {}, helpers.requiresReturnPack, helpers.returnPackFacts, async (no, payload) => { calls.push(payload); return value.current }, () => ({ value: 'key', signature: 'signature' }), () => {}, uni)
const item = (id, mark) => ({ itemId: id, returnCartonId: id, cartonMark: mark, returnQuantity: 10, operationType: 'RETURN_BULK_SKU_LABEL', skuCode: 'SKU', newSkuCode: 'TARGET', expectedPackages: 4, relabelledPackages: 0, skippedPackages: 0, exceptionPackages: 0 })
const value = { current: { relabelNo: 'RL', version: 1, items: [item('1', 'BOX-A'), item('2', 'BOX-B')], history: [] }, busy: false, allProcessed: true, remark: '', photoFileIds: ['photo'], returnCartonCode: '', verifiedReturnCarton: '', returnSkuCode: '', bulkQuantities: {}, returnTargetLabels: {}, setError(error) { errors.push(error.message) }, applyCurrent(result) { this.current = result }, async run(action, work) { await work() } }
for (const [name, fn] of Object.entries(component.methods)) if (!value[name]) value[name] = fn.bind(value)
for (const name of ['isReturnTask', 'isReturnBulk', 'returnBulkSources', 'returnBulkSkuGroups', 'requiresPalletBinding', 'palletTask', 'completionBlockReason']) Object.defineProperty(value, name, { get: component.computed[name].bind(value) })
return { value, calls, errors }
}
test('SKU alone cannot register or allocate unscanned sources', async () => {
const { value, calls, errors } = setup()
value.verifyReturnSku('SKU'); assert.match(errors.pop(), /先确认来源/)
value.verifyReturnCarton('BOX-A'); value.verifyReturnSku('SKU')
assert.deepEqual(value.returnBulkSources.map(item => item.cartonMark), ['BOX-A'])
value.returnTargetLabels['2'] = 'TARGET'; value.bulkQuantities['2'] = '4'
await value.completeBulkSku(value.current.items[1]); assert.equal(calls.length, 0)
})
test('quantity is explicit, integral and source bounded; wrong target cannot write', async () => {
const { value, calls } = setup(); value.verifyReturnCarton('BOX-A'); value.verifyReturnSku('SKU')
const item = value.current.items[0]; value.returnTargetLabels['1'] = 'TARGET'
for (const quantity of ['', '1.5', '5', '0']) { value.bulkQuantities['1'] = quantity; await value.completeBulkSku(item) }
assert.equal(calls.length, 0)
value.bulkQuantities['1'] = '2'; value.returnTargetLabels['1'] = 'WRONG'; await value.completeBulkSku(item); assert.equal(calls.length, 0)
value.returnTargetLabels['1'] = 'TARGET'; await value.completeBulkSku(item)
assert.deepEqual(calls, [{ itemId: '1', cartonMark: 'BOX-A', skuCode: 'SKU', quantity: 2, expectedVersion: 1, idempotencyKey: 'key' }])
})
test('all action needs confirmation and rescanning clears quantity and target', async () => {
for (const confirm of [false, true]) { const { value } = setup(confirm); value.verifyReturnCarton('BOX-A'); await value.fillAllReturn(value.current.items[0]); assert.equal(value.bulkQuantities['1'], confirm ? '4' : undefined); value.verifyReturnCarton('BOX-B'); assert.deepEqual(value.bulkQuantities, {}); assert.deepEqual(value.returnTargetLabels, {}) }
})
test('normal return SKU rejects incomplete approved target before server scan', async () => {
const { value, errors } = setup(); const item = { ...value.current.items[0], operationType: 'SKU_LABEL', expectedPackages: 1, skuItems: [{ skuCode: 'SKU', newSkuCode: 'TARGET', quantity: 3 }] }
value.current.items = [item]; value.pendingSkuRows = [{ item, sku: item.skuItems[0], index: 0 }]
item.skuItems[0].newSkuCode = ''; value.quickSku = 'SKU'; await value.commitSku(value.pendingSkuRows[0]); assert.match(errors.pop(), /申请件数缺失/)
})
test('packing uses SKU pieces instead of box count and preserves both sources', () => {
const task = { items: [{ returnCartonId: '1', cartonMark: 'A', operationType: 'SKU_LABEL', expectedPackages: 1, returnQuantity: 8, skuItems: [{ skuCode: 'SKU', quantity: 3 }] }, { returnCartonId: '2', cartonMark: 'B', operationType: 'SKU_LABEL', expectedPackages: 1, returnQuantity: 2, skuItems: [{ skuCode: 'SKU', quantity: 2 }] }] }
assert.deepEqual(helpers.returnDeductions(task), [{ cartonMark: 'A', skuCode: 'SKU', quantity: 3 }, { cartonMark: 'B', skuCode: 'SKU', quantity: 2 }]); assert.equal(helpers.requiresReturnPack(task), true)
task.items[0].returnQuantity = 3; assert.equal(helpers.requiresReturnPack(task), false)
})
test('only truck requires return pallet binding and counts physical new boxes', () => {
const { value } = setup(); for (const method of [null, 'EXPRESS', 'EXPRESS_PICKUP', 'TRUCK']) { value.current.deliveryMethod = method; assert.equal(value.requiresPalletBinding, method === 'TRUCK') }
value.current.items.forEach(item => { item.cartonMark = 'NEW'; item.relabelledPackages = item.expectedPackages })
assert.equal(value.palletTask.items.length, 1); assert.equal(value.palletTask.items[0].expectedPackages, 1)
})
test('pack recovery ignores previous rework round', () => {
const task = { reviewedAt: 'ROUND2', history: [{ action: 'RETURN_PACK', detail: JSON.stringify({ round: 'INITIAL', cartonMark: 'OLD' }) }, { action: 'RETURN_PACK', detail: JSON.stringify({ round: 'ROUND2', cartonMark: 'NEW' }) }] }
assert.deepEqual(helpers.returnPackFacts(task).map(row => row.cartonMark), ['NEW'])
})
test('partial return completion requires current-round independent box then truck binding and photo', () => {
const { value } = setup()
value.current.items.forEach(item => { item.relabelledPackages = item.expectedPackages })
value.current.relabelledPackages = 8
value.current.deliveryMethod = 'EXPRESS'
assert.match(value.completionBlockReason, /独立新箱/)
value.current.history.push({ action: 'RETURN_PACK', detail: JSON.stringify({ round: 'INITIAL', cartonMark: 'NEW' }) })
value.current.items.forEach(item => { item.cartonMark = 'NEW'; item.returnQuantity = 8 })
assert.equal(value.completionBlockReason, '')
value.current.deliveryMethod = 'TRUCK'
assert.match(value.completionBlockReason, /组托/)
value.current.history.push({ action: 'PALLET_BIND', detail: JSON.stringify({ round: 'INITIAL', cartonMarks: ['NEW'] }) })
assert.equal(value.completionBlockReason, '')
value.photoFileIds = []
assert.match(value.completionBlockReason, /照片/)
})