diff --git a/src/modules/overseas-warehouse/api/lastMileOrders.test.ts b/src/modules/overseas-warehouse/api/lastMileOrders.test.ts index d44e0331..e7f63d5c 100644 --- a/src/modules/overseas-warehouse/api/lastMileOrders.test.ts +++ b/src/modules/overseas-warehouse/api/lastMileOrders.test.ts @@ -87,12 +87,15 @@ describe('last-mile order overseas API', () => { it('uses the manual inbound-plan endpoint with the complete form and idempotency evidence', async () => { vi.mocked(overseasHttp.post).mockResolvedValue({ code: '200', message: 'success', data: {} }) const payload = { orderId: '190000000000000099', customerId: '190000000000000001', expectedOrderVersion: 4, - plannedWarehouseId: '193000000000200003', plannedWarehouseCode: 'US-LAX-01', plannedWarehouseName: '洛杉矶仓', - plannedArrivalDate: '2026-09-08', inboundCode: 'NYEXX2609060063', palletCount: 8, palletType: 'STANDARD' as const, + plannedArrivalDate: '2026-09-08', palletCount: 8, palletType: 'STANDARD' as const, assigned: false, mergeRequired: false, urgent: true, remark: '加急' } await createManualInboundPlan(payload, 'plan-key-1') expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/manual', payload, { headers: { 'Idempotency-Key': 'plan-key-1' } }) + expect(payload).not.toHaveProperty('inboundCode') + expect(payload).not.toHaveProperty('plannedWarehouseId') + expect(payload).not.toHaveProperty('plannedWarehouseCode') + expect(payload).not.toHaveProperty('plannedWarehouseName') }) it('uses versioned idempotent order hold and complete manual-node snapshot endpoints', async () => { diff --git a/src/modules/overseas-warehouse/api/lastMileOrders.ts b/src/modules/overseas-warehouse/api/lastMileOrders.ts index cfb08a98..214a168e 100644 --- a/src/modules/overseas-warehouse/api/lastMileOrders.ts +++ b/src/modules/overseas-warehouse/api/lastMileOrders.ts @@ -52,6 +52,8 @@ export interface LastMileOrderSummaryDto { sourceOrderNo?: string | null sourceInboundId?: string | null customerName?: string | null + overseasWarehouseId?: string | null + overseasWarehouseCode?: string | null overseasWarehouseName?: string | null transferWarehouseName?: string | null transferWarehouseId?: string | null @@ -224,11 +226,7 @@ export interface ManualInboundPlanPayload { customerId: string orderId: string expectedOrderVersion: number - plannedWarehouseId: string - plannedWarehouseCode: string - plannedWarehouseName: string plannedArrivalDate: string - inboundCode: string palletCount: number palletType: 'STANDARD' | 'WOOD' | 'PAPER' assigned: boolean diff --git a/src/modules/overseas-warehouse/lastMilePresentation.test.ts b/src/modules/overseas-warehouse/lastMilePresentation.test.ts index 9090e149..f4ffa4e9 100644 --- a/src/modules/overseas-warehouse/lastMilePresentation.test.ts +++ b/src/modules/overseas-warehouse/lastMilePresentation.test.ts @@ -52,6 +52,17 @@ describe('last-mile order presentation adapter', () => { }).owner).toBe('—') }) + it('preserves the frozen overseas receiving warehouse id and code as strings', () => { + const row = toLastMileOrderListItem({ + id: '190000000000000099', customerId: '190000000000000001', orderNo: 'LMO-260908-0001', + overseasWarehouseId: '190000000000000123', overseasWarehouseCode: 'LAX01', overseasWarehouseName: '洛杉矶接柜仓', + }) + + expect(row.overseasWarehouseId).toBe('190000000000000123') + expect(row.overseasWarehouseCode).toBe('LAX01') + expect(row.warehouse).toBe('洛杉矶接柜仓') + }) + it('preserves the exact server LMO short number without padding, prefixing or deriving it from id', () => { const serverOrderNo = 'LMO-9A7' const row = toLastMileOrderListItem({ id: '2092873463568596994', customerId: '100', orderNo: serverOrderNo }) diff --git a/src/modules/overseas-warehouse/lastMilePresentation.ts b/src/modules/overseas-warehouse/lastMilePresentation.ts index 4a13655e..12b98694 100644 --- a/src/modules/overseas-warehouse/lastMilePresentation.ts +++ b/src/modules/overseas-warehouse/lastMilePresentation.ts @@ -106,6 +106,8 @@ export function toLastMileOrderListItem(dto: LastMileOrderSummaryDto): LastMileO inboundCode: text(dto.inboundCode), lxCode: text(dto.lxCode), owner: text(dto.customerName), + overseasWarehouseId: String(dto.overseasWarehouseId ?? '').trim(), + overseasWarehouseCode: String(dto.overseasWarehouseCode ?? '').trim(), warehouse: text(dto.overseasWarehouseName), transferWarehouse: text(dto.transferWarehouseName), fbaWarehouse: text(dto.fbaWarehouseCode), diff --git a/src/modules/overseas-warehouse/pages/LastMileOrdersView.behavior.test.ts b/src/modules/overseas-warehouse/pages/LastMileOrdersView.behavior.test.ts index a48f7b96..167d8011 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrdersView.behavior.test.ts +++ b/src/modules/overseas-warehouse/pages/LastMileOrdersView.behavior.test.ts @@ -73,7 +73,12 @@ const currentReceivingWarehouse = { id: summary.overseasWarehouseId, warehouseCode: summary.overseasWarehouseCode, warehouseName: summary.overseasWarehouseName, } function selectedRow(overrides: Record = {}) { - return { id: '2092873463568596994', customerId, sourceContainerBookingId: bookingId, orderNo: 'LMO-260827-0001', ...overrides } + return { + id: '2092873463568596994', customerId, sourceContainerBookingId: bookingId, orderNo: 'LMO-260827-0001', + overseasWarehouseId: summary.overseasWarehouseId, overseasWarehouseCode: summary.overseasWarehouseCode, + warehouse: summary.overseasWarehouseName, inboundCode: 'IN-LOCKED', version: 3, pallets: 2, + ...overrides, + } } enableAutoUnmount(afterEach) @@ -195,11 +200,44 @@ describe('LastMileOrdersView pending order and container conversion', () => { expect(document.body.textContent).toContain('计划入库仓') expect(document.body.textContent).toContain('预计到仓日期') expect(document.body.textContent).toContain('托盘类型') + const inboundCodeInput = wrapper.findAllComponents({ name: 'ElInput' }) + .find((input) => input.props('placeholder') === '订单冻结入仓码') + expect(inboundCodeInput?.props('disabled')).toBe(true) + const receivingWarehouseInput = wrapper.findAllComponents({ name: 'ElInput' }) + .find((input) => input.props('placeholder') === '当前柜接柜仓') + expect(receivingWarehouseInput?.props('disabled')).toBe(true) + expect(receivingWarehouseInput?.props('modelValue')).toBe('LAX01 · 洛杉矶接柜仓') + expect(document.body.textContent).toContain('接柜仓按柜统一') + expect(listAllPlatformWarehouses).not.toHaveBeenCalled() expect(createManualInboundPlan).not.toHaveBeenCalled() expect(convertLastMileContainerToInboundPlans).not.toHaveBeenCalled() expect(listContainerPlanProgress).not.toHaveBeenCalled() }) + it('shows the order-frozen inbound code but never sends it or a per-order receiving warehouse override', async () => { + const wrapper = await openPending() + await selectRows(wrapper, [selectedRow()]) + await button(wrapper, '生成入库计划')!.trigger('click') + await flushPromises() + const datePicker = wrapper.find('.command-form').findComponent({ name: 'ElDatePicker' }) + datePicker.vm.$emit('update:modelValue', '2026-09-08') + await wrapper.vm.$nextTick() + const confirm = [...document.body.querySelectorAll('button')] + .find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement + confirm.click() + await flushPromises() + + expect(createManualInboundPlan).toHaveBeenCalledWith(expect.objectContaining({ + customerId, orderId: '2092873463568596994', expectedOrderVersion: 3, + plannedArrivalDate: '2026-09-08', palletCount: 2, + }), expect.stringContaining('manual-inbound-plan-')) + const payload = vi.mocked(createManualInboundPlan).mock.calls[0][0] as unknown as Record + expect(payload).not.toHaveProperty('inboundCode') + expect(payload).not.toHaveProperty('plannedWarehouseId') + expect(payload).not.toHaveProperty('plannedWarehouseCode') + expect(payload).not.toHaveProperty('plannedWarehouseName') + }) + it('opens a cabinet selector without requiring order selection and loads server summaries only then', async () => { const wrapper = await openPending() await openContainerDialog(wrapper) diff --git a/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue b/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue index 349cac3d..af70bced 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue @@ -42,9 +42,10 @@ async function exportOrders() { exportLoading.value = true; try { const { create const actionError = ref(null) const actionTarget = ref(null) const inboundPlanOpen = ref(false) -const inboundPlanWarehouseLoading = ref(false) -const inboundPlanWarehouses = ref([]) -const inboundPlanForm = reactive({ warehouseId: '', plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'STANDARD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' }) +const inboundPlanWarehouseLabel = computed(() => [actionTarget.value?.overseasWarehouseCode, actionTarget.value?.warehouse] + .filter((value) => value && value !== '—') + .join(' · ')) +const inboundPlanForm = reactive({ plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'STANDARD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' }) const holdOpen = ref(false) const holdForm = reactive({ held: true, chargeable: true, reason: '' }) const manualNodesOpen = ref(false) @@ -436,43 +437,28 @@ async function submitTransfer() { function idempotencyKey(prefix: string) { return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}` } -async function loadInboundPlanWarehouses() { - inboundPlanWarehouseLoading.value = true - try { - inboundPlanWarehouses.value = (await listAllPlatformWarehouses({ warehouseType: 'OVERSEAS', status: 'ACTIVE' })).map(toTransferWarehouseOption) - const targetWarehouse = inboundPlanWarehouses.value.find((item) => actionTarget.value?.warehouse.includes(item.warehouseName)) - if (targetWarehouse) inboundPlanForm.warehouseId = targetWarehouse.warehouseId - } catch (error) { - actionError.value = getApiErrorDetails(error, '入库仓候选加载失败,请重试') - } finally { - inboundPlanWarehouseLoading.value = false - } -} function openInboundPlan(source?: object) { const target = source ? source as LastMileOrderListItem : (selectedRows.value.length === 1 ? selectedRows.value[0] : null) if (!target) { ElMessage.warning('生成入库计划时请选择 1 条尾程订单'); return } if (target.detained) { ElMessage.warning('订单处于暂扣状态,解除暂扣后才能生成入库计划'); return } actionTarget.value = target - Object.assign(inboundPlanForm, { warehouseId: '', plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: typeof target.pallets === 'number' && target.pallets > 0 ? target.pallets : 1, palletType: 'STANDARD', assigned: false, mergeRequired: false, urgent: false, remark: '' }) - inboundPlanWarehouses.value = [] + Object.assign(inboundPlanForm, { plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: typeof target.pallets === 'number' && target.pallets > 0 ? target.pallets : 1, palletType: 'STANDARD', assigned: false, mergeRequired: false, urgent: false, remark: '' }) actionError.value = null inboundPlanOpen.value = true - void loadInboundPlanWarehouses() } async function submitInboundPlan() { const row = actionTarget.value - const warehouse = inboundPlanWarehouses.value.find((item) => item.warehouseId === inboundPlanForm.warehouseId) - if (!row || !warehouse || !inboundPlanForm.plannedArrivalDate || !inboundPlanForm.inboundCode.trim() || inboundPlanForm.palletCount < 1) { - ElMessage.warning('请完整填写计划入库仓、预计到仓日期、入仓码和托数') + const frozenInboundCode = String(row?.inboundCode || '').trim() + if (!row || !row.overseasWarehouseId || !inboundPlanForm.plannedArrivalDate || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < 1) { + ElMessage.warning('当前柜须已分配接柜仓,并完整填写预计到仓日期和托数') return } actionLoading.value = true; actionError.value = null try { await createManualInboundPlan({ customerId: row.customerId, orderId: row.id, expectedOrderVersion: row.version, - plannedWarehouseId: warehouse.warehouseId, plannedWarehouseCode: warehouse.warehouseCode, - plannedWarehouseName: warehouse.warehouseName, plannedArrivalDate: inboundPlanForm.plannedArrivalDate, - inboundCode: inboundPlanForm.inboundCode.trim(), palletCount: inboundPlanForm.palletCount, + plannedArrivalDate: inboundPlanForm.plannedArrivalDate, + palletCount: inboundPlanForm.palletCount, palletType: inboundPlanForm.palletType, assigned: inboundPlanForm.assigned, mergeRequired: inboundPlanForm.mergeRequired, urgent: inboundPlanForm.urgent, remark: inboundPlanForm.remark.trim() || undefined, @@ -715,10 +701,10 @@ onMounted(() => { void load() }) - + -
+
接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。
diff --git a/src/modules/overseas-warehouse/types.ts b/src/modules/overseas-warehouse/types.ts index 85beff1b..47146cb8 100644 --- a/src/modules/overseas-warehouse/types.ts +++ b/src/modules/overseas-warehouse/types.ts @@ -14,6 +14,8 @@ export interface LastMileOrderListItem { inboundCode: string lxCode: string owner: string + overseasWarehouseId: string + overseasWarehouseCode: string warehouse: string transferWarehouse: string fbaWarehouse: string