Files
admin-web/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts
T
2026-10-09 15:25:19 +08:00

144 lines
9.7 KiB
TypeScript
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 ElementPlus from 'element-plus'
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { getLastMileOrder } from '../api/lastMileOrders'
import LastMileOrderDetailView from './LastMileOrderDetailView.vue'
import { getWarehouseTransfer } from '../api/warehouseTransfers'
const push = vi.hoisted(() => vi.fn())
vi.mock('vue-router', () => ({ useRoute: () => ({ params: { orderId: 'internal-order-id' } }), useRouter: () => ({ push }) }))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => false }) }))
vi.mock('../api/lastMileOrders', () => ({ getLastMileOrder: vi.fn(), createLastMileOrderFee: vi.fn() }))
vi.mock('../api/warehouseTransfers', () => ({ getWarehouseTransfer: vi.fn() }))
enableAutoUnmount(afterEach)
describe('LastMileOrderDetailView domestic detail alignment', () => {
it('shows signed box contents and actual quantities on return orders', async () => {
vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'tail-return', customerId: 'customer-1', orderNo: 'RETU-1', orderType: 'RETURN_RELABEL', currentStatus: 'INBOUND_COMPLETED' }, cargo: [], fees: [], statusHistory: [], operationHistory: [],
returnReceipt: { order: { returnNo: 'RT-1', receivedQuantity: 10, expectedQuantity: 10 }, receipts: [{ receiptNo: 'RR-1', unitType: 'PALLET', unitCode: 'PLT-1', receivedQuantity: 10, boxCount: 2, signedAt: '' }],
cartons: [{ receiptNo: 'RR-1', cartonMark: 'RC#2-1', countStatus: 'CONFIRMED', items: [{ skuCode: 'SKU-A', quantity: 3 }, { skuCode: 'SKU-B', quantity: 7 }] }, { receiptNo: 'RR-2', cartonMark: 'RC-PENDING', countStatus: 'PENDING', items: [] }] } })
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises()
await wrapper.findAll('[role="tab"]').find(tab => tab.text().includes('退货签收与箱内清点'))!.trigger('click'); await flushPromises()
const panel = wrapper.get('[aria-label="退货签收与箱内清点"]')
expect(panel.text()).toContain('实收 10 / 预报 10 件')
expect(panel.text()).toContain('RC#2-1')
expect(panel.text()).toContain('SKU-A')
expect(panel.text()).toContain('实际签收数量')
expect(panel.text()).toContain('待清点')
})
it('loads transfer fulfillment only on the plan tab using sourceOrderNo, never the tail order number', async () => {
vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'internal-transfer-tail', customerId: 'customer-1', orderNo: 'WT-TAIL-1', sourceOrderNo: 'WT-SOURCE-1', orderType: 'WAREHOUSE_TRANSFER', currentStatus: 'INBOUND_COMPLETED' }, cargo: [], fees: [], statusHistory: [], operationHistory: [] })
vi.mocked(getWarehouseTransfer).mockResolvedValue({ id: 'internal-transfer', transferNo: 'WT-SOURCE-1', needRelabel: true, status: 'WAIT_RELABEL', relabelNo: 'RL-1', relabelStatus: 'CUSTOMER_CONFIRM_PENDING', packageCount: 2, items: [], cartons: [], pallets: [], relabelBindings: [] })
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises()
expect(getWarehouseTransfer).not.toHaveBeenCalled()
await wrapper.findAll('[role="tab"]').find(x => x.text().includes('计划信息'))!.trigger('click'); await flushPromises()
expect(getWarehouseTransfer).toHaveBeenCalledExactlyOnceWith('customer-1', 'WT-SOURCE-1')
expect(wrapper.get('[aria-label="本票转运与换标履约"]').text()).toContain('待客户确认')
expect(wrapper.get('[aria-label="本票转运与换标履约"]').text()).toContain('不得提前出库')
})
beforeEach(() => {
push.mockReset()
vi.mocked(getWarehouseTransfer).mockReset()
vi.mocked(getLastMileOrder).mockResolvedValue({
order: { id: 'internal-order-id', customerId: 'customer-1', orderNo: 'FL-WH-001', currentStatus: 'PENDING_ASSIGNMENT', customerName: '示例客户', overseasWarehouseName: '洛杉矶仓', packageCount: 0, palletCount: 2, grossWeightKg: 30, netWeightKg: 22, volumeCbm: 6.374, inboundCode: 'IN-001', containerNo: 'CONT-001', customerRemark: '完整保留客户备注', updatedAt: '2026-09-14T10:00:00' },
cargo: [{ id: 'cargo-1', skuCode: 'SKU-001', productNameZh: '测试货物' }], fees: [], statusHistory: [], operationHistory: [],
})
})
it('shows one business heading, a semantic status and summary without losing basic fields', async () => {
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
await flushPromises()
expect(wrapper.findAll('h1')).toHaveLength(1)
expect(wrapper.get('h1').text()).toBe('FL-WH-001')
const header = wrapper.get('.density-detail-header')
expect(header.text()).not.toContain('尾程订单详情')
expect(header.text()).not.toContain('尾程单号')
expect(header.get('.status-badge').classes()).toContain('is-warning')
expect(header.get('.order-reference-text__copy').classes()).toContain('is-text')
const summary = wrapper.get('[aria-label="尾程订单概览"]')
expect(summary.findAll('dd').map(item => item.text())).toEqual(['示例客户', '洛杉矶仓', '0 箱', '2 托', '30 kg', '22 kg', '6.374 m³', expect.stringContaining('2026-09-14')])
const fields = wrapper.get('dl[aria-label="基本信息"]')
expect(fields.findAll('dt')).toHaveLength(22)
expect(fields.text()).toContain('货物毛重(kg)30')
expect(fields.text()).toContain('货物净重(kg)22')
expect(fields.text()).toContain('CONT-001')
expect(fields.text()).toContain('完整保留客户备注')
expect(fields.findAll('.wide')).toHaveLength(3)
expect(wrapper.text()).not.toContain('internal-order-id')
await header.get('.density-back').trigger('click')
expect(push).toHaveBeenCalledWith({ name: 'overseas-last-mile-orders' })
})
it('keeps all five tab panels reachable after applying the shared tab style', async () => {
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
await flushPromises()
const tabs = wrapper.findAll('[role="tab"]')
expect(tabs.map(tab => tab.text())).toEqual(['📋基本信息', '📦货物信息', '🗓️计划信息', '💰费用明细', '📝操作记录'])
for (const [index, tab] of tabs.entries()) {
await tab.trigger('click')
await flushPromises()
expect(tab.attributes('aria-selected')).toBe('true')
const panel = wrapper.findAll('[role="tabpanel"]')[index]!
expect(panel.isVisible()).toBe(true)
if (index === 1) expect(panel.text()).toContain('SKU-001')
}
})
it('shows declared, received and highlighted signed difference only for warehouse transfers', async () => {
vi.mocked(getLastMileOrder).mockResolvedValue({
order: { id: 'transfer-id', customerId: 'customer-1', orderNo: 'WT-001', orderType: 'WAREHOUSE_TRANSFER', currentStatus: 'WAIT_CUSTOMER_CONFIRMATION', packageCount: 10, receivedPackageCount: 12, packageDifference: 2 },
cargo: [], fees: [], statusHistory: [], operationHistory: [],
})
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
await flushPromises()
const fields = wrapper.get('dl[aria-label="基本信息"]')
expect(fields.text()).toContain('客户预报箱数10')
expect(fields.text()).toContain('实收箱数12')
expect(fields.text()).toContain('箱数差异+2')
expect(fields.get('.package-difference--nonzero').text()).toBe('+2')
expect(wrapper.get('.status-badge').text()).toBe('待客户确认实测')
const overview = wrapper.get('[aria-label="尾程订单概览"]')
expect(overview.text()).toContain('实收总箱数12 箱')
expect(overview.text()).not.toContain('客户预报总箱数')
})
it('shows the declared total before a warehouse transfer has a received count', async () => {
vi.mocked(getLastMileOrder).mockResolvedValue({
order: { id: 'transfer-id', customerId: 'customer-1', orderNo: 'WT-002', orderType: 'WAREHOUSE_TRANSFER', currentStatus: 'WAIT_PDA_RECEIVING', packageCount: 10, receivedPackageCount: null, packageDifference: null },
cargo: [], fees: [], statusHistory: [], operationHistory: [],
})
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
await flushPromises()
const overview = wrapper.get('[aria-label="尾程订单概览"]')
expect(overview.text()).toContain('客户预报总箱数10 箱')
expect(overview.text()).not.toContain('实收总箱数')
})
it('renders both return relabel documents with independent states and quantities', async () => {
vi.mocked(getLastMileOrder).mockResolvedValue({
order: {
id: 'return-id', customerId: 'customer-1', orderNo: 'RT-001', orderType: 'RETURN_RELABEL', currentStatus: 'INBOUND_COMPLETED',
relabelOrderNo: 'RL-SECOND', relabelStatus: 'PROCESSING',
relabelApplications: [
{ relabelNo: 'RL-FIRST', status: 'COMPLETED', quantity: 10, createdAt: '2026-09-21T10:00:00' },
{ relabelNo: 'RL-SECOND', status: 'PROCESSING', quantity: 5, createdAt: '2026-09-21T11:00:00' },
],
},
cargo: [], fees: [], statusHistory: [], operationHistory: [],
})
const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } })
await flushPromises()
await wrapper.get('#tab-plan').trigger('click')
await flushPromises()
const rows = wrapper.findAll('.related-panel .el-table__body tbody tr')
expect(rows).toHaveLength(2)
expect(rows[0].text()).toContain('RL-FIRST')
expect(rows[0].text()).toContain('已完成')
expect(rows[0].text()).toContain('10')
expect(rows[1].text()).toContain('RL-SECOND')
expect(rows[1].text()).toContain('处理中')
expect(rows[1].text()).toContain('5')
})
})