This commit is contained in:
2026-10-12 15:56:16 +08:00
parent aebe4f9528
commit 785b4bca51
4 changed files with 90 additions and 10 deletions
+21
View File
@@ -59,6 +59,7 @@ test('pallet comparison tolerates mixed API versions and canonical code formatti
test('quantity disassembly has no extra carton scanner', () => {
assert.doesNotMatch(source, /relabel-move-carton/)
assert.match(source, /actualSource:/)
assert.match(source, /来源已确认:.*尚未拆出.*绑定成功后才保存拆托明细/)
assert.match(source, /this\.message = `托盘 \$\{this\.target\.palletCode\} 已绑定`/)
})
@@ -107,9 +108,29 @@ test('target confirmation submits binding directly, with no second submit button
await value.confirmTargetAndBind()
assert.equal(binds, 1)
assert.match(source, /@submit="confirmTargetAndBind"/)
assert.match(source, /@tap="confirmTargetAndBind"/)
assert.doesNotMatch(source, /@tap="bind"/)
})
test('mixed carton scan alone explains missing SKU and does not bind; completing SKU allows binding', async () => {
const value = context()
value.task.items = [{ cartonMark: 'A', operationType: 'CARTON_AND_SKU', expectedPackages: 1, relabelledPackages: 0, cartonProcessedPackages: 1, skuProcessedPackages: 0, destinationFbaWarehouseCode: 'ONT8' }]
value.source = { palletCode: 'SOURCE' }; value.targetCode = 'TARGET'; value.quantity = '1'
let message = ''; let binds = 0
value.showBindingMessage = (title, content) => { message = content }
value.resolve = async () => { value.target = { palletCode: 'TARGET' } }
value.bind = async () => { binds++ }
await value.confirmTargetAndBind()
assert.match(message, /SKU 贴标登记/)
assert.equal(binds, 0)
assert.equal(value.target, null)
value.task.items[0].skuProcessedPackages = 1
value.task.items[0].relabelledPackages = 1
assert.equal(value.labelBlockReason, '')
await value.confirmTargetAndBind()
assert.equal(binds, 1)
})
test('source scan asks before accepting a listed pallet and rejects unknown pallets', async () => {
const value = context()
value.sourceRows = [{ positionId: '1', palletCode: 'SOURCE', movablePackages: 2 }]
+42 -3
View File
@@ -3,11 +3,50 @@ import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
const page = await readFile(new URL('../src/pages/relabel/relabel.vue', import.meta.url), 'utf8')
const helper = await readFile(new URL('../src/utils/return-relabel.js', import.meta.url), 'utf8')
const { returnDeductions } = await import('data:text/javascript;base64,' + Buffer.from(helper).toString('base64'))
test('return carton label path requires source confirmation before carton scan', () => {
assert.match(page, /退货箱唛换标必须先确认同一来源包裹\/箱/)
const { returnDeductions, requiresReturnPack, returnPackFacts } = await import('data:text/javascript;base64,' + Buffer.from(helper).toString('base64'))
const body = page.split('<script>')[1].split('</script>')[0].replace(/^\s*import .*$/gm, '').replace('export default', 'return')
const component = new Function('PortalHeader', 'RelabelPalletWork', 'ScanTargetInput', 'ReturnRelabelPackWork', 'requiresReturnPack', 'returnPackFacts', body)({}, {}, {}, {}, requiresReturnPack, returnPackFacts)
test('return carton label identifies source with one carton scan and retains source check for SKU tab', () => {
assert.doesNotMatch(page, /退货箱唛换标必须先确认同一来源包裹\/箱/)
assert.match(page, /v-if="isReturnTask && taskTab !== 'carton'"/)
assert.match(page, /this.activeTarget = value === 'carton' \? 'quick-carton'/)
assert.match(page, /this\.taskTab === 'carton' \? 'quick-carton' : 'quick-sku'/)
})
test('whole carton and full SKU relabel hide new box dimensions; partial SKU keeps isolation form', () => {
const value = { isReturnTask: true, current: { items: [{ returnCartonId: '1', cartonMark: 'C1', operationType: 'CARTON_LABEL', returnQuantity: 15 }], history: [] } }
assert.equal(component.computed.showReturnPack.call(value), false)
value.current.items[0].operationType = 'SKU_LABEL'
value.current.items[0].skuItems = [{ skuCode: 'A', quantity: 15 }]
assert.equal(component.computed.showReturnPack.call(value), false)
value.current.items[0].skuItems[0].quantity = 5
assert.equal(component.computed.showReturnPack.call(value), true)
assert.match(page, /ReturnRelabelPackWork v-if="showReturnPack"/)
})
test('one return carton scan saves label stage without a prior source scan; unrelated carton is rejected', async () => {
const calls = []
const item = { itemId: 'I1', cartonMark: 'C1', operationType: 'CARTON_LABEL', fbaCartonMark: 'FBA1' }
const result = { relabelNo: 'RL1', version: 1, items: [{ ...item, relabelledPackages: 1 }] }
const subject = new Function('PortalHeader', 'RelabelPalletWork', 'ScanTargetInput', 'ReturnRelabelPackWork', 'scanRelabelCarton', 'warehouseIdempotencyKey', 'releaseWarehouseKey', 'uni', body)(
{}, {}, {}, {},
async (no, payload) => { calls.push({ no, payload }); return result },
() => ({ value: 'KEY', signature: 'SIG' }), () => {},
{ showModal: options => options.complete() }
)
const value = {
busy: false, current: { relabelNo: 'RL1', version: 0, items: [item] }, verifiedReturnCarton: '',
run: async (action, work) => work(), applyCurrent(task) { this.current = task },
setError(error) { this.error = error.message }
}
await subject.methods.submitCarton.call(value, ' C1\n')
assert.deepEqual(calls, [{ no: 'RL1', payload: { cartonMark: 'C1', expectedVersion: 0, idempotencyKey: 'KEY' } }])
assert.equal(value.current.version, 1)
assert.equal(value.lastCarton.fbaCartonMark, 'FBA1')
await subject.methods.submitCarton.call(value, 'OTHER')
assert.equal(calls.length, 1)
assert.match(value.error, /不属于本单/)
})
test('mixed return carton keeps one physical package while SKU deductions use pieces', () => {
const task={items:[{returnCartonId:'1',cartonMark:'P1',operationType:'CARTON_AND_SKU',returnQuantity:5,skuItems:[{skuCode:'S',quantity:5}]}]}
assert.deepEqual(returnDeductions(task),[{cartonMark:'P1',skuCode:'S',quantity:5}])