This commit is contained in:
2026-10-10 13:47:56 +08:00
parent c4d43542f4
commit e6689f0d0d
8 changed files with 163 additions and 45 deletions
+2 -1
View File
@@ -6,7 +6,8 @@ const page = readFileSync(new URL('../src/pages/relabel/relabel.vue', import.met
test('PDA 箱唛换标明确展示每箱的新标签和 FBA 目的仓', () => {
assert.match(page, /item\.cartonMark[^\n]+item\.fbaCartonMark/)
assert.match(page, /FBA 目的仓:\{\{ item\.destinationFbaWarehouseCode/)
assert.match(page, /class="destination-summary"[^\n]+FBA 目的仓:[^\n]+v-for="destination in fbaDestinations"/)
assert.match(page, /v-if="fbaDestinations.length > 1"[^\n]+item\.destinationFbaWarehouseCode/)
assert.match(page, /confirmedItem\?\.destinationFbaWarehouseCode \|\| item\.destinationFbaWarehouseCode/)
assert.match(page, /请贴 FBA 箱唛[^`]+FBA 目的仓:\$\{this\.lastCarton\.destinationFbaWarehouseCode/s)
})
+12
View File
@@ -0,0 +1,12 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { isRelabelFundsBlocked, relabelErrorMessage } from '../src/utils/relabel-error.js'
test('wallet block receives actionable Chinese guidance without version-conflict recovery', () => {
const error = { code: 'A0409', message: 'Relabel funds must be frozen before processing' }
assert.equal(isRelabelFundsBlocked(error), true)
assert.match(relabelErrorMessage(error), /后台检查客户余额或授信/)
assert.doesNotMatch(relabelErrorMessage(error), /刷新任务版本/)
assert.equal(isRelabelFundsBlocked({ code: 'A0409', message: 'Task version changed' }), false)
assert.equal(relabelErrorMessage({ message: 'Task version changed' }), 'Task version changed')
})
+50 -3
View File
@@ -14,6 +14,7 @@ function context() {
] } }
for (const [key, method] of Object.entries(component.methods)) value[key] = method.bind(value)
for (const [key, method] of Object.entries(component.computed)) Object.defineProperty(value, key, { get: method.bind(value) })
value.confirmBindingChoice = async () => true
value.selectSource({ positionId: '1', palletId: '20', palletCode: 'SOURCE', movablePackages: 2, cartonMarks: ['A', 'B', 'C'] })
return value
}
@@ -58,7 +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\} 已绑定`/)
})
test('source pallet list is concise and hides rows with no movable packages', () => {
@@ -70,7 +71,8 @@ test('source pallet list is concise and hides rows with no movable packages', ()
assert.deepEqual(value.operableSourceRows.map(row => row.palletCode), ['PALLET-1'])
assert.match(source, />托盘</)
assert.match(source, />刷新</)
assert.match(source, /托盘号:\{\{ row\.palletCode \}\} · 入仓码:\{\{ row\.inboundCode \}\}/)
assert.match(source, /class="source-caption">托盘号<\/text><text class="source-code">\{\{ row\.palletCode \}\}/)
assert.doesNotMatch(template, /入仓码|row\.inboundCode/)
assert.match(source, /可拆\{\{ isReturnBulk \? '件数' : '箱数' \}\}:\{\{ row\.movablePackages \}\}/)
assert.doesNotMatch(template, /刷新来源托盘/)
assert.doesNotMatch(template, /按原入仓单拆托/)
@@ -104,10 +106,55 @@ test('target confirmation submits binding directly, with no second submit button
value.bind = async () => { binds++ }
await value.confirmTargetAndBind()
assert.equal(binds, 1)
assert.match(source, /@tap="confirmTargetAndBind"/)
assert.match(source, /@submit="confirmTargetAndBind"/)
assert.doesNotMatch(source, /@tap="bind"/)
})
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 }]
value.sourceCode = 'SOURCE'
let prompts = 0
value.resolve = async () => { value.source = { palletCode: 'SOURCE' } }
value.confirmBindingChoice = async () => { prompts++; return false }
await value.confirmSourceScan()
assert.equal(value.source, null)
value.confirmBindingChoice = async () => { prompts++; return true }
await value.confirmSourceScan()
assert.equal(value.source.palletCode, 'SOURCE')
value.sourceCode = 'OTHER'
value.showBindingMessage = () => {}
value.resolve = async () => assert.fail('unknown pallet must not resolve')
await value.confirmSourceScan()
assert.equal(value.source, null)
assert.equal(prompts, 2)
})
test('declining target confirmation does not move stock', async () => {
const value = context()
value.quantity = '1'; value.source = { palletId: '20', palletCode: 'SOURCE' }; value.targetCode = 'TARGET'
value.resolve = async () => { value.target = { palletId: '30', palletCode: 'TARGET' } }
value.confirmBindingChoice = async () => false
value.bind = async () => assert.fail('cancelled target must not bind')
await value.confirmTargetAndBind()
assert.equal(value.target, null)
})
test('simplified split automatically fills a batch and counts saved bindings without repeats', async () => {
const value = context()
value.task.items = ['A', 'B', 'C'].map(cartonMark => ({ cartonMark, operationType: 'CARTON_LABEL', expectedPackages: 1, relabelledPackages: 1, destinationFbaWarehouseCode: 'ONT8' }))
value.task.history = [{ action: 'PALLET_BIND', detail: JSON.stringify({ cartonMarks: ['A'], targetPallets: ['OLD-TARGET'] }) }]
assert.equal(value.requiredSplitPackages, 3)
assert.equal(value.splitPackages, 1)
value.quantity = '1'; value.marks = []; value.source = { palletId: '20', palletCode: 'SOURCE' }; value.targetCode = 'TARGET'
value.resolve = async () => { value.target = { palletId: '30', palletCode: 'TARGET' } }
value.bind = async () => { assert.deepEqual(value.marks, ['B']); assert.equal(value.readyToBind, true) }
await value.confirmTargetAndBind()
assert.doesNotMatch(template, /按箱数填入本批换标箱|选择本批新目的仓|本批确认|@tap="toggleMark/)
value.task.history.push({ action: 'REWORK_ARCHIVE', detail: '{}' })
assert.equal(value.splitPackages, 0)
})
test('missing source scan shows modal and never submits a binding', async () => {
const value = context(); value.quantity = '1'; value.fillBatch(); value.targetCode = 'TARGET'
let message = ''
+11 -1
View File
@@ -70,7 +70,17 @@ test('parcel signing allows an empty code but still rejects quantities above the
await page.confirmReceipt()
assert.equal(calls.length, 1)
assert.equal(calls[0].trackingNo, undefined)
assert.equal(calls[0].unitCode, '')
assert.equal(calls[0].unitCode, undefined)
assert.equal(calls[0].palletCode, undefined)
})
test('parcel waybill is submitted as trackingNo and leaves unitCode for the server', async () => {
const calls = []
const page = setup(async payload => { calls.push(payload); return { unitType: 'PARCEL' } })
page.unitType = 'PARCEL'; page.unitCode = ' sf561626 '; page.trackingNo = 'stale-from-pallet'
await page.confirmReceipt()
assert.equal(calls[0].trackingNo, 'sf561626')
assert.equal(calls[0].unitCode, undefined)
assert.equal(calls[0].palletCode, undefined)
})