Release/test 合并上生产 #2
@@ -16,7 +16,7 @@ function text(value: unknown) { return String(value ?? '').trim() || '—' }
|
||||
function status(value: unknown) { const labels: Record<string, string> = { PENDING_ASSIGNMENT: '待入库', PDA_PENDING: '待入库', ASSIGNED: '待入库', PICKING: '拣货中', PARTIAL_INBOUND: '部分入库', COMPLETED: '全部入库' }; return labels[String(value || '')] || text(value) }
|
||||
const firstLine = computed(() => detail.value?.lines[0])
|
||||
const fieldValues = computed<Record<string, string | number>>(() => ({
|
||||
入库单号: text(detail.value?.planNo), 入仓码: text(firstLine.value?.inboundCode), 类型: '—', 货主: '—', 关联订单: text(detail.value?.sourceOrderId),
|
||||
入库单号: text(detail.value?.planNo), 入仓码: text(firstLine.value?.inboundCode), 类型: '—', 货主: '—', 关联订单: '—',
|
||||
海外仓: text(firstLine.value?.destinationWarehouseName), FBA仓库: text(firstLine.value?.fbaWarehouseCode), 总箱数: detail.value?.expectedPackageCount ?? detail.value?.lines.reduce((sum, line) => sum + line.requiredPackageCount, 0) ?? 0,
|
||||
应拣货数: detail.value?.expectedPackageCount ?? detail.value?.lines.reduce((sum, line) => sum + line.requiredPackageCount, 0) ?? 0, 实际拣货数: detail.value?.pickedPackageCount ?? detail.value?.allocations.reduce((sum, item) => sum + item.pickedPackageCount, 0) ?? 0,
|
||||
'计费体积(m³)': Number(detail.value?.totalVolumeCbm || 0), 总托盘数: Number(detail.value?.palletCount || 0), 状态: status(detail.value?.status),
|
||||
|
||||
@@ -60,7 +60,7 @@ function selectableRow(source: object) {
|
||||
const row = source as InboundOrderListItem
|
||||
if (!row.fbaWarehouse || row.fbaWarehouse === '—') return false
|
||||
const anchor = selectedRows.value[0]
|
||||
return !anchor || anchor.fbaWarehouse === row.fbaWarehouse
|
||||
return !anchor || (anchor.customerId === row.customerId && anchor.fbaWarehouse === row.fbaWarehouse)
|
||||
}
|
||||
|
||||
const columns: DataTableColumn[] = [
|
||||
|
||||
@@ -234,8 +234,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
||||
|
||||
it('renders pending cabinets as an arrival-ordered workbench with the remaining inbound-code count', async () => {
|
||||
const nullArrival = { ...summary, sourceContainerBookingId: '2092873463568597002', containerNo: 'CONT-NULL', plannedArrivalAt: null, planCount: 0, pendingPlanCount: 15 }
|
||||
const completed = { ...summary, sourceContainerBookingId: '2092873463568597003', containerNo: 'CONT-DONE', plannedInboundCodeCount: 15, pendingPlanCount: 0, allPlansReady: true }
|
||||
vi.mocked(listContainerPlanProgress).mockResolvedValueOnce({ records: [completed, summary, nullArrival], page: 1, size: 10, total: 3, pages: 1 })
|
||||
vi.mocked(listContainerPlanProgress).mockResolvedValueOnce({ records: [summary, nullArrival], page: 1, size: 10, total: 2, pages: 1 })
|
||||
const wrapper = await mountView()
|
||||
const containerTab = wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))
|
||||
await containerTab!.trigger('click')
|
||||
@@ -250,7 +249,6 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
||||
expect(wrapper.text()).not.toContain('缺入仓码')
|
||||
expect(wrapper.text()).not.toContain('已生成 10 个计划')
|
||||
expect(wrapper.text()).not.toContain('存在5票尚未生成有效入库计划')
|
||||
expect(wrapper.text()).not.toContain('CONT-DONE')
|
||||
expect(wrapper.text()).toContain('2 个柜待处理')
|
||||
expect(wrapper.text().indexOf('CONT-15')).toBeLessThan(wrapper.text().indexOf('CONT-NULL'))
|
||||
})
|
||||
@@ -272,6 +270,10 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
||||
expect(document.body.textContent).toContain('LMO-1')
|
||||
expect(document.body.textContent).toContain('IN-1')
|
||||
expect(document.body.textContent).toContain('LIP-1')
|
||||
expect(document.body.textContent).toContain('已生成入库计划')
|
||||
expect(document.body.textContent).toContain('待收货')
|
||||
expect(document.body.textContent).not.toContain('INBOUND_PLANNED')
|
||||
expect(document.body.textContent).not.toContain('PENDING_RECEIVING')
|
||||
expect(document.body.querySelector('.el-dialog')).not.toBeNull()
|
||||
expect(document.body.querySelector('.el-drawer')).toBeNull()
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ import { changeContainerReceivingWarehouse, convertLastMileContainerToInboundPla
|
||||
import OrderReferenceText from '../components/OrderReferenceText.vue'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { overseasWarehousePermissions as permission } from '../permissions'
|
||||
import { toLastMileOrderListItem } from '../lastMilePresentation'
|
||||
import { lastMileStatusLabel, toLastMileOrderListItem } from '../lastMilePresentation'
|
||||
import type { LastMileOrderListItem } from '../types'
|
||||
import { saveCsv } from '@/shared/downloadCsv'
|
||||
|
||||
@@ -175,15 +175,28 @@ const columns: DataTableColumn[] = [
|
||||
const containerDetailColumns: DataTableColumn[] = [
|
||||
{ key: 'orderNo', prop: 'orderNo', label: '尾程单号', minWidth: 170 },
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 150 },
|
||||
{ key: 'orderStatus', prop: 'orderStatus', label: '订单状态', width: 130 },
|
||||
{ key: 'orderStatus', prop: 'orderStatus', label: '订单状态', width: 130, slotName: 'orderStatus' },
|
||||
{ key: 'planNo', prop: 'planNo', label: '独立入库计划', minWidth: 170 },
|
||||
{ key: 'planStatus', prop: 'planStatus', label: '计划状态', width: 130 },
|
||||
{ key: 'planStatus', prop: 'planStatus', label: '计划状态', width: 130, slotName: 'planStatus' },
|
||||
{ key: 'plannedArrivalAt', prop: 'plannedArrivalAt', label: '计划到仓时间', minWidth: 170, slotName: 'plannedArrivalAt' },
|
||||
]
|
||||
|
||||
function formatDateTime(value: string | null | undefined) {
|
||||
return value ? value.replace('T', ' ').slice(0, 19) : '—'
|
||||
}
|
||||
function inboundPlanStatusLabel(value: unknown) {
|
||||
const code = String(value ?? '').trim()
|
||||
return ({
|
||||
PENDING_ASSIGNMENT: '待入库',
|
||||
ASSIGNED: '待入库',
|
||||
PDA_PENDING: '待入库',
|
||||
PENDING_RECEIVING: '待收货',
|
||||
PICKING: '拣货中',
|
||||
PARTIAL_INBOUND: '部分入库',
|
||||
COMPLETED: '全部入库',
|
||||
CANCELLED: '已取消',
|
||||
} as Record<string, string>)[code] || (code ? '未知状态' : '—')
|
||||
}
|
||||
function canGenerateContainerPlans(container: ContainerPlanProgressSummaryDto) {
|
||||
return Boolean(container.containerNo
|
||||
&& container.overseasWarehouseId
|
||||
@@ -226,9 +239,8 @@ async function load(model: QueryModel = filters.value) {
|
||||
createdFrom: dateRange[0] ? `${dateRange[0]}T00:00:00` : undefined,
|
||||
createdTo: dateRange[1] ? `${dateRange[1]}T23:59:59` : undefined,
|
||||
})
|
||||
const pendingRecords = result.records.filter((container) => pendingInboundCodeCount(container) > 0)
|
||||
containerRows.value = pendingRecords
|
||||
containerTotal.value = Math.max(0, result.total - (result.records.length - pendingRecords.length))
|
||||
containerRows.value = result.records
|
||||
containerTotal.value = result.total
|
||||
} catch (error) {
|
||||
containerRows.value = []
|
||||
containerTotal.value = 0
|
||||
@@ -589,6 +601,8 @@ onMounted(() => { void load() })
|
||||
<span v-if="!containerDetail.summary.canChangeReceivingWarehouse">修改状态:{{ containerDetail.summary.receivingWarehouseLockReason || '当前已锁定' }}</span>
|
||||
</div>
|
||||
<DataTable :data="containerDetail.orders" :columns="containerDetailColumns" :loading="containerDetailLoading" preference-key="overseas-last-mile-container-detail" row-key="orderId" :total="undefined" empty-text="该柜暂无尾程订单">
|
||||
<template #orderStatus="{ row }">{{ lastMileStatusLabel((row as ContainerPlanProgressDetailDto['orders'][number]).orderStatus) }}</template>
|
||||
<template #planStatus="{ row }">{{ inboundPlanStatusLabel((row as ContainerPlanProgressDetailDto['orders'][number]).planStatus) }}</template>
|
||||
<template #plannedArrivalAt="{ row }">{{ formatDateTime((row as ContainerPlanProgressDetailDto['orders'][number]).plannedArrivalAt) }}</template>
|
||||
</DataTable>
|
||||
</template>
|
||||
|
||||
@@ -151,7 +151,7 @@ describe('overseas warehouse first-batch prototype contract', () => {
|
||||
expect(tailLeg).toContain('<OrderReferenceText :value="referenceValue" copyable />')
|
||||
const inbound = source('InboundOrderDetailView.vue')
|
||||
expect(inbound).toContain('getInboundPlan')
|
||||
expect(inbound).toContain('sourceOrderId')
|
||||
expect(inbound).not.toContain('detail.value?.sourceOrderId')
|
||||
expect(inbound).toContain('<LoadErrorState')
|
||||
const inventory = source('InventoryDetailView.vue')
|
||||
expect(inventory).toContain('listInventoryPositions')
|
||||
@@ -198,6 +198,7 @@ describe('overseas warehouse first-batch prototype contract', () => {
|
||||
expect(value).toContain('createFbaAppointment')
|
||||
expect(value).toContain('预计装车时间')
|
||||
expect(value).toContain('所选入库单必须属于同一 FBA 仓库')
|
||||
expect(value).toContain('anchor.customerId === row.customerId')
|
||||
expect(value).toContain('<CalendarCheck2 :size="16" />预约')
|
||||
expect(value).toContain('<Truck :size="16" />智能排车')
|
||||
expect(value).toContain('type="primary" @click="openSmartDispatch"')
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('overseas warehouse mounted real API pages', () => {
|
||||
vi.mocked(listInventoryLedger).mockReset().mockResolvedValue({ records: [{ id: '9007199254740997', customerId: '10001', customerName: '客户甲', destinationWarehouseCode: 'LAX', inboundCode: 'NYEXX2608300001', inboundOrderNo: 'IP-1', businessType: 'STOCK_IN', beforePackages: 0, deltaPackages: 10, afterPackages: 10, occurredAt: '2026-08-20T10:00:00' }], page: 1, size: 20, total: 1, pages: 1 })
|
||||
vi.mocked(getLastMileOrder).mockReset().mockResolvedValue({ order: { id: '9007199254740999', customerId: '10001', customerName: '华远贸易', orderNo: 'LMO-260827-0001', currentStatus: 'PENDING_ASSIGNMENT' }, cargo: [], statusHistory: [], fees: [], operationHistory: [] })
|
||||
})
|
||||
it('mounts inbound detail with a string-safe source order id', async () => { routing.route.params = { orderId: 'IP-1' }; routing.route.query = { customerId: '10001' }; const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(getInboundPlan).toHaveBeenCalledWith('IP-1', '10001'); expect(wrapper.text()).toContain('9007199254740995'); expect(wrapper.text()).toContain('洛杉矶仓') })
|
||||
it('mounts inbound detail with business references without exposing the internal source order id', async () => { routing.route.params = { orderId: 'IP-1' }; routing.route.query = { customerId: '10001' }; const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(getInboundPlan).toHaveBeenCalledWith('IP-1', '10001'); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('IN-1'); expect(wrapper.text()).not.toContain('9007199254740995'); expect(wrapper.text()).toContain('洛杉矶仓') })
|
||||
it('mounts inventory ledger as localized read-only business facts without exposing internal ids', async () => { const wrapper = mount(InventoryTransactionsView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(listInventoryLedger).toHaveBeenCalledWith(expect.objectContaining({ page: 1, size: 10 })); expect(listInventoryLedger).not.toHaveBeenCalledWith(expect.objectContaining({ customerId: expect.anything() })); expect(wrapper.text()).toContain('客户甲'); expect(wrapper.text()).toContain('NYEXX2608300001'); expect(wrapper.text()).not.toContain('10001'); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('10'); expect(wrapper.text()).toContain('入库'); expect(wrapper.text()).not.toContain('STOCK_IN'); expect(wrapper.text()).toContain('不提供出库单跳转'); expect(wrapper.text()).not.toContain('当前流水 DTO'); expect(wrapper.text()).not.toContain('操作人名称待补充') })
|
||||
it('renders the backend owner and complete short order number in last-mile detail', async () => { routing.route.params = { orderId: '9007199254740999' }; const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(getLastMileOrder).toHaveBeenCalledWith('9007199254740999'); expect(wrapper.text()).toContain('华远贸易'); expect(wrapper.text()).toContain('LMO-260827-0001'); expect(wrapper.get('.order-reference-text').attributes('title')).toBe('LMO-260827-0001') })
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user