import test from 'node:test' import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' test('warehouse transfer PDA API exposes candidates and palletize operations', async () => { const source = await import('../src/api/warehouse-transfers.js') assert.equal(typeof source.listWarehouseTransferCandidates, 'function') assert.equal(typeof source.getWarehouseTransferDetail, 'function') assert.equal(typeof source.palletizeWarehouseTransfer, 'function') assert.equal(typeof source.saveWarehouseTransferPalletDraft, 'function') assert.equal(typeof source.generateWarehouseTransferPallet, 'function') assert.equal(typeof source.bindWarehouseTransferPallet, 'function') assert.equal(typeof source.deleteWarehouseTransferPallet, 'function') assert.equal(typeof source.uploadWarehouseTransferMeasurementPhoto, 'function') assert.equal(typeof source.listWarehouseTransferMeasurementPhotos, 'function') assert.equal(typeof source.downloadWarehouseTransferMeasurementPhoto, 'function') assert.equal(typeof source.downloadWarehouseTransferPalletLabel, 'function') }) test('warehouse transfer workbench entry is registered', async () => { const { MODULES } = await import('../src/data/overseas-workbench.js') const operation = MODULES.flatMap((m) => m.operations).find((item) => item.key === 'warehouseTransferList') assert.equal(operation?.available, true) assert.equal(operation?.route, '/pages/warehouse-transfer/warehouse-transfer?mode=list') const work = MODULES.flatMap((m) => m.operations).find((item) => item.key === 'warehouseTransferWork') assert.equal(work?.route, '/pages/warehouse-transfer/warehouse-transfer?mode=work') }) test('warehouse transfer page loads detail without exposing internal identifiers', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.match(page, /getWarehouseTransferDetail\(item\.transferNo\)/) assert.match(page, /待测转运单/) assert.match(page, /仓储转运测量列表/) assert.match(page, /转运测量作业/) assert.match(page, /!selected && entryMode === 'list'/) assert.match(page, /entryMode === 'work' && taskNo/) assert.match(page, /mode=work&task=\$\{encodeURIComponent\(String\(item\.transferNo\)\)\}/) assert.match(page, /v-if="!selected && entryMode === 'list'"/) assert.match(page, /返回列表/) assert.match(page, /backToCandidates\(\)/) assert.match(page, /backToCandidates\(\) \{ if \(this\.submitting.*this\.entryMode = 'list'/) assert.match(page, /存在未完结的测量单/) assert.match(page, /title: '开始测量'/) assert.match(page, /确认开始测量转运单/) assert.match(page, /warehouseWork\.warehouseTransfer\.currentNo/) assert.match(page, /resetWarehouseDomain\('warehouseTransfer'\)/) assert.match(page, /statusLabel\(status, transferNo = ''\) \{ if \(String\(status\) === 'WAIT_PDA_RECEIVING'/) assert.match(page, /listWarehouseTransferCandidates\(\)\)\.map\(\(item\) => String\(item\.transferNo\) === activeNo.*PARTIALLY_RECEIVED/s) assert.doesNotMatch(page, /转运明细 ID|manualItems/) }) test('warehouse transfer removes completed work from the pending list', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.match(page, /this\.selected = null; this\.pendingKey = '';[\s\S]*await this\.loadCandidates\(\)/) }) 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 ['客户预报', 'SKU实收', 'SKU签收差异']) assert.match(page, new RegExp(label)) assert.match(page, /hasSkuReceiptDifference/) assert.doesNotMatch(page, /全部托盘箱数必须等于订单计划箱数/) assert.doesNotMatch(page, /本托 SKU 箱数|itemCounts/) }) test('warehouse transfer creates a fresh completion key for each explicit attempt', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.doesNotMatch(page, /if \(!this\.pendingKey\) this\.pendingKey = `pda-transfer-/) assert.match(page, /this\.pendingKey = `pda-transfer-/) assert.match(page, /idempotencyKey: this\.pendingKey/) assert.equal((page.match(/pendingKey = `pda-transfer-/g) || []).length, 1) }) test('warehouse transfer measurement inputs retain explicit placeholders', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.doesNotMatch(page, /v-model.number="pallet\.(lengthCm|widthCm|heightCm|grossWeightKg)"/) }) test('warehouse transfer measurements are grouped and summarized per pallet', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.match(page, /v-for="\(pallet, index\) in pallets"/) for (const label of ['本托箱子汇总', '实收箱数', '箱子重量 kg', '箱子体积 m³']) assert.match(page, new RegExp(label)) assert.match(page, /palletVolume\(pallet\)/) assert.match(page, /rawPalletVolume\(pallet\)/) }) test('warehouse transfer requires a camera photo and submits its file id with palletization', async () => { const [page, api] = await Promise.all([ readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8'), readFile(new URL('../src/api/warehouse-transfers.js', import.meta.url), 'utf8') ]) assert.match(page, /sourceType: \['camera', 'album'\]/) assert.match(page, /请至少拍摄 1 张测量现场照片/) assert.match(page, /photoFileIds: \[\.\.\.this\.photoFileIds\]/) assert.match(api, /measurement-photos\/files/) assert.match(page, /tempFilePaths \|\| \[\]/) assert.match(page, /未获取到可上传的照片路径,请重新拍摄/) assert.match(page, /所选转运单已切换,请重新拍摄/) }) test('warehouse transfer restores uploaded photos and exposes honest pallet label printing', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.match(page, /listWarehouseTransferMeasurementPhotos/) assert.match(page, /downloadWarehouseTransferMeasurementPhoto/) assert.match(page, /completedPallets = \(result\?\.pallets \|\| \[\]\)/) assert.match(page, /downloadWarehouseTransferPalletLabel/) assert.match(page, /pallet\.labelDownloadPath/) assert.match(page, /destinationLabel\(pallet, \{ destinationType: completedDestinationType \}\)/) assert.match(page, /type === 'PRIVATE' \? '私人仓' : '目的仓待定'/) assert.match(page, /completedDestinationType = result\?\.destinationType \|\| this\.selected\.destinationType/) assert.doesNotMatch(page, /privateDestinationAddress/) assert.match(page, /uni\.openDocument\(\{ filePath, fileType: 'pdf', showMenu: true/) assert.doesNotMatch(page, /批量打印|printAllPallets/) }) test('warehouse transfer enforces the photo limit and restores pending server uploads', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.match(page, /const MAX_MEASUREMENT_PHOTOS = 20/) assert.match(page, /photos = await listWarehouseTransferMeasurementPhotos\(transferNo\)/) assert.match(page, /const uploadPaths = paths\.slice\(0, remaining\)/) assert.match(page, /this\.photoFileIds\.length >= MAX_MEASUREMENT_PHOTOS/) assert.match(page, /await this\.restorePhotos\(transferNo\)/) assert.match(page, /at most 20\|最多\.\?20\|supports at most/) }) test('warehouse transfer always requests newly generated pallets', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.doesNotMatch(page, /托盘码\s*选填|扫描或输入当前托盘码/) assert.doesNotMatch(page, /palletCode: String\(p\.palletCode/) assert.match(page, /completedPallets = \(result\?\.pallets \|\| \[\]\)/) }) test('warehouse transfer pallet cards prepare generated and server-bound states', async () => { const page = await readFile(new URL('../src/pages/warehouse-transfer/warehouse-transfer.vue', import.meta.url), 'utf8') assert.match(page, /生成托盘号/) assert.match(page, /pallet\.generated && !pallet\.bound/) assert.match(page, /ScanTargetInput/) assert.match(page, /startScanBroadcast/) assert.match(page, /handlePalletScan/) assert.match(page, /扫描或手输新托盘标签号/) assert.match(page, /已绑定实物托盘/) assert.match(page, /@tap\.stop="removePallet\(index\)">×/) assert.match(page, /请先生成托盘号,再填写签收箱子/) assert.match(page, /collapsed: false/) assert.match(page, /pallet\.collapsed = !pallet\.collapsed/) assert.match(page, /v-show="!pallet\.collapsed"/) assert.match(page, /this\.selected\.pallets\?\.length \? this\.selected\.pallets\.map\(restoredPallet\)/) assert.doesNotMatch(page, /if \(!this\.completedTransferNo \|\| this\.openingLabel\) return/) assert.match(page, /saveWarehouseTransferPalletDraft/) assert.match(page, /generateWarehouseTransferPallet/) assert.match(page, /bindWarehouseTransferPallet/) assert.match(page, /deleteWarehouseTransferPallet/) assert.match(page, /请先生成并扫描确认绑定全部托盘/) assert.match(page, /const payload = \{ idempotencyKey: this\.pendingKey, photoFileIds: \[\.\.\.this\.photoFileIds\], discrepancyReason:/) })