1
This commit is contained in:
@@ -296,6 +296,8 @@ export interface MerchantFirstLegOrderRow {
|
||||
inboundedAt: string | null
|
||||
businessDate: string | null
|
||||
shipmentId: string | null
|
||||
fulfillmentType?: 'STANDARD' | 'ONE_CLICK_DROPSHIP' | null
|
||||
lxCode?: string | null
|
||||
createdAt: string
|
||||
cargoes?: Array<{ skuNo?: string | null }>
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ test('shows the published merchant inbound, measurement details and customer con
|
||||
assert.match(source, /latestFee\.rateCode/)
|
||||
assert.match(source, /latestFee\.chargeRuleVersion/)
|
||||
assert.match(source, /latestFee\.carrierRateVersion/)
|
||||
assert.doesNotMatch(source, /箱规规则|calculationRuleVersion/)
|
||||
assert.match(source, /箱规规则/)
|
||||
assert.match(source, /getCalculationRuleLabel\(row\.calculationRuleVersion\)/)
|
||||
})
|
||||
|
||||
test('stacks core information for 720 and 360 pixel layouts', () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
formatInboundMeasure,
|
||||
formatInboundMoney,
|
||||
formatInboundNumber,
|
||||
getCalculationRuleLabel,
|
||||
getChargeModeLabel,
|
||||
getTaxModeLabel,
|
||||
} from '@/modules/first-leg/utils/inboundBilling'
|
||||
@@ -139,6 +140,7 @@ async function confirmFee() {
|
||||
<el-table-column label="每箱体积" min-width="105"><template #default="{ row }">{{ formatInboundMeasure(row.unitVolumeCbm, 'm³') }}</template></el-table-column>
|
||||
<el-table-column label="总体积" min-width="105"><template #default="{ row }">{{ formatInboundMeasure(row.totalVolumeCbm, 'm³') }}</template></el-table-column>
|
||||
<el-table-column label="总材积重" min-width="105"><template #default="{ row }">{{ formatInboundMeasure(row.volumeWeightKg, 'kg') }}</template></el-table-column>
|
||||
<el-table-column label="箱规规则" min-width="170"><template #default="{ row }">{{ getCalculationRuleLabel(row.calculationRuleVersion) }}</template></el-table-column>
|
||||
<el-table-column prop="boxSpecification" label="备注" min-width="130" />
|
||||
</el-table>
|
||||
</div>
|
||||
@@ -241,6 +243,7 @@ async function confirmFee() {
|
||||
<el-table-column label="每箱体积" min-width="105"><template #default="{ row }">{{ formatInboundMeasure(row.unitVolumeCbm, 'm³') }}</template></el-table-column>
|
||||
<el-table-column label="总体积" min-width="105"><template #default="{ row }">{{ formatInboundMeasure(row.totalVolumeCbm, 'm³') }}</template></el-table-column>
|
||||
<el-table-column label="总材积重" min-width="105"><template #default="{ row }">{{ formatInboundMeasure(row.volumeWeightKg, 'kg') }}</template></el-table-column>
|
||||
<el-table-column label="箱规规则" min-width="170"><template #default="{ row }">{{ getCalculationRuleLabel(row.calculationRuleVersion) }}</template></el-table-column>
|
||||
<el-table-column prop="boxSpecification" label="备注" min-width="130" />
|
||||
</el-table>
|
||||
<el-alert
|
||||
|
||||
@@ -12,7 +12,7 @@ test('maps the first-leg aggregate list fields without loading detail rows', ()
|
||||
assert.match(apiSource, /cargoes\?: Array<\{ skuNo\?: string \| null \}>/)
|
||||
assert.match(apiSource, /apiRequest<PageResult<MerchantFirstLegOrderRow>>/)
|
||||
assert.match(storeSource, /function mapFirstLegOrderRow/)
|
||||
for (const field of ['inboundId', 'inboundCode', 'channelName', 'destinationWarehouseName', 'pickupMethod', 'packageCount', 'expressCompany', 'trackingNo', 'totalGrossWeightKg', 'totalNetWeightKg', 'totalVolumeCbm', 'totalQuantity', 'remark', 'shipmentId', 'referenceId', 'createdAt']) {
|
||||
for (const field of ['inboundId', 'inboundCode', 'channelName', 'destinationWarehouseName', 'pickupMethod', 'packageCount', 'expressCompany', 'trackingNo', 'totalGrossWeightKg', 'totalNetWeightKg', 'totalVolumeCbm', 'totalQuantity', 'remark', 'shipmentId', 'fulfillmentType', 'lxCode', 'referenceId', 'createdAt']) {
|
||||
assert.match(storeSource, new RegExp(`row\\.${field}`))
|
||||
}
|
||||
assert.match(storeSource, /row\.skuNos/)
|
||||
@@ -102,7 +102,9 @@ test('keeps unified order pagination valid when the store omits page and size',
|
||||
|
||||
test('keeps drafts on the legacy read path without assigning a unified main status', () => {
|
||||
assert.match(storeSource, /getMerchantFirstLegOrders\(legacyQuery\(query, 'DRAFT'\)\)/)
|
||||
assert.match(storeSource, /getById\(id\)\?\.status === 'DRAFT' \? loadLegacyDetail\(id\) : loadUnifiedDetail\(id\)/)
|
||||
assert.match(storeSource, /const listOrder = getById\(id\)/)
|
||||
assert.match(storeSource, /listOrder\?\.status === 'DRAFT' \? loadLegacyDetail\(id, listOrder\) : loadUnifiedDetail\(id\)/)
|
||||
assert.match(storeSource, /detail\.order\.lxCode == null && listOrder\?\.lxCode/)
|
||||
assert.match(storeSource, /return loadLegacyDetail\(String\(createdId\)\)/)
|
||||
assert.match(storeSource, /draftLoadError/)
|
||||
})
|
||||
|
||||
@@ -359,7 +359,7 @@ function mapFirstLegOrderRow(row: MerchantFirstLegOrderRow): FirstLegOrder {
|
||||
const uniqueSkuNos = [...new Set(resolvedSkuNos)]
|
||||
return {
|
||||
id: String(row.orderId), orderNo: row.orderNo, referenceNo: row.referenceId ?? '', transportMode: null,
|
||||
businessType: 'EXPORT', origin: '', destination: row.destinationWarehouseName ?? '', receivingWarehouseId: '',
|
||||
businessType: 'EXPORT', fulfillmentType: row.fulfillmentType ?? 'STANDARD', lxCode: row.lxCode ?? '', origin: '', destination: row.destinationWarehouseName ?? '', receivingWarehouseId: '',
|
||||
receivingWarehouseName: '', receivingWarehouseSnapshot: null, expectedDepartureDate: '', etd: '', eta: '',
|
||||
shipperPartyId: '', shipperPartySnapshot: null, shipper: '', consigneePartyId: '', consigneePartySnapshot: null,
|
||||
consignee: row.receivingCompanyName ?? '', expressCompanyName: row.expressCompany ?? '', trackingNumber: row.trackingNo ?? '',
|
||||
@@ -542,9 +542,19 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', ()
|
||||
return mapped
|
||||
}
|
||||
|
||||
async function loadLegacyDetail(id: string) {
|
||||
async function loadLegacyDetail(id: string, listOrder?: FirstLegOrder) {
|
||||
const detail = await getMerchantFirstLegOrder(id)
|
||||
return upsertOrder(mapOrder(detail.order, warehouses.value, detail))
|
||||
const mapped = mapOrder(detail.order, warehouses.value, detail)
|
||||
// Older detail responses may omit newly added dropship fields. Keep the
|
||||
// values already returned by the list row so editing a draft does not
|
||||
// clear the persisted LX code in the form.
|
||||
if (detail.order.fulfillmentType == null && listOrder?.fulfillmentType) {
|
||||
mapped.fulfillmentType = listOrder.fulfillmentType
|
||||
}
|
||||
if (detail.order.lxCode == null && listOrder?.lxCode) {
|
||||
mapped.lxCode = listOrder.lxCode
|
||||
}
|
||||
return upsertOrder(mapped)
|
||||
}
|
||||
|
||||
async function loadUnifiedDetail(id: string) {
|
||||
@@ -558,7 +568,8 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', ()
|
||||
}
|
||||
|
||||
async function loadDetail(id: string) {
|
||||
return getById(id)?.status === 'DRAFT' ? loadLegacyDetail(id) : loadUnifiedDetail(id)
|
||||
const listOrder = getById(id)
|
||||
return listOrder?.status === 'DRAFT' ? loadLegacyDetail(id, listOrder) : loadUnifiedDetail(id)
|
||||
}
|
||||
|
||||
async function loadWarehouseDetail(id: string) {
|
||||
@@ -616,7 +627,7 @@ export const useFirstLegOrdersStore = defineStore('client-first-leg-orders', ()
|
||||
}
|
||||
|
||||
async function loadDraftDetail(id: string) {
|
||||
return loadLegacyDetail(id)
|
||||
return loadLegacyDetail(id, getById(id))
|
||||
}
|
||||
|
||||
return { orders, warehouses, loading, loadedFromApi, loadError, warehouseLoadError, draftLoadError, activeCount, pendingCount, getById, load, loadDetail, loadDraftDetail, loadWarehouseDetail, loadWarehouseType, saveDraft, saveDraftBatch, submitSaved, submit }
|
||||
|
||||
@@ -19,6 +19,13 @@ test('translates backend rounding modes into customer-facing calculation labels'
|
||||
assert.match(source, /return value \? labels\[value\] \|\| value : '-'/)
|
||||
})
|
||||
|
||||
test('translates carton calculation rules into customer-facing labels', () => {
|
||||
assert.match(source, /export function getCalculationRuleLabel/)
|
||||
assert.match(source, /ACTUAL_DIMENSION_V1/)
|
||||
assert.match(source, /按实际箱规计算/)
|
||||
assert.match(source, /CUSTOMER_DIMENSION_\(SMALL\|MEDIUM\|LARGE\|EXTRA_LARGE\)_V/)
|
||||
})
|
||||
|
||||
test('renders conversion metadata as one complete label or a single empty marker', () => {
|
||||
assert.match(source, /export function formatConversionPrecision/)
|
||||
assert.match(source, /roundingScale === null \|\| roundingScale === undefined/)
|
||||
|
||||
@@ -46,6 +46,20 @@ export function getTaxModeLabel(value: string) {
|
||||
return value || '-'
|
||||
}
|
||||
|
||||
export function getCalculationRuleLabel(value: string | null | undefined) {
|
||||
if (!value) return '-'
|
||||
if (value === 'ACTUAL_DIMENSION_V1' || value === 'DIMENSION_ACTUAL_V1') return '按实际箱规计算'
|
||||
const customerRule = /^CUSTOMER_DIMENSION_(SMALL|MEDIUM|LARGE|EXTRA_LARGE)_V(\d+)$/.exec(value)
|
||||
if (customerRule) {
|
||||
const sizeLabel: Record<string, string> = {
|
||||
SMALL: '小号', MEDIUM: '中号', LARGE: '大号', EXTRA_LARGE: '超大号',
|
||||
}
|
||||
return `客户箱规规则(${sizeLabel[customerRule[1]]},V${customerRule[2]})`
|
||||
}
|
||||
const warehouseRule = /^WAREHOUSE_OUTER_OVERRIDE_(.+)$/.exec(value)
|
||||
return warehouseRule ? `仓库外箱规覆盖(${warehouseRule[1]})` : value
|
||||
}
|
||||
|
||||
export function getRoundingModeLabel(value: string | null | undefined) {
|
||||
const labels: Record<string, string> = {
|
||||
UP: '向上舍入',
|
||||
|
||||
@@ -106,6 +106,7 @@ interface ServiceDetail extends Record<string, unknown> {
|
||||
estimatedShipAt?: string; actualShipAt?: string; fbaShipmentNo?: string; trackingNo?: string
|
||||
remark?: string; needRelabel?: boolean; labelDeadline?: string; relabelContactName?: string
|
||||
relabelContactPhone?: string; relabelDeliveryMethod?: string
|
||||
createdAt?: string
|
||||
items?: Array<{ skuCode?: string; productNameZh?: string; packageCount?: number; shippingMark?: string }>
|
||||
relabelBindings?: Array<{ operationType?: string; customerCartonMark?: string; originalSku?: string; newSku?: string; quantity?: number; fbaCartonMark?: string; destinationWarehouseCode?: string; remark?: string }>
|
||||
cartons?: TransferCarton[]; pallets?: TransferPallet[]; measurementPhotos?: TransferMeasurementPhoto[]; customerConfirmedAt?: string
|
||||
@@ -1138,7 +1139,7 @@ function selectRelabelInventory(inboundOrderId: string) {
|
||||
<template v-else>
|
||||
<div class="warehouse-breadcrumb"><button type="button" @click="mode = 'list'">{{ config.title }}</button><b>/</b><strong>订单详情</strong></div>
|
||||
<div class="warehouse-page-heading"><h2>{{ config.title }}详情</h2><div><el-button v-if="canCancelDiscard && props.service === 'disposal' && selectedDetail?.status === 'PENDING_REVIEW'" type="danger" @click="cancelSelectedDiscard">取消申请</el-button><el-button v-if="canCreate && props.service === 'transfer' && selectedDetail?.status === 'DRAFT'" type="primary" @click="openEdit(String(selectedDetail?.transferNo || selectedDetail?.['0'] || ''))">编辑</el-button><el-button v-if="canConfirmTransfer && props.service === 'transfer' && selectedDetail?.status === 'WAIT_CUSTOMER_CONFIRMATION'" type="primary" @click="confirmTransferMeasurements">确认测量数据</el-button><el-button @click="mode = 'list'"><ArrowLeft :size="16" />返回列表</el-button></div></div>
|
||||
<div class="warehouse-card warehouse-form-section"><h3>订单概览</h3><div class="warehouse-detail-grid"><div v-for="(column, index) in config.columns.slice(1)" :key="column"><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '—' }}</strong></div></div></div>
|
||||
<div class="warehouse-card warehouse-form-section"><h3>订单概览</h3><div v-if="props.service === 'transfer'" class="warehouse-detail-grid"><div><span>转运单号</span><strong>{{ selectedDetail?.transferNo || '—' }}</strong></div><div><span>入库箱数</span><strong>{{ selectedDetail?.packageCount ?? '—' }}</strong></div><div><span>海外仓</span><strong>{{ warehouseDisplay(selectedDetail?.operatingWarehouseId) }}</strong></div><div><span>FBA 目的仓</span><strong>{{ selectedDetail?.destinationWarehouseName || warehouseDisplay(selectedDetail?.destinationWarehouseCode || selectedDetail?.fbaWarehouseCode) }}</strong></div><div><span>入仓方式</span><strong>{{ deliveryMethodLabel(selectedDetail?.deliveryMethod || '') }}</strong></div><div><span>是否换标</span><strong>{{ selectedDetail?.needRelabel ? '是' : '否' }}</strong></div><div><span>状态</span><strong>{{ statusLabel(selectedDetail?.status || '') }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedDetail?.createdAt) }}</strong></div></div><div v-else class="warehouse-detail-grid"><div v-for="(column, index) in config.columns.slice(1)" :key="column"><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '—' }}</strong></div></div></div>
|
||||
<div v-if="props.service === 'transfer'" class="warehouse-card warehouse-form-section warehouse-transfer-cargo-card">
|
||||
<div class="warehouse-section-heading"><h3>货物明细</h3><el-button v-if="selectedDetail?.status !== 'DRAFT'" native-type="button" type="primary" @click="printTransferCartons(String(selectedDetail?.transferNo || selectedDetail?.['0'] || ''))"><Printer :size="16" />打印箱唛</el-button></div>
|
||||
<div class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU</th><th>中文品名</th><th>预计箱数</th><th>客户箱唛号</th></tr></thead><tbody><tr v-for="(item, index) in selectedDetail?.items || []" :key="`${item.skuCode}-${index}`"><td>{{ item.skuCode || '—' }}</td><td>{{ item.productNameZh || '—' }}</td><td>{{ item.packageCount ?? '—' }}</td><td>{{ item.shippingMark || '—' }}</td></tr><tr v-if="!selectedDetail?.items?.length"><td colspan="4"><div class="warehouse-empty">暂无货物明细</div></td></tr></tbody></table></div>
|
||||
|
||||
@@ -127,6 +127,18 @@ test('客户仓储转运展示每个实际流转状态的中文名称', () => {
|
||||
assert.match(view, /OUTBOUNDED: '已出库'/)
|
||||
})
|
||||
|
||||
test('仓储转运详情按接口具名字段展示订单概览', () => {
|
||||
const overview = view.split('<h3>订单概览</h3>')[1]?.split('warehouse-transfer-cargo-card')[0] || ''
|
||||
assert.match(overview, /v-if="props\.service === 'transfer'"/)
|
||||
assert.match(overview, /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, /row\.__status === 'WAIT_CUSTOMER_CONFIRMATION'/)
|
||||
assert.match(view, /confirmTransferFromList\(row\['0'\]\)/)
|
||||
|
||||
Reference in New Issue
Block a user