优化
This commit is contained in:
@@ -56,8 +56,8 @@ test('warehouse transfer removes completed work from the pending list', async ()
|
||||
|
||||
test('warehouse transfer shows forecast and actual variance and requires counted box reconciliation', async () => {
|
||||
const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8')
|
||||
for (const label of ['客户预报', '当前实收', '短收差异', '超收差异']) assert.match(page, new RegExp(label))
|
||||
assert.match(page, /cartonDifference/)
|
||||
for (const label of ['客户预报', 'SKU实收', 'SKU签收差异']) assert.match(page, new RegExp(label))
|
||||
assert.match(page, /hasSkuReceiptDifference/)
|
||||
assert.doesNotMatch(page, /全部托盘箱数必须等于订单计划箱数/)
|
||||
assert.doesNotMatch(page, /本托 SKU 箱数|itemCounts/)
|
||||
})
|
||||
@@ -137,7 +137,7 @@ test('warehouse transfer pallet cards prepare generated and server-bound states'
|
||||
assert.match(page, /handlePalletScan/)
|
||||
assert.match(page, /扫描或手输新托盘标签号/)
|
||||
assert.match(page, /已绑定实物托盘/)
|
||||
assert.match(page, /!pallet\.generated.*removePallet/)
|
||||
assert.match(page, /@tap\.stop="removePallet\(index\)">×/)
|
||||
assert.match(page, /请先生成托盘号,再填写签收箱子/)
|
||||
assert.match(page, /collapsed: false/)
|
||||
assert.match(page, /pallet\.collapsed = !pallet\.collapsed/)
|
||||
@@ -149,5 +149,5 @@ test('warehouse transfer pallet cards prepare generated and server-bound states'
|
||||
assert.match(page, /bindWarehouseTransferPallet/)
|
||||
assert.match(page, /deleteWarehouseTransferPallet/)
|
||||
assert.match(page, /请先生成并扫描确认绑定全部托盘/)
|
||||
assert.match(page, /const payload = \{ idempotencyKey: this\.pendingKey, photoFileIds: \[\.\.\.this\.photoFileIds\] \}/)
|
||||
assert.match(page, /const payload = \{ idempotencyKey: this\.pendingKey, photoFileIds: \[\.\.\.this\.photoFileIds\], discrepancyReason:/)
|
||||
})
|
||||
|
||||
@@ -11,18 +11,63 @@ function setup() {
|
||||
const value = scope.page.data()
|
||||
for(const [key,method] of Object.entries(scope.page.methods)) value[key]=method.bind(value)
|
||||
for(const [key,get] of Object.entries(scope.page.computed)) Object.defineProperty(value,key,{get:()=>get.call(value)})
|
||||
value.selected={transferNo:'WT1',packageCount:1,status:'WAIT_PDA_RECEIVING',items:[{id:'I1',skuCode:'A'}],cartons:[{cartonMark:'BOX1',transferItemId:'I1',status:'EXPECTED'}]}
|
||||
value.selected={transferNo:'WT1',packageCount:1,status:'WAIT_PDA_RECEIVING',items:[{id:'I1',skuCode:'A',quantity:4}],cartons:[{cartonMark:'BOX1',transferItemId:'I1',status:'EXPECTED'}]}
|
||||
value.pallets=[{key:'P1',draftId:'P1',palletNo:'PALLET1',generated:true,bound:false}];value.restoreCartons();Object.assign(value.cartonBoxes[0],{length:10,width:20,height:30,weight:2});value.cartonBoxes[0].items[0].quantity=4
|
||||
return {scope,value,storage}
|
||||
}
|
||||
test('summary toggle preserves measurements and volume uses three decimals',()=>{
|
||||
const {value}=setup()
|
||||
assert.equal(value.summaryCollapsed,false)
|
||||
value.summaryCollapsed=true
|
||||
assert.equal(value.cartonBoxes[0].weight,2)
|
||||
Object.assign(value.cartonBoxes[0],{length:10,width:10,height:6})
|
||||
value.addExtraCarton()
|
||||
Object.assign(value.cartonBoxes[1],{length:10,width:10,height:6,weight:2})
|
||||
assert.equal(value.palletVolume(value.pallets[0]),'0.001')
|
||||
assert.equal(value.totalVolume,'0.001')
|
||||
assert.match(source,/aria-label="添加 SKU"/)
|
||||
assert.match(source,/aria-label="删除箱子"/)
|
||||
})
|
||||
|
||||
test('remeasure preserves saved measurements and unlocks carton editing',async()=>{
|
||||
const {value,scope}=setup();
|
||||
const box={...value.cartonBoxes[0],countedAt:'2026-10-16',status:'COUNTED'};
|
||||
value.selected.cartons=[box]; value.cartonRequestKey='old-key';
|
||||
scope.reopenWarehouseTransferMeasurement=async()=>({...value.selected,cartons:[{...box,countedAt:null}]});
|
||||
await value.remeasure();
|
||||
assert.equal(value.cartonsCounted,false); assert.equal(value.cartonBoxes[0].weight,2);
|
||||
assert.equal(value.cartonBoxes[0].transferPalletId,'P1'); assert.equal(value.cartonRequestKey,'');
|
||||
assert.equal(value.cartonCountReady,true);
|
||||
})
|
||||
|
||||
test('bound pallet deletion retains box values and requires reassignment',async()=>{
|
||||
const {value,scope}=setup();
|
||||
scope.uni.showModal=options=>options.success({confirm:true});
|
||||
value.pallets[0].bound=true;
|
||||
scope.deleteWarehouseTransferPallet=async()=>({...value.selected,pallets:[]});
|
||||
await value.removePallet(0);
|
||||
assert.equal(value.cartonBoxes[0].transferPalletId,''); assert.equal(value.cartonBoxes[0].weight,2);
|
||||
assert.equal(value.generatedPallets.length,0); assert.equal(value.pallets.length,1);
|
||||
assert.equal(value.cartonCountReady,false); assert.equal(value.cartonPalletFilter,'');
|
||||
})
|
||||
|
||||
test('failed pallet deletion retains binding and measurements',async()=>{
|
||||
const {value,scope}=setup();
|
||||
scope.uni.showModal=options=>options.success({confirm:true});
|
||||
scope.deleteWarehouseTransferPallet=async()=>{throw new Error('offline')};
|
||||
await value.removePallet(0);
|
||||
assert.equal(value.cartonBoxes[0].transferPalletId,'P1'); assert.equal(value.pallets[0].busy,false);
|
||||
assert.equal(value.error.message,'offline');
|
||||
})
|
||||
|
||||
test('transfer counts require valid pieces and measurements and record variance reason',()=>{
|
||||
const {value}=setup();assert.equal(value.cartonCountReady,true)
|
||||
value.cartonBoxes[0].items[0].quantity=0;assert.equal(value.cartonCountReady,false)
|
||||
value.cartonBoxes[0].items[0].quantity=1.5;assert.equal(value.cartonCountReady,false)
|
||||
value.cartonBoxes[0].items[0].quantity=4;value.cartonBoxes[0].height=0;assert.equal(value.cartonCountReady,false)
|
||||
value.cartonBoxes[0].height=30;value.addExtraCarton();Object.assign(value.cartonBoxes[1],{length:10,width:20,height:30,weight:2});value.cartonBoxes[1].items[0].quantity=5
|
||||
assert.equal(value.cartonCountReady,false);value.countDiscrepancyReason='多一箱';assert.equal(value.cartonCountReady,true)
|
||||
assert.equal(value.cartonQuantity,9);assert.equal(value.cartonVolume,'0.012000')
|
||||
assert.equal(value.cartonCountReady,true);value.countDiscrepancyReason='多一箱';assert.equal(value.cartonCountReady,true)
|
||||
assert.equal(value.cartonQuantity,9);assert.equal(value.cartonVolume,'0.012')
|
||||
})
|
||||
test('network retry preserves request key and changed content uses a new request',async()=>{
|
||||
const {value,scope}=setup();const requests=[]
|
||||
@@ -32,11 +77,9 @@ test('network retry preserves request key and changed content uses a new request
|
||||
value.cartonBoxes[0].items[0].quantity=8;await value.confirmCartons();assert.notEqual(requests[1].idempotencyKey,requests[2].idempotencyKey)
|
||||
assert.equal(value.countingCartons,false);assert.equal(value.error.message,'offline')
|
||||
})
|
||||
test('draft restores additional cartons and discrepancy reason and scanning rejects foreign box',()=>{
|
||||
test('draft restores additional cartons and discrepancy reason without crossing tasks',()=>{
|
||||
const {value}=setup();value.addExtraCarton();value.countDiscrepancyReason='超收';value.saveCartonDraft();value.restoreCartons()
|
||||
assert.equal(value.cartonBoxes.length,2);assert.equal(value.countDiscrepancyReason,'超收')
|
||||
value.locateCarton('FOREIGN');assert.match(value.error.message,/不属于/)
|
||||
value.locateCarton('BOX1');assert.equal(value.selectedCartonMark,'BOX1');assert.equal(value.cartonScrollTarget,'transfer-box-0')
|
||||
value.selected={...value.selected,transferNo:'WT2'};value.restoreCartons();assert.equal(value.cartonBoxes.length,1)
|
||||
})
|
||||
test('received count gates pallet completion and saving cannot switch active order',()=>{
|
||||
@@ -47,6 +90,7 @@ test('received count gates pallet completion and saving cannot switch active ord
|
||||
|
||||
test('batch applies only selected received boxes of one SKU and preserves independent drafts',()=>{
|
||||
const {value}=setup()
|
||||
value.selected.items.push({id:'I2',skuCode:'B'})
|
||||
value.addExtraCarton();value.addExtraCarton();value.addExtraCarton()
|
||||
value.cartonBoxes[2].items[0].skuCode='B'
|
||||
value.cartonBoxes[3].received=false
|
||||
@@ -127,3 +171,83 @@ test('pallet number can be created before measurements and new boxes receive tha
|
||||
assert.equal(value.cartonBoxes[0].transferPalletId,'CREATED')
|
||||
assert.equal(value.pallets[0].palletNo,'NEWPALLET')
|
||||
})
|
||||
|
||||
// 最终完成入口需要区分无差异、取消、空白原因和网络重试。
|
||||
for (const answer of [{confirm:false}, {confirm:true,content:' '}, {confirm:true,content:'少到一箱'}]) {
|
||||
test('completion handles discrepancy answer '+JSON.stringify(answer),async()=>{
|
||||
const {value,scope}=setup();let requests=[];let prompts=0
|
||||
value.selected.items[0].quantity=5;value.selected.cartons[0].countedAt='2026-10-16'
|
||||
value.pallets[0].bound=true;value.photoFileIds=[91]
|
||||
scope.uni.showModal=options=>{prompts++;options.success(answer)}
|
||||
scope.palletizeWarehouseTransfer=async(no,payload)=>{requests.push(payload);throw new Error('offline')}
|
||||
await value.submit()
|
||||
assert.equal(prompts,1);assert.equal(value.submitting,false)
|
||||
assert.equal(requests.length,answer.confirm && answer.content?.trim()?1:0)
|
||||
if(requests.length){assert.equal(requests[0].discrepancyReason,'少到一箱');await value.submit();assert.equal(requests[0].idempotencyKey,requests[1].idempotencyKey)}
|
||||
})
|
||||
}
|
||||
test('matching SKU counts with different carton count submit without prompting, and missing photos block before prompting',async()=>{
|
||||
const {value,scope}=setup();let prompts=0;let requests=[]
|
||||
value.selected.packageCount=9;value.selected.cartons[0].countedAt='2026-10-16';value.pallets[0].bound=true
|
||||
scope.uni.showModal=()=>{prompts++}
|
||||
scope.palletizeWarehouseTransfer=async(no,payload)=>{requests.push(payload);throw new Error('offline')}
|
||||
await value.submit();assert.equal(requests.length,0);assert.equal(prompts,0)
|
||||
value.photoFileIds=[91];await value.submit();assert.equal(prompts,0);assert.equal(requests.length,1);assert.equal(requests[0].discrepancyReason,null)
|
||||
})
|
||||
|
||||
// 逐箱确认收起、再次编辑及草稿恢复必须保留其他箱的确认状态。
|
||||
test('individual confirmation collapses only that box and resumes from draft',async()=>{
|
||||
const {value,scope}=setup();value.addExtraCarton()
|
||||
Object.assign(value.cartonBoxes[1],{length:10,width:20,height:30,weight:2});value.cartonBoxes[1].items[0].quantity=5
|
||||
let saves=0;scope.countWarehouseTransferCartons=async()=>{saves++;throw new Error('offline')}
|
||||
await value.confirmSingleCarton(value.cartonBoxes[0])
|
||||
assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),true);assert.equal(value.allCartonsConfirmed,false);assert.equal(saves,0)
|
||||
value.restoreCartons();assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),true)
|
||||
await value.editCarton(value.cartonBoxes[0]);assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),false)
|
||||
await value.confirmSingleCarton(value.cartonBoxes[0]);await value.confirmSingleCarton(value.cartonBoxes[1])
|
||||
assert.equal(value.allCartonsConfirmed,true);assert.equal(saves,1);assert.equal(value.error.message,'offline')
|
||||
value.cartonBoxes[0].weight=3;assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),false);assert.equal(value.allCartonsConfirmed,false)
|
||||
})
|
||||
test('saved boxes restore collapsed and editing reopens only selected box',async()=>{
|
||||
const {value,scope}=setup()
|
||||
const first={...value.cartonBoxes[0],quantity:4,countedAt:'2026-10-16',status:'COUNTED'}
|
||||
const second={...first,cartonMark:'BOX2'}
|
||||
value.selected.cartons=[first,second];value.restoreCartons()
|
||||
assert.equal(value.cartonBoxes.every(box=>value.cartonConfirmed(box)),true)
|
||||
scope.reopenWarehouseTransferMeasurement=async()=>({...value.selected,cartons:value.selected.cartons.map(box=>({...box,countedAt:null}))})
|
||||
await value.editCarton(value.cartonBoxes[0])
|
||||
assert.equal(value.cartonsCounted,false);assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),false);assert.equal(value.cartonConfirmed(value.cartonBoxes[1]),true)
|
||||
value.restoreCartons();assert.equal(value.cartonConfirmed(value.cartonBoxes[1]),true)
|
||||
})
|
||||
test('failed reopening preserves confirmed data and invalid box cannot confirm',async()=>{
|
||||
const {value,scope}=setup();value.cartonBoxes[0].weight=0
|
||||
await value.confirmSingleCarton(value.cartonBoxes[0]);assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),false)
|
||||
value.cartonBoxes[0].weight=2;value.selected.cartons=[{...value.cartonBoxes[0],countedAt:'2026-10-16',status:'COUNTED'}];value.restoreCartons()
|
||||
scope.reopenWarehouseTransferMeasurement=async()=>{throw new Error('offline')}
|
||||
await value.editCarton(value.cartonBoxes[0]);assert.equal(value.cartonsCounted,true);assert.equal(value.cartonConfirmed(value.cartonBoxes[0]),true)
|
||||
})
|
||||
|
||||
// 箱数变化不影响 SKU 门禁,异 SKU 正负差异不能按总件数抵消。
|
||||
test('SKU differences compare each SKU and ignore carton count',()=>{
|
||||
const {value}=setup();value.selected.packageCount=9
|
||||
assert.equal(value.hasSkuReceiptDifference,false)
|
||||
value.selected.items.push({id:'I2',skuCode:'B',quantity:3})
|
||||
value.cartonBoxes[0].items=[{skuCode:'A',quantity:3},{skuCode:'B',quantity:4}]
|
||||
assert.equal(value.cartonQuantity,value.forecastSkuQuantity)
|
||||
assert.equal(value.skuReceiptDifferences.length,2)
|
||||
value.cartonBoxes[0].items=[{skuCode:'A',quantity:4},{skuCode:'B',quantity:3}]
|
||||
assert.equal(value.hasSkuReceiptDifference,false)
|
||||
})
|
||||
|
||||
// 客户补箱后,新增预报箱进入PDA,原本机草稿和删除标记不丢失。
|
||||
test('forecast additions merge into existing draft without losing edited boxes',()=>{
|
||||
const {value}=setup();value.cartonBoxes[0].weight=7;value.saveCartonDraft()
|
||||
value.selected.packageCount=2;value.selected.cartons.push({cartonMark:'BOX2',transferItemId:'I1',status:'EXPECTED'})
|
||||
value.restoreCartons();assert.equal(value.cartonBoxes.length,2);assert.equal(value.cartonBoxes[0].weight,7);assert.equal(value.cartonBoxes[1].cartonMark,'BOX2')
|
||||
value.removeCarton(0);value.restoreCartons();assert.equal(value.cartonBoxes.length,1);assert.equal(value.cartonBoxes[0].cartonMark,'BOX2')
|
||||
})
|
||||
test('count payload carries forecast version for stale snapshot rejection',async()=>{
|
||||
const {value,scope}=setup();let payload
|
||||
scope.countWarehouseTransferCartons=async(no,request)=>{payload=request;throw new Error('offline')}
|
||||
await value.confirmCartons();assert.equal(payload.forecastPackageCount,1);assert.equal(payload.boxes[0].cartonMark,'BOX1')
|
||||
})
|
||||
|
||||
@@ -58,14 +58,14 @@ test('delete only changes actual draft and preserves forecast; additions keep th
|
||||
test('draft reload preserves deletions, including an empty actual list', () => {
|
||||
const ctx = context()
|
||||
ctx.selected.cartons = [{ cartonMark: 'BOX1', transferItemId: '1', status: 'EXPECTED' }, { cartonMark: 'BOX2', transferItemId: '2', status: 'EXPECTED' }]
|
||||
let saved = { boxes: [{ ...box('B'), cartonMark: 'BOX2', transferItemId: '2' }], discrepancyReason: '少到一箱' }
|
||||
let saved = { forecastMarks: ['BOX1','BOX2'], boxes: [{ ...box('B'), cartonMark: 'BOX2', transferItemId: '2' }], discrepancyReason: '少到一箱' }
|
||||
globalThis.uni = { getStorageSync: () => saved }
|
||||
try {
|
||||
ctx.restoreCartons()
|
||||
assert.equal(ctx.cartonBoxes.length, 1)
|
||||
assert.equal(ctx.cartonBoxes[0].cartonMark, 'BOX2')
|
||||
assert.equal(ctx.countDiscrepancyReason, '少到一箱')
|
||||
saved = { boxes: [], discrepancyReason: '重新录入' }
|
||||
saved = { forecastMarks: ['BOX1','BOX2'], boxes: [], discrepancyReason: '重新录入' }
|
||||
ctx.restoreCartons()
|
||||
assert.equal(ctx.cartonBoxes.length, 0)
|
||||
assert.equal(ctx.selected.cartons.length, 2)
|
||||
@@ -93,7 +93,7 @@ test('100 same SKU cartons accept two distinct specification groups and independ
|
||||
assert.equal(ctx.palletSummary(ctx.pallets[1]).cartonCount, 40)
|
||||
assert.equal(ctx.cartonQuantity, 1040)
|
||||
assert.equal(ctx.totalWeight, '580.00')
|
||||
assert.equal(ctx.totalVolume, '3.840000')
|
||||
assert.equal(ctx.totalVolume, '3.840')
|
||||
assert.equal(ctx.cartonCountReady, true)
|
||||
ctx.cartonBoxes[0].items[0].quantity = 11
|
||||
ctx.cartonBoxes[0].weight = 6
|
||||
|
||||
Reference in New Issue
Block a user