Files
client-web/src/modules/overseas-warehouse/views/warehouseTransferForm.test.mjs
T
2026-10-20 11:54:44 +08:00

409 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
const view = readFileSync(new URL('./WarehouseServiceView.vue', import.meta.url), 'utf8')
const api = readFileSync(new URL('../api/warehouseServices.ts', import.meta.url), 'utf8')
test('贴标总文件与关联模板保持左右分组,退货来源使用专用明细', () => {
assert.match(view, /class="warehouse-file-groups(?:\s[^"]*)?"/)
assert.match(view, /\.warehouse-file-groups--compact\s*\{[^}]*grid-template-columns:\s*minmax\(0, 1fr\) minmax\(0, 1fr\)/)
assert.match(view, /v-if="isReturnRelabelSource" class="warehouse-table-scroll"/)
assert.match(view, /warehouse-service-page--return-relabel/)
assert.match(view, /const sourceValue = `RETURN_ORDER:\$\{requestedReturnNo\}`/)
})
test('仓储转运先选择入仓方式并按方式切换地址信息', () => {
assert.match(view, /value="WAREHOUSE_PICKUP">仓库上门提货/)
assert.match(view, /value="CUSTOMER_DELIVERY">客户送货到仓/)
assert.match(view, /form\.deliveryMethod === 'WAREHOUSE_PICKUP'.*提货地址/s)
assert.match(view, /form\.deliveryMethod === 'CUSTOMER_DELIVERY'.*送货地址.*收货联系人.*联系电话/s)
assert.match(view, /getMerchantWarehouseDetail\(form\.warehouse\)/)
})
test('仓储转运支持无目的地、FBA 仓和私人仓并显式选择是否换标', () => {
assert.match(view, />目的去向</)
assert.match(view, /value="NONE">暂未确定/)
assert.match(view, /value="FBA">FBA 仓/)
assert.match(view, /value="PRIVATE">私人仓/)
assert.match(view, /transferDestinationType === 'PRIVATE'.*私人仓地址.*邮编.*私人仓联系人.*联系电话/s)
assert.match(view, />是否贴标</)
assert.match(view, /:value="false">不需要贴标/)
assert.match(view, /:value="true">需要贴标/)
assert.doesNotMatch(view, /v-model="form\.relabel" disabled/)
assert.doesNotMatch(view, /value === 'FBA'\) form\.relabel = true/)
assert.doesNotMatch(view, /value === 'PRIVATE'.*form\.relabel = false/)
assert.match(view, /v-if="props\.service === 'transfer' && form\.relabel" class="warehouse-card warehouse-form-section warehouse-transfer-relabel"/)
assert.match(api, /fbaWarehouseCode\?: string/)
assert.match(api, /privateDestinationAddress\?: string; privateDestinationPostalCode\?: string/)
assert.match(api, /fbaShipmentNo\?: string/)
assert.doesNotMatch(view, /换标费用(CNY)/)
assert.doesNotMatch(view, /relabelChargeAmount:/)
})
test('换标详情待客户确认时提供确认入口并刷新详情', () => {
assert.match(view, /application\.status === 'CUSTOMER_CONFIRM_PENDING'[\s\S]*@click="confirmSelectedRelabel">确认换标完成/)
assert.match(view, /async function confirmSelectedRelabel\(\)[\s\S]*openCompletionConfirmation\(relabelNo, 'labeling'\)/)
assert.match(view, /async function refreshAfterCompletion\(no: string\)[\s\S]*await openDetail\(\{ '0': no \}\)/)
})
test('仓储转运换标绑定明细自动同步只读的旧 SKU', () => {
assert.match(view, /watch\(\(\) => cargoRows\.value\.map\(\(row\) => row\.sku\)/)
assert.match(view, /if \(sourceSku\) row\.sku = sourceSku/)
assert.match(view, /transferRelabelRows\.value\.push\(\{ \.\.\.createCargoRow\(\), sku: source\?\.sku \|\| '' \}\)/)
})
test('仓储转运换标整票只允许一个目的仓', () => {
assert.match(view, /props\.service === 'transfer' && transferDestinationType === 'FBA'[^\n]+FBA 目的仓/)
assert.match(view, /FBA 货件编号<\/span><el-input v-model="form\.fbaShipmentNo" placeholder=""/)
assert.match(view, /transferDestinationType\.value === 'FBA' && !form\.destination/)
assert.match(view, /transferDestinationType\.value === 'PRIVATE'.*!form\.privateDestinationPostalCode/)
assert.match(view, /整票换标货物目的 FBA 仓/)
assert.match(view, /if \(!transferRelabelDefaultDestination\.value\) \{ ElMessage\.warning\('请选择整票换标货物的 FBA 目的仓'\)/)
assert.match(view, /destinationWarehouseCode: transferRelabelDefaultDestination\.value \|\| undefined/)
const transferRelabelSection = view.split('warehouse-transfer-relabel">')[1]?.split('warehouse-transfer-submit-bar')[0] || ''
assert.doesNotMatch(transferRelabelSection, /v-model="row\.destinationWarehouseId"/)
assert.doesNotMatch(transferRelabelSection, /<th>FBA 目的仓 \*<\/th>/)
assert.doesNotMatch(view, /applyTransferRelabelDestination/)
assert.match(view, /destinationWarehouseId: binding\.destinationWarehouseCode \|\| ''/)
assert.match(view, /props\.service === 'transfer' \? '客户箱唛号' : '仓库唛头'/)
assert.match(view, /已导入 \$\{importedRows\.length\} 条仓储转运换标明细/)
assert.doesNotMatch(transferRelabelSection, /新 FBA 箱唛<\/th><th>FBA 目的仓<\/th>/)
assert.match(view, /const includeDestination = false/)
})
test('仓储转运不要求客户填写内部入仓号和我方箱唛,但换标时填写客户箱唛', () => {
const transferConfig = view.match(/transfer:\s*\{[^\n]+/)?.[0] || ''
const transferSubmit = view.match(/const payload: TransferCreate = \{[^\n]+/)?.[0] || ''
assert.doesNotMatch(transferConfig, /进仓编号/)
assert.doesNotMatch(transferSubmit, /inboundCode|sourceLastlegOrderIds/)
assert.match(view, /货物明细/)
assert.match(view, /仓库唛头 \*/)
assert.match(view, /downloadTransferCartonMarkTemplate/)
assert.match(view, /importTransferCartonMarks/)
assert.match(view, /<strong>SKU 关联<\/strong>/)
assert.match(view, /<strong>箱号关联<\/strong>/)
assert.match(view, /<strong>SKU 混箱<\/strong>/)
assert.match(view, />下载模板<\/el-button>/)
assert.match(view, />上传文件<\/el-button>/)
assert.match(view, /换标类型/)
assert.match(view, /v-if="labelingMode === 'carton'"/)
assert.match(view, /v-else-if="labelingMode === 'sku'"/)
assert.match(view, /v-else class="warehouse-table-scroll"/)
assert.match(view, /添加 SKU 明细/)
assert.match(view, /绑定明细/)
assert.match(view, /旧 SKU \*.*新 SKU \*.*换标数量(件)\*.*新 FBA 箱唛 \*.*备注/)
assert.match(transferSubmit, /fbaBoxNos: customerCartonMarks/)
assert.match(transferSubmit, /shippingMark: transferCustomerMarks\(item\)/)
assert.match(transferSubmit, /relabelItems: form\.relabel/)
assert.match(view, /props\.service === 'disposal' \? '本次废弃箱数' : '预计箱数'/)
assert.match(view, /<th v-if="props\.service === 'relabel'">FNSKU \/ 箱唛<\/th>/)
assert.doesNotMatch(view, /<th v-if="props\.service === 'transfer'">FNSKU \/ 箱唛<\/th>/)
})
test('仓储转运填写货物即可打印,保存或提交保留已打印箱号', () => {
assert.doesNotMatch(view, /printTransferDraftCartonMarks|CT-\$\{batch\}/)
assert.match(view, /getTransferCartonLabel\(transferNo\)/)
assert.match(view, /打印预报箱唛/)
assert.match(view, /@click="printForecastCartons"/)
assert.match(view, /cartonMarks: item.generatedCartonMarks/)
})
test('仓储转运换标资料按步骤分组展示', () => {
assert.match(view, /<span v-if="props\.service === 'transfer'" class="warehouse-step-number">1<\/span>货物明细/)
assert.match(view, /<span class="warehouse-step-number">2<\/span>换标申请/)
assert.match(view, /<strong><span>3<\/span>明细关联模板<\/strong>/)
assert.match(view, /<span class="warehouse-step-number">4<\/span>绑定明细/)
assert.match(view, /warehouse-transfer-template-group/)
assert.match(view, /warehouse-transfer-relabel/)
})
test('仓储转运需要换标时上传总文件并挂到关联换标单', () => {
const transferRelabelSection = view.split('warehouse-transfer-relabel">')[1]?.split('warehouse-transfer-submit-bar')[0] || ''
assert.match(transferRelabelSection, /warehouse-transfer-total-files/)
assert.match(transferRelabelSection, />贴标总文件</)
assert.match(transferRelabelSection, />箱唛总文件</)
assert.match(transferRelabelSection, />SKU 总文件</)
assert.match(view, /action === 'submit' && requiresBothTotalFiles\.value/)
assert.match(view, /const submitted = await submitTransfer\(transferNo\)/)
assert.match(view, /submitted\.relabelNo \|\| created\.relabelNo/)
assert.match(view, /uploadRelabelFile\(relabelNo, 'CARTON_TOTAL', cartonTotalFile\.value\)/)
assert.match(view, /uploadRelabelFile\(relabelNo, 'SKU_TOTAL', skuTotalFile\.value\)/)
assert.match(view, /转运单已提交,附件待补传/)
assert.match(transferRelabelSection, /native-type="button"[^>]+chooseCreateTotalFile\('CARTON_TOTAL'\)/)
assert.match(transferRelabelSection, /native-type="button"[^>]+chooseCreateTotalFile\('SKU_TOTAL'\)/)
})
test('仓储转运换标总文件与明细模板在桌面端左右排列', () => {
assert.match(view, /class="warehouse-transfer-file-groups(?:\s[^"]*)?"/)
assert.match(view, /\.warehouse-transfer-file-groups\{display:grid;grid-template-columns:minmax\(0,1\.15fr\) minmax\(360px,\.85fr\)/)
assert.match(view, /\.warehouse-transfer-total-files,\.warehouse-transfer-template-group\{height:100%;margin:0\}/)
assert.match(view, /@media\(max-width:1180px\)\{\.warehouse-transfer-file-groups\{grid-template-columns:minmax\(0,1fr\)\}\}/)
})
test('上门提货不提交物流号,需要换标时随转运单登记申请', () => {
assert.match(view, /trackingNo:\s*!inventoryTransfer\.value && form\.deliveryMethod === 'CUSTOMER_DELIVERY'\s*\?[^:]+:\s*undefined/)
assert.match(view, /relabelItems: form\.relabel/)
assert.match(view, /form\.deliveryMethod === 'CUSTOMER_DELIVERY'.*物流跟踪号/s)
assert.match(view, /提货联系人/)
assert.match(view, /联系人电话/)
assert.match(view, /换标联系人/)
assert.match(view, /form\.pickupContact/)
assert.match(view, /form\.pickupPhone/)
assert.match(view, /form\.relabelContact/)
assert.match(view, /form\.relabelPhone/)
assert.match(view, /pickupContactName: !inventoryTransfer\.value && form\.deliveryMethod === 'WAREHOUSE_PICKUP' \? form\.pickupContact : undefined/)
assert.match(view, /pickupContactPhone: !inventoryTransfer\.value && form\.deliveryMethod === 'WAREHOUSE_PICKUP' \? form\.pickupPhone : undefined/)
assert.match(view, /relabelContactName: form\.relabel \? form\.relabelContact/)
assert.match(api, /pickupContactName\?: string; pickupContactPhone\?: string/)
})
test('仓储转运列表操作仓使用海外仓接口数据', () => {
assert.match(view, /loadAllWarehouseRecords\('OVERSEAS', getMerchantWarehouses\)/)
assert.match(view, /warehouseDisplay\(item\.operatingWarehouseId\)/)
assert.match(view, /\['OVERSEAS', 'OVERSEAS_PARTNER'\]/)
assert.match(view, /props\.service === 'relabel' \? fbaWarehouses\.value : operationWarehouses\.value/)
assert.doesNotMatch(view, /美西洛杉矶仓/)
})
test('仓储转运期望发货时间可选并在订单概览回填发货时间', () => {
assert.doesNotMatch(view, /预计发货时间/)
assert.match(view, /期望发货时间/)
assert.match(view, /v-model="form\.estimatedShipAt"/)
assert.match(view, /v-model="form\.estimatedShipAt"[^>]+placeholder=""/)
assert.doesNotMatch(view, /!form\.estimatedShipAt/)
assert.match(view, /estimatedShipAt: form\.estimatedShipAt/)
assert.match(view, /<span>期望发货时间<\/span><strong>\{\{ formatDateTime\(selectedDetail\?\.estimatedShipAt\) \}\}<\/strong>/)
assert.match(view, /<span>实际发货时间<\/span><strong>\{\{ formatDateTime\(selectedDetail\?\.actualShipAt\) \}\}<\/strong>/)
assert.doesNotMatch(view, /class="warehouse-card warehouse-form-section warehouse-transfer-times"/)
assert.match(view, /estimatedArrivalAt: detail\.estimatedArrivalAt \|\| '', estimatedShipAt: detail\.estimatedShipAt \|\| ''/)
assert.match(api, /estimatedShipAt\?: string/)
})
test('客户仓储转运展示每个实际流转状态的中文名称', () => {
assert.match(view, /label: '草稿', value: 'DRAFT'/)
assert.match(view, /label: '待审核', value: 'PENDING_REVIEW'/)
assert.match(view, /label: '入库测量中', value: 'INBOUND_MEASURING'/)
assert.match(view, /label: '待客户确认实测', value: 'WAIT_CUSTOMER_CONFIRMATION'/)
assert.match(view, /label: '待确认目的地', value: 'WAIT_FBA_DESTINATION'/)
assert.match(view, /label: '待排车', value: 'READY_FOR_DISPATCH'/)
assert.match(view, /WAIT_OUTBOUND: '待出库'/)
assert.match(view, /OUTBOUNDED: '已出库'/)
})
test('仓储转运列表展示换标单号、换标状态并为两类状态着色', () => {
const transferConfig = view.match(/transfer:\s*\{[^\n]+/)?.[0] || ''
assert.match(transferConfig, /'是否换标', '换标单号', '换标状态', '状态'/)
assert.match(api, /relabelNo\?: string; relabelStatus\?: string; relabelFulfillmentStatus\?: string/)
assert.match(view, /item\.needRelabel \? '是' : '否', item\.relabelNo, relabelStatus\.label/)
assert.match(view, /if \(!item\.relabelNo\) return \{ code: 'WAIT_RELABEL', label: '待换标' \}/)
assert.match(view, /READY_FOR_OUTBOUND: '已换标', COMPLETED: '已换标'/)
assert.match(view, /#transferRelabelStatus=.*transferRelabelStatusTagType/)
assert.match(view, /#transferStatus=.*transferStatusTagType/)
assert.doesNotMatch(view, /是 · 待绑定换标单/)
})
test('仓储转运详情订单概览使用详情接口命名字段回填', () => {
assert.match(view, /v-else-if="props\.service === 'transfer'" class="warehouse-detail-grid warehouse-transfer-overview-grid"/)
assert.doesNotMatch(view, /入库箱数/)
assert.match(view, /'转运单号', '预计箱数', '海外仓'/)
for (const field of ['transferNo', 'packageCount', 'operatingWarehouseId', 'deliveryMethod', 'needRelabel', 'relabelNo', 'status', 'createdAt']) {
assert.match(view, new RegExp(`selectedDetail\\?\\.${field}`))
}
assert.match(view, /selectedDetail\?\.destinationWarehouseCode \|\| selectedDetail\?\.fbaWarehouseCode/)
assert.match(view, /transferRelabelStatus\(selectedDetail \|\| \{\}\)/)
assert.match(view, /transferStatusTagType\(selectedDetail\?\.status \|\| ''\)/)
})
test('仓储转运和贴标申请详情先展示主数据再串行补充仓库名称', () => {
const loader = view.match(/async function loadDetailPage\(\) \{[\s\S]*?\n\}/)?.[0] || ''
assert.match(loader, /await openDetail\(\{ '0': detailNumber\.value \}\)/)
assert.match(loader, /await loadWarehouses\(\)/)
assert.ok(loader.indexOf('await openDetail') < loader.indexOf('await loadWarehouses'))
assert.match(view, /props\.service === 'labeling' \? await getRelabel\(value\['0'\]\)/)
assert.match(view, /: props\.service === 'disposal' \? await getDiscard\(value\['0'\]\) : await getTransfer\(value\['0'\]\)/)
})
test('仓储转运详情展示目的去向和对应目的仓资料并让货物明细铺满', () => {
assert.match(view, />目的去向<\/span><strong>\{\{ destinationTypeLabel\(selectedDetail\?\.destinationType, selectedDetail\?\.fbaWarehouseCode\) \}\}/)
assert.match(view, />FBA 目的仓<\/span>.*selectedDetail\?\.destinationWarehouseCode \|\| selectedDetail\?\.fbaWarehouseCode/)
assert.match(view, />FBA 货件号<\/span><strong>\{\{ selectedDetail\?\.fbaShipmentNo \|\| '—' \}\}/)
for (const field of ['privateDestinationAddress', 'privateDestinationPostalCode', 'privateDestinationContactName', 'privateDestinationContactPhone']) {
assert.match(view, new RegExp(`selectedDetail\\?\\.${field}`))
}
assert.match(view, /warehouse-service-page--detail \.warehouse-transfer-cargo-card \.warehouse-section-heading,[^}]+\.warehouse-table-scroll\{max-width:none\}/)
})
test('仓储转运订单概览使用紧凑左右信息布局', () => {
assert.match(view, /warehouse-detail-grid warehouse-transfer-overview-grid/)
assert.match(view, /warehouse-transfer-overview-grid"><div><span>状态<\/span>.*<span>目的去向<\/span>/)
assert.match(view, /class="warehouse-overview-wide"><span>私人仓地址/)
assert.match(view, /warehouse-transfer-overview-grid>\.warehouse-overview-wide\{grid-column:span 2\}/)
assert.match(view, /warehouse-transfer-overview-grid>div\{min-height:26px;padding:2px 0;border:0\}/)
assert.match(view, /warehouse-transfer-overview-grid span\{flex:0 0 auto;min-width:76px\}/)
assert.match(view, /warehouse-transfer-overview-grid\{grid-template-columns:1fr;gap:4px/)
})
test('贴标申请详情使用紧凑订单概览并中文展示换标状态', () => {
assert.match(view, /WAIT_APPROVAL: '待审核'/)
assert.match(view, /WAIT_PROCESSING: '待仓库处理'/)
assert.match(view, /relabelFulfillmentLabel\(selectedRelabelDetail\?\.application\.fulfillmentStatus\)/)
assert.match(view, />\.warehouse-detail-header\+\.warehouse-card \.warehouse-detail-grid\{grid-template-columns:repeat\(4,minmax\(180px,1fr\)\)/)
})
test('仓储转运详情按接口具名字段展示订单概览', () => {
const overview = view.split('<h3>订单概览</h3>')[1]?.split('warehouse-transfer-cargo-card')[0] || ''
assert.match(overview, /v-if="props\.service === 'transfer'"/)
assert.doesNotMatch(overview, /<span>转运单号<\/span>/)
assert.match(view.split('<header class="warehouse-detail-header">')[1]?.split('</header>')[0] || '', /selectedDetail\?\.transferNo/)
assert.match(overview, /selectedDetail\?\.packageCount \?\? '—'/)
assert.match(overview, /warehouseDisplay\(selectedDetail\?\.operatingWarehouseId\)/)
assert.match(overview, /selectedDetail\?\.destinationWarehouseName \|\| warehouseDisplay\(selectedDetail\?\.destinationWarehouseCode \|\| selectedDetail\?\.fbaWarehouseCode\)/)
assert.match(overview, /deliveryMethodLabel\(selectedDetail\?\.deliveryMethod \|\| ''\)/)
assert.match(overview, /statusLabel\(selectedDetail\?\.status \|\| ''\)/)
assert.match(overview, /formatDateTime\(selectedDetail\?\.createdAt\)/)
})
test('PDA 打托完成后客户可从列表或详情确认并查看实测数据', () => {
assert.match(view, /item\.__status === 'WAIT_CUSTOMER_CONFIRMATION'/)
assert.match(view, /confirmTransferFromList\(item\['0'\]\)/)
assert.match(view, /selectedDetail\?\.status === 'WAIT_CUSTOMER_CONFIRMATION'/)
assert.match(view, /PDA 打托实测数据/)
for (const label of ['托盘号', '库位', '箱数', 'SKU 汇总', '毛重(kg)', '体积(m³)', '完成时间']) assert.equal(view.includes(label), true)
assert.match(view, /pallet\.warehouseLocationCode/)
assert.match(view, /palletCartons\(pallet\)\.length/)
assert.match(view, /pallet\.grossWeightKg/)
assert.match(view, /pallet\.volumeCbm/)
assert.doesNotMatch(view, /pallet\.items/)
})
test('仓储转运详情按订单概览、货物预报、PDA 实测数据排序并放大测量照片', () => {
const detailStart = view.indexOf('<header class="warehouse-detail-header">')
const detailSource = view.slice(detailStart)
const overviewIndex = detailSource.indexOf('<h3>订单概览</h3>')
const measurementIndex = detailSource.indexOf('<h3>PDA 打托实测数据</h3>')
const cargoIndex = detailSource.indexOf('<h3>预报与实际签收</h3>')
assert.ok(overviewIndex >= 0 && overviewIndex < measurementIndex)
assert.ok(cargoIndex < measurementIndex)
assert.match(view, /class="warehouse-photo-grid warehouse-measurement-photo-grid"/)
assert.match(view, /class="warehouse-photo-item warehouse-measurement-photo-item"/)
assert.match(view, /warehouse-measurement-photo-grid\{flex:1;grid-template-columns:repeat\(auto-fill,minmax\(240px,1fr\)\)\}/)
assert.match(view, /warehouse-measurement-photo-item\{width:100%;height:120px\}/)
})
test('仓储转运详情自动加载 PDA 测量照片并释放 Blob URL', () => {
assert.match(api, /downloadTransferMeasurementPhoto/)
assert.match(api, /measurement-photos\/files\/\$\{encodeURIComponent\(fileId\)\}\/download/)
assert.match(view, /measurementPhotos\?: TransferMeasurementPhoto\[\]/)
assert.match(view, /fileId: string \| number/)
assert.match(view, /if \(props\.service === 'transfer'\) void loadMeasurementPhotos\(\)/)
assert.match(view, /测量打托现场照片/)
assert.match(view, /:preview-src-list="measurementPhotoPreviewUrls"/)
assert.match(view, /加载失败,点击重试/)
assert.match(view, /URL\.createObjectURL\(blob\)/)
assert.match(view, /URL\.revokeObjectURL\(url\)/)
assert.match(view, /onBeforeUnmount\(\(\) => \{ clearCompletionPhotos\(\); clearMeasurementPhotos\(\); clearDiscardPhotos\(\) \}\)/)
})
test('PDA 完成后主单显示入库完成并保留客户确认测量入口', () => {
assert.match(view, /item\.__status === 'WAIT_CUSTOMER_CONFIRMATION'.*确认测量/s)
assert.match(view, /selectedDetail\?\.status === 'WAIT_CUSTOMER_CONFIRMATION'.*确认测量数据/s)
assert.doesNotMatch(view, /确认实测并入库/)
})
test('仓储转运可在新建页直接提交,也可保存草稿后编辑提交', () => {
assert.match(api, /function submitTransfer\(transferNo: string\)/)
assert.match(api, /function updateTransfer\(transferNo: string, payload: TransferCreate\)/)
assert.match(api, /warehouse-transfers\/\$\{encodeURIComponent\(transferNo\)\}\/save-draft/)
assert.match(api, /warehouse-transfers\/\$\{encodeURIComponent\(transferNo\)\}\/submit/)
assert.match(view, /submit\('draft'\).*保存草稿/s)
assert.match(view, /async function openEdit\(transferNo: string\)/)
assert.match(view, /\['DRAFT', 'REJECTED'\]\.includes\(item\.__status\).*openEdit\(item\['0'\]\)/s)
assert.match(view, /\['DRAFT', 'REJECTED'\]\.includes\(detail\.status \|\| ''\)/)
assert.match(view, /\['DRAFT', 'REJECTED'\]\.includes\(selectedDetail\?\.status \|\| ''\)/)
assert.match(view, /v-if="props\.service === 'transfer'"[^>]*type="primary"[^>]*native-type="submit"[^>]*>提交订单/)
assert.match(view, /await updateTransfer\(editingTransferNo\.value, payload\)/)
assert.match(view, /await submitTransfer\(transferNo\)/)
assert.match(view, /<form [^>]*@submit\.prevent="submit\('submit'\)"/)
assert.match(view, /selectedDetail\?\.status !== 'DRAFT'[^>]*[\s\S]*?printTransferCartons[^>]*[\s\S]*?打印预报箱唛/)
assert.doesNotMatch(view, /row\.__status !== 'DRAFT'.*printTransferCartons\(row\['0'\]\)/s)
assert.doesNotMatch(view, /selectedDetail\?\.status === 'DRAFT'[^>]*[\s\S]*?>提交订单/)
assert.match(view, /native-type="button"[^>]*:loading="saving"[^>]*@click="submit\('draft'\)"/)
assert.match(view, /DRAFT: '草稿'/)
assert.doesNotMatch(view, /<h3>箱唛明细<\/h3>/)
})
test('编辑仓储转运时加载库存转运来源候选', () => {
assert.match(view, /async function loadTransferInventoryCandidates\(warehouseId: string\)/)
assert.match(view, /if \(props\.service === 'transfer' && form\.warehouse\) \{[\s\S]*await loadTransferInventoryCandidates\(form\.warehouse\)/)
assert.match(view, /transferInventoryCandidates\.value = \(await listRelabelInventoryCandidates\(warehouseId\)\)\.filter/)
assert.match(view, /transferInventoryOptions\.value\.some\(\(option\) => option\.value === sourceInboundCode\)/)
assert.match(view, /selectedTransferInventory\.value = sourceInboundCode/)
assert.match(view, /detail\.sourceOrders \|\| \[\]/)
})
test('仓储转运来源订单雪花 ID 按字符串传递,避免 Number 精度丢失', () => {
assert.match(view, /const selectedTransferSourceOrderIds = ref<string\[\]>\(\[\]\)/)
assert.match(view, /historical\.map\(\(item\) => String\(item\.id \|\| ''\)\)/)
assert.match(api, /sourceLastlegOrderIds\?: string\[\]/)
assert.doesNotMatch(view, /sourceLastlegOrderIds[\s\S]{0,160}Number\(/)
})
test('转运贴标单在PDA确认库存前不误报附件上传失败', () => {
assert.match(view, /if \(!relabelNo\) \{[\s\S]*PDA 入库确认后将自动生成关联换标单/)
assert.match(view, /届时请进入关联换标单详情上传贴标总文件/)
assert.doesNotMatch(view, /if \(!relabelNo\) throw new Error\('转运单已提交,但未返回关联换标单号'\)/)
})
test('仓储转运基础货物不强制 SKU 和中文品名,只换箱唛不强制旧 SKU', () => {
assert.match(view, /商品 SKU\{\{ props\.service === 'transfer' \? '' : ' \*' \}\}/)
assert.match(view, /中文品名\{\{ props\.service === 'transfer' \? '' : ' \*' \}\}/)
assert.match(view, /props\.service === 'relabel' \? '请输入商品 SKU' : ''/)
assert.match(view, /v-if="labelingMode === 'carton'"[^\n]+<th>旧 SKU<\/th>/)
assert.match(view, /labelingMode\.value !== 'carton' && \(!row\.sku\.trim\(\) \|\| !row\.newSkuCode\.trim\(\)\)/)
assert.match(api, /skuCode\?: string; productNameZh\?: string/)
assert.match(api, /originalSku\?: string/)
})
test('SKU 件数和预计箱数独立提交,签收展示实际清点箱数', () => {
assert.match(view, /quantity: item.quantity, packageCount: Number\(item.expectedPackageCount\)/)
assert.match(view, /sum \+ Number\(item.expectedPackageCount \|\| 0\)/)
assert.match(view, /v-model="row.expectedPackageCount"/)
assert.match(view, /Number.isSafeInteger\(row.quantity\)/)
assert.match(view, /Number.isSafeInteger\(row.expectedPackageCount\)/)
assert.match(view, /\['COUNTED', 'DISCARD_PENDING', 'DISCARDED', 'SHIPPED'\]\.includes\(carton.status\)/)
assert.match(view, /实际签收箱数/)
})
test('转运客户详情保留预报并显示实际签收箱内明细和托盘汇总', () => {
for (const label of ['预报数量(件)', '预报 SKU 种数', '签收箱号明细', '查看箱号明细', 'SKU 汇总', '毛重(kg)', '体积(m³)']) assert.ok(view.includes(label), label)
assert.match(view, /countedTransferCartons\(\)\.filter\(\(carton\) => pallet.id != null/)
assert.match(view, /String\(carton.transferPalletId\) === String\(pallet.id\)/)
assert.match(view, /carton.transferItemId != null && String\(item.id\) === String\(carton.transferItemId\)/)
assert.match(view, /@click="showTransferCartons = true"/)
assert.match(view, /<el-dialog v-model="showTransferCartons"[\s\S]*?<el-collapse-item v-for="carton in countedTransferCartons\(\)"/)
assert.doesNotMatch(view, /warehouse-transfer-receipt-summary|expandedTransferCartons/)
const receiptSection = view.slice(view.indexOf('warehouse-transfer-received-card', view.indexOf('<header class="warehouse-detail-header">')), view.indexOf('v-if="props.service === \'transfer\' && selectedDetail?.needRelabel"'))
assert.doesNotMatch(receiptSection.split('<el-dialog')[0], /<table|<el-table|<el-collapse/)
assert.match(view, /暂无实际签收箱,等待 PDA 清点/)
assert.doesNotMatch(view, /<td>\{\{ \[pallet.lengthCm/)
})
test('预报实收同区对照、托盘卡片换行、签收按钮紧邻标题', () => {
const start = view.indexOf('class="warehouse-card warehouse-form-section warehouse-transfer-cargo-card"', view.indexOf('<header class="warehouse-detail-header">'))
const section = view.slice(start, view.indexOf('warehouse-transfer-received-card', start))
assert.ok(section.includes('预报与实际签收'))
assert.ok(section.includes('PDA 打托实测数据'))
assert.ok(section.includes('receivedItemQuantity(item.id)'))
assert.ok(section.includes('receivedItemCartons(item.id).length'))
assert.ok(section.includes('class="warehouse-transfer-pallet-card"'))
assert.ok(section.includes('class="warehouse-transfer-pallet-sku"'))
assert.doesNotMatch(section, /打印箱唛/)
const receipt = view.slice(view.indexOf('warehouse-transfer-received-card', start)).split('<el-dialog')[0]
assert.ok(view.includes('打印预报箱唛')); assert.ok(view.includes('增加预报箱数'))
assert.ok(receipt.includes('查看箱号明细'))
assert.match(view, /warehouse-section-heading h3\{flex:0 0 auto/)
})