From b8c9fddf8e166c8ed38acaec528a0af8b12291b1 Mon Sep 17 00:00:00 2001
From: liuyihe <2513762445@qq.com>
Date: Fri, 16 Oct 2026 18:26:06 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/RelabelPalletWork.vue | 62 +++++++++------------
tests/relabel-pallet-measurement.test.mjs | 67 ++++++++++++++---------
tests/relabel-pallet-work.test.mjs | 6 +-
3 files changed, 71 insertions(+), 64 deletions(-)
diff --git a/src/components/RelabelPalletWork.vue b/src/components/RelabelPalletWork.vue
index 033c253..9b1a5df 100644
--- a/src/components/RelabelPalletWork.vue
+++ b/src/components/RelabelPalletWork.vue
@@ -5,20 +5,17 @@
- 已绑定箱唛:选择后扫描新目标托盘可改绑(已排车或出库不可直接改绑)。
{{ pallet.palletCode }} · {{ pallet.totalPackages }} 箱 · 体积 {{ pallet.volumeCbm == null ? '待补测' : pallet.volumeCbm + ' m³' }} · 重量 {{ pallet.grossWeightKg == null ? '待补测' : pallet.grossWeightKg + ' kg' }}
{{ pallet.palletCode }} · {{ pallet.packages }} 箱
- {{ pallet.cartonAggregated ? '按当前托盘内实测箱自动汇总,移出扣减、移入增加。' : pallet.inherited ? '原托盘货物未变,沿用来源测量。' : '请核对托盘测量数据。' }}
{{ pallet.inherited ? '来源汇总' : '已确认' }}:{{ pallet.volumeCbm }} m³ · {{ pallet.grossWeightKg }} kg
来源箱规(长 × 宽 × 高)
{{ spec.lengthCm }} × {{ spec.widthCm }} × {{ spec.heightCm }} cm · {{ spec.grossWeightKg }} kg/箱 · {{ spec.cartons }} 箱
原整托尺寸:{{ pallet.lengthCm }} × {{ pallet.widthCm }} × {{ pallet.heightCm }} cm
- 来源按箱测量,未测整托长宽高。
@@ -35,32 +32,20 @@
当前没有可操作的来源托盘或来源箱
- 来源已确认:{{ source.palletCode }};尚未拆出,请完成贴标登记、核对拆出数量并扫描目标托盘,绑定成功后才保存拆托明细。
+ 来源已确认:{{ source.palletCode }}
- 本次{{ wholeSource ? '整托换标数量' : '拆出件数' }}(可分多个来源托盘,逐批提交)
{{ selectedSource.palletCode }}:{{ wholeSource ? '整托换标' : '部分拆箱' }} {{ quantity || 0 }} 件 → {{ selectedDestination || '目的仓待维护' }}
- 贴标数量等于本托盘全部 {{ selectedSource.stockPackages }} 件,无需填写拆箱数量;直接新增带目的仓的托盘标签并绑定。
- 贴标数量少于本托盘库存,请按现场实际选择来源托盘并填写拆箱数量;本托盘应拆 {{ plannedPackages }} 件。
- 退货换标按整托绑定,不支持拆箱;托盘仍会展示用于核对。
- 退货换标无需扫描箱唛。
+
本次拆出箱数箱
-
- 来源有多种箱规,请选择本次移出的实测箱,系统自动扣减体积和重量。
-
- {{ mark }}:{{ measuredBoxLabel(mark) }}
-
-
本次需换标拆 {{ requiredSplitPackages }} 箱已拆 {{ splitPackages }} 箱
- {{ labelBlockReason }}
- 已识别目标:{{ target.palletCode }},尚未绑定成功
托盘 {{ newPallet.palletCode }} 待绑定
{{ message }}{{ error }}
@@ -74,11 +59,11 @@ export default {
components: { ScanTargetInput },
props: { task: { type: Object, required: true }, allowDisassemble: { type: Boolean, default: true } },
emits: ['activate', 'busy', 'saved'],
- data() { return { measurements: [], measuredSelections: {}, measurementFields: [{ key: 'lengthCm', label: '长 cm' }, { key: 'widthCm', label: '宽 cm' }, { key: 'heightCm', label: '高 cm' }, { key: 'grossWeightKg', label: '毛重 kg' }], palletTotals: {}, rebindSelection: null, active: 'source', sourceCode: '', targetCode: '', source: null, target: null, marks: [], busy: false, error: '', message: '', newPallet: null, sourceRows: [], selectedSource: null, quantity: '', selectedDestination: '' } },
+ data() { return { measurements: [], measurementFields: [{ key: 'lengthCm', label: '长 cm' }, { key: 'widthCm', label: '宽 cm' }, { key: 'heightCm', label: '高 cm' }, { key: 'grossWeightKg', label: '毛重 kg' }], palletTotals: {}, rebindSelection: null, active: 'source', sourceCode: '', targetCode: '', source: null, target: null, marks: [], busy: false, error: '', message: '', newPallet: null, sourceRows: [], selectedSource: null, quantity: '', selectedDestination: '' } },
mounted() { this.loadSources() },
computed: {
measuredBoxChoices() { return this.measurements.find(p => p.palletCode === this.selectedSource?.palletCode)?.cartons || [] },
- requiresMeasuredBoxChoice() { return new Set(this.measuredBoxChoices.map(box => [box.lengthCm,box.widthCm,box.heightCm,box.grossWeightKg].join(':'))).size > 1 },
+ requiresMeasuredBoxMapping() { return new Set(this.measuredBoxChoices.map(box => [box.lengthCm,box.widthCm,box.heightCm,box.grossWeightKg].join(':'))).size > 1 },
canRetainOriginal() { return !this.isReturnBulk && !this.unboundSourceMode && this.source && this.selectedSource && this.plannedPackages === Number(this.source.totalPackages) },
boundPalletTotals() { const codes = new Set(this.currentBindings.map(binding => binding.pallet)); return Object.values(this.palletTotals).filter(pallet => codes.has(pallet.palletCode) && !this.measurements.some(measured => measured.palletCode === pallet.palletCode)) },
allBindingsComplete() {
@@ -111,8 +96,8 @@ export default {
const pending = this.sourceItems().filter(item => Number(item.relabelledPackages || 0) < Number(item.expectedPackages || 1))
if (!pending.length) return ''
const missingSku = pending.some(item => item.operationType === 'CARTON_AND_SKU' && Number(item.skuProcessedPackages || 0) < Number(item.expectedPackages || 1))
- if (missingSku) return '本托盘还有未完成的 SKU 贴标登记。混合换标须完成箱唛和 SKU 两项,请返回 SKU 页签处理后再确认绑定。'
- return '本托盘还有未完成的换标明细,请先完成贴标登记,再确认目标托盘绑定。'
+ if (missingSku) return '请先完成 SKU 贴标登记'
+ return '请先完成贴标登记'
},
wholeSource() { return this.isReturnBulk && this.selectedSource && (!this.allowDisassemble || this.plannedPackages === Number(this.selectedSource.stockPackages || 0)) },
unboundSourceMode() { return !this.isReturnBulk && this.returnCartonItems.length > 0 && (this.selectedSource ? (!this.selectedSource.palletCode || this.selectedSource.palletCode === 'UNBOUND') : !this.operableSourceRows.some(row => row.palletCode && row.palletCode !== 'UNBOUND')) },
@@ -121,16 +106,21 @@ export default {
methods: {
prepareMeasuredBatch(event) {
this.quantity = String(event.detail.value)
- this.marks = []; this.measuredSelections = {}
+ this.marks = []
const count = Number(this.quantity)
if (this.measuredBoxChoices.length && Number.isSafeInteger(count) && count > 0 && count <= this.selectedSource.movablePackages && count <= this.eligibleItems.length) this.fillBatch()
},
- measuredBoxOptions(mark) {
- const used = Object.entries(this.measuredSelections || {}).filter(([key]) => key !== mark && this.marks.includes(key)).map(([,value]) => value)
- return this.measuredBoxChoices.filter(box => !used.includes(box.cartonMark)).map(box => ({ ...box, label: `${box.cartonMark} · ${box.lengthCm}×${box.widthCm}×${box.heightCm} cm · ${box.grossWeightKg} kg` }))
+ // 按申请时的仓库箱唛或客户箱唛对应原实测箱,禁止重新选择其他箱。
+ measuredCartonBindings() {
+ if (!this.requiresMeasuredBoxMapping) return []
+ const used = new Set()
+ return this.marks.map(mark => {
+ const matches = this.measuredBoxChoices.filter(box => box.cartonMark === mark || box.customerCartonMark === mark)
+ const box = matches.length === 1 && !used.has(matches[0].cartonMark) ? matches[0] : null
+ if (box) used.add(box.cartonMark)
+ return { cartonMark: mark, sourceCartonMark: box?.cartonMark }
+ })
},
- chooseMeasuredBox(mark, event) { const box = this.measuredBoxOptions(mark)[Number(event.detail.value)]; if (box) this.measuredSelections = { ...this.measuredSelections, [mark]: box.cartonMark } },
- measuredBoxLabel(mark) { return this.measuredBoxOptions(mark).find(box => box.cartonMark === this.measuredSelections?.[mark])?.label || '点击选择实测箱' },
async saveMeasurement(pallet) {
const dimensions = Object.fromEntries(this.measurementFields.map(field => [field.key, Number(pallet[field.key])]))
if (!Object.values(dimensions).every(value => Number.isFinite(value) && value > 0)) { this.error = '请填写有效的长、宽、高和毛重'; return }
@@ -171,9 +161,9 @@ export default {
const binding = this.rebindSelection
const targetCode = this.targetCode.trim().toUpperCase()
if (!binding || !targetCode || this.busy) return
- if (targetCode === binding.pallet.toUpperCase()) { this.showBindingMessage('无需改绑', '该箱唛已经绑定此托盘,不会重复扣库存。'); return }
+ if (targetCode === binding.pallet.toUpperCase()) { this.showBindingMessage('无需改绑', '该箱唛已绑定此托盘'); return }
this.busy = true; this.$emit('busy', true)
- const confirmed = await new Promise(resolve => uni.showModal({ title: '确认改绑', content: `将 ${binding.mark} 从 ${binding.pallet} 移至 ${targetCode}?旧托盘扣箱,新托盘加箱。`, confirmText: '是', cancelText: '否', success: result => resolve(result.confirm), fail: () => resolve(false) }))
+ const confirmed = await new Promise(resolve => uni.showModal({ title: '确认改绑', content: `将 ${binding.mark} 从 ${binding.pallet} 移至 ${targetCode}?`, confirmText: '是', cancelText: '否', success: result => resolve(result.confirm), fail: () => resolve(false) }))
this.busy = false; this.$emit('busy', false)
if (!confirmed) return
await this.run(async () => {
@@ -194,7 +184,7 @@ export default {
async loadSources() { await this.run(async () => { try { this.sourceRows = await findRelabelSources(this.task.relabelNo) } catch (error) { if (!this.isReturnBulk && this.returnCartonItems.length) this.sourceRows = []; else throw error } if (!this.isReturnBulk && this.returnCartonItems.length && !this.sourceRows.some(row => Number(row.movablePackages) > 0)) this.sourceRows = [{ positionId: this.returnCartonItems[0].returnCartonId, positionVersion: 0, palletCode: 'UNBOUND', stockPackages: this.returnCartonItems.length, movablePackages: this.returnCartonItems.length, cartonMarks: this.returnCartonItems.map(item => item.cartonMark).filter(Boolean) }]; for (const code of new Set(this.currentBindings.map(binding => binding.pallet))) { const pallet = await resolvePallet(code); this.savePalletTotals({ pallets: [pallet] }) } this.measurements = await listRelabelPalletMeasurements(this.task.relabelNo); this.selectedSource = null; this.source = null; this.marks = []; this.quantity = ''; if (this.unboundSourceMode && this.operableSourceRows.length === 1) { this.selectedSource = this.operableSourceRows[0]; this.selectedDestination = this.returnCartonItems[0]?.destinationFbaWarehouseCode || ''; this.marks = this.completedItems().map(item => item.cartonMark); this.quantity = String(this.marks.length); this.activate('target') } }) },
async selectSource(row) {
if (this.busy) return
- this.measuredSelections = {}; this.rebindSelection = null; this.selectedSource = row; this.source = null; this.sourceCode = ''; this.quantity = ''; this.marks = []
+ this.rebindSelection = null; this.selectedSource = row; this.source = null; this.sourceCode = ''; this.quantity = ''; this.marks = []
this.selectedDestination = (this.isReturnBulk ? this.sourceItems() : this.completedItems())[0]?.destinationFbaWarehouseCode || ''
if (this.unboundSourceMode) {
this.marks = this.completedItems().map(item => item.cartonMark)
@@ -265,17 +255,17 @@ export default {
let reason = ''
if (!this.selectedSource) reason = '请先选择实际拆出货物的来源托盘或来源箱。'
else if (this.unboundSourceMode && !this.bindingSelectionValid()) reason = '请先完成来源箱换标,再绑定目标托盘。'
- else if (!this.unboundSourceMode && !this.samePallet(this.source, this.selectedSource)) reason = this.isReturnBulk ? '来源托盘识别失败,请重新选择。' : '请先扫描并确认实际来源托盘;选择来源列表不等于扫描确认。'
- else if (this.isReturnBulk && this.completedItems().length !== this.eligibleItems.length) reason = '请先完成本托盘的 SKU 贴标登记,再绑定新托盘;整托无需填写拆箱数量。'
+ else if (!this.unboundSourceMode && !this.samePallet(this.source, this.selectedSource)) reason = this.isReturnBulk ? '来源托盘识别失败,请重新选择。' : '请扫描确认来源托盘'
+ else if (this.isReturnBulk && this.completedItems().length !== this.eligibleItems.length) reason = '请先完成 SKU 贴标登记'
else if (!Number.isSafeInteger(Number(this.quantity)) || Number(this.quantity) <= 0 || Number(this.quantity) > this.selectedSource.movablePackages) reason = this.isReturnBulk ? '本托盘可操作件数不足,请刷新来源托盘。' : '请填写有效的拆出箱数,不能超过来源托盘可拆数量。'
else if (!this.selectedDestination || !this.bindingSelectionValid()) reason = this.isReturnBulk ? `本托盘拆箱数量必须等于贴标预报分配数量 ${this.plannedPackages} 件。` : '本次拆出箱数必须对应已完成换标且尚未拆出的同目的仓箱唛。'
- else if (this.requiresMeasuredBoxChoice && this.marks.some(mark => !this.measuredSelections?.[mark])) reason = '请选择本次移出的实测箱,系统将按所选箱扣减体积和重量。'
+ else if (this.measuredCartonBindings().some(row => !row.sourceCartonMark)) reason = '换标箱与来源实测箱不匹配,请核对来源托盘'
else if (!this.targetCode.trim()) reason = '请扫描或输入目标托盘号。'
if (reason) { this.error = reason; this.showBindingMessage('尚未绑定', reason); return }
await this.resolve('target')
if (this.error) { this.showBindingMessage('绑定失败', this.error); return }
if (!this.readyToBind) { this.error = '请核对来源箱、目标托盘和换标数量后重新确认。'; this.showBindingMessage('尚未绑定', this.error); return }
- if (!await this.confirmBindingChoice(`是否绑定目标托盘 ${this.target.palletCode}(${this.quantity} ${this.isReturnBulk ? '件' : '箱'})?${this.samePallet(this.source, this.target) ? '整托保留原托盘且货物和包装未变可沿用原测量;包装变化请重新补测。' : ''}`)) { this.target = null; return }
+ if (!await this.confirmBindingChoice(`是否绑定目标托盘 ${this.target.palletCode}(${this.quantity} ${this.isReturnBulk ? '件' : '箱'})?`)) { this.target = null; return }
await this.bind()
},
destination(mark) { return this.task.items.find(item => item.cartonMark === mark)?.destinationFbaWarehouseCode || '目的仓待维护' },
@@ -309,8 +299,8 @@ export default {
async createPallet() { const payload = { relabelNo: this.task.relabelNo }; const key = warehouseIdempotencyKey('relabel', 'new-pallet', payload); await this.run(async () => { this.newPallet = await createRelabelPallet({ ...payload, idempotencyKey: key.value }); this.target = null; this.targetCode = this.newPallet.palletCode || ''; this.activate('target') }) },
async bind() {
if (!this.readyToBind) { this.error = '请核对来源托盘、拆出箱数、本批箱唛和目标托盘'; return }
- const measuredCartons = this.requiresMeasuredBoxChoice ? this.marks.map(mark => ({ cartonMark: mark, sourceCartonMark: this.measuredSelections?.[mark] })) : []
- if (measuredCartons.some(row => !row.sourceCartonMark)) { this.error = '请选择本次移出的实测箱'; return }
+ const measuredCartons = this.measuredCartonBindings()
+ if (measuredCartons.some(row => !row.sourceCartonMark)) { this.error = '换标箱与来源实测箱不匹配,请核对来源托盘'; return }
const expectation = value => ({ palletCode: value.palletCode, expectedVersion: value.palletVersion })
const payload = { relabelNo: this.task.relabelNo, ...(this.unboundSourceMode ? {} : { source: expectation(this.source), actualSource: { positionId: this.selectedSource.positionId, expectedPositionVersion: this.selectedSource.positionVersion, packages: Number(this.quantity) } }), target: expectation(this.target), cartonMarks: [...this.marks], reason: this.unboundSourceMode ? 'PDA 扫描无来源托盘退货箱并绑定目的托盘' : 'PDA 按原入仓单箱数拆托并按新目的仓合托' }
if (measuredCartons.length) payload.measuredCartons = measuredCartons
diff --git a/tests/relabel-pallet-measurement.test.mjs b/tests/relabel-pallet-measurement.test.mjs
index 2e3e6ec..bfe9647 100644
--- a/tests/relabel-pallet-measurement.test.mjs
+++ b/tests/relabel-pallet-measurement.test.mjs
@@ -57,7 +57,7 @@ test('unchanged source renders real grouped box specs and totals without zero pa
assert.match(html, /60 × 50 × 50 cm · 2.5 kg\/箱 · 2 箱/)
assert.match(html, /50 × 40 × 40 cm · 3.6 kg\/箱 · 2 箱/)
assert.match(html, /来源汇总:0.31 m³ · 9.7 kg/)
- assert.match(html, /未测整托长宽高/)
+ assert.doesNotMatch(html, /未测整托长宽高/)
assert.equal((html.match(/class="measurement-inputs"/g) || []).length, 0)
assert.doesNotMatch(html, /确认本托实测/)
})
@@ -72,37 +72,54 @@ test('changed pallets and explicit packaging changes still render final pallet m
test('box aggregated pallets render current totals and no manual pallet measurement even after moving', async () => {
const html = await renderMeasurements([{ palletCode: 'NEW', packages: 1, inherited: true, cartonAggregated: true,
grossWeightKg: 3.6, volumeCbm: 0.08, sourceCartonSpecs: [{lengthCm:50,widthCm:40,heightCm:40,grossWeightKg:3.6,cartons:1}] }])
- assert.match(html, /移出扣减、移入增加/)
+ assert.doesNotMatch(html, /移出扣减、移入增加/)
assert.match(html, /0.08 m³ · 3.6 kg/)
assert.doesNotMatch(html, /确认本托实测|重新测量|measurement-inputs/)
})
-test('mixed box selection is required and submits exact measured source box with binding', async () => {
+const mixedBoxes = [
+ { cartonMark: 'OTHER', lengthCm: 60, widthCm: 50, heightCm: 50, grossWeightKg: 2.5 },
+ { cartonMark: 'SMALL1', customerCartonMark: 'CUSTOMER', lengthCm: 50, widthCm: 40, heightCm: 40, grossWeightKg: 3.6 }
+]
+
+test('mixed specs automatically bind the submitted carton without another picker', async () => {
const { value, calls } = setup()
- Object.assign(value, { readyToBind:true, requiresMeasuredBoxChoice:true, marks:['CUSTOMER'],
- source:{palletCode:'OLD',palletVersion:1}, target:{palletCode:'NEW',palletVersion:1},
- selectedSource:{positionId:'1',positionVersion:1},quantity:'1', loadSources:async()=>{}, showBindingMessage(){} })
+ Object.assign(value, { readyToBind: true, requiresMeasuredBoxMapping: true, marks: ['CUSTOMER'], measuredBoxChoices: mixedBoxes,
+ source: { palletCode: 'OLD', palletVersion: 1 }, target: { palletCode: 'NEW', palletVersion: 1 },
+ selectedSource: { positionId: '1', positionVersion: 1 }, quantity: '1', loadSources: async () => {}, showBindingMessage() {} })
await value.bind()
- assert.equal(calls.length,0)
- assert.match(value.error,/请选择/)
- value.measuredSelections={CUSTOMER:'SMALL1'}
- await value.bind()
- assert.deepEqual(calls[0].measuredCartons,[{cartonMark:'CUSTOMER',sourceCartonMark:'SMALL1'}])
+ assert.deepEqual(calls[0].cartonMarks, ['CUSTOMER'])
+ assert.deepEqual(calls[0].measuredCartons, [{ cartonMark: 'CUSTOMER', sourceCartonMark: 'SMALL1' }])
+ assert.doesNotMatch(source, / {
+
+test('warehouse carton marks also match automatically and follow the current batch', () => {
const { value } = setup()
- Object.assign(value,{marks:['X','Y'],measuredSelections:{X:'A'},measuredBoxChoices:[
- {cartonMark:'A',lengthCm:60,widthCm:50,heightCm:50,grossWeightKg:2.5},
- {cartonMark:'B',lengthCm:50,widthCm:40,heightCm:40,grossWeightKg:3.6}]})
- assert.deepEqual(value.measuredBoxOptions('Y').map(box=>box.cartonMark),['B'])
- assert.equal(value.measuredBoxOptions('X').length,2)
+ Object.assign(value, { requiresMeasuredBoxMapping: true, measuredBoxChoices: mixedBoxes, marks: ['SMALL1'] })
+ assert.deepEqual(value.measuredCartonBindings(), [{ cartonMark: 'SMALL1', sourceCartonMark: 'SMALL1' }])
+ value.marks = ['OTHER']
+ assert.deepEqual(value.measuredCartonBindings(), [{ cartonMark: 'OTHER', sourceCartonMark: 'OTHER' }])
})
-test('entering split quantity prepares measured box choices before target binding', () => {
+
+test('unmatched, ambiguous or duplicate physical boxes cannot bind another carton', async () => {
+ for (const [marks, boxes] of [
+ [['MISSING'], mixedBoxes],
+ [['CUSTOMER'], [...mixedBoxes, { ...mixedBoxes[0], customerCartonMark: 'CUSTOMER' }]],
+ [['CUSTOMER', 'SMALL1'], mixedBoxes]
+ ]) {
+ const { value, calls } = setup()
+ Object.assign(value, { readyToBind: true, requiresMeasuredBoxMapping: true, measuredBoxChoices: boxes, marks })
+ await value.bind()
+ assert.equal(calls.length, 0)
+ assert.match(value.error, /不匹配/)
+ }
+})
+
+test('entering split quantity prepares the batch and clears invalid quantities', () => {
const { value } = setup()
- Object.assign(value,{measuredBoxChoices:[{cartonMark:'A'}],eligibleItems:[{cartonMark:'X'}],
- selectedSource:{movablePackages:1},measuredSelections:{OLD:'A'},fillBatch(){this.marks=['X']}})
- value.prepareMeasuredBatch({detail:{value:'1'}})
- assert.deepEqual(value.marks,['X'])
- assert.deepEqual(value.measuredSelections,{})
- value.prepareMeasuredBatch({detail:{value:'0'}})
- assert.deepEqual(value.marks,[])
+ Object.assign(value, { measuredBoxChoices: [{ cartonMark: 'A' }], eligibleItems: [{ cartonMark: 'X' }],
+ selectedSource: { movablePackages: 1 }, fillBatch() { this.marks = ['X'] } })
+ value.prepareMeasuredBatch({ detail: { value: '1' } })
+ assert.deepEqual(value.marks, ['X'])
+ value.prepareMeasuredBatch({ detail: { value: '0' } })
+ assert.deepEqual(value.marks, [])
})
diff --git a/tests/relabel-pallet-work.test.mjs b/tests/relabel-pallet-work.test.mjs
index 6b489af..208ec6f 100644
--- a/tests/relabel-pallet-work.test.mjs
+++ b/tests/relabel-pallet-work.test.mjs
@@ -59,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, /来源已确认:\{\{ source.palletCode \}\}<\/text>/)
assert.match(source, /this\.message = `托盘 \$\{this\.target\.palletCode\} 已绑定`/)
})
@@ -96,7 +96,7 @@ test('rescan same bound pallet does not call stock movement', async () => {
value.showBindingMessage = (title, content) => { message = content }
value.run = async () => assert.fail('same pallet must not move stock')
await value.submitRebind()
- assert.match(message, /不会重复扣库存/)
+ assert.match(message, /已绑定此托盘/)
})
test('target confirmation submits binding directly, with no second submit button', async () => {
@@ -183,7 +183,7 @@ test('missing source scan shows modal and never submits a binding', async () =>
value.bind = async () => assert.fail('must not bind without source confirmation')
value.resolve = async () => assert.fail('must validate source first')
await value.confirmTargetAndBind()
- assert.match(message, /扫描并确认实际来源托盘/)
+ assert.match(message, /扫描确认来源托盘/)
})
test('target lookup failure cannot submit binding or report success', async () => {