fix: show cross-plan merged pallet ownership
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
| 尾程整柜转入仓计划 | `POST /platform/v1/last-leg/inbound-plans/convert-container` | body:`sourceContainerBookingId:string`;只调用一次柜级原子接口,不在前端循环逐票;返回一个计划数组,每个尾程订单/入仓码对应一个独立计划 | `...:inbound-order:create`;成功按接口返回计划数提示并刷新原待分配订单列表,不用当前订单页行数校验整柜总数;错误保留 code/requestId/traceId |
|
||||
| 柜级计划进度/详情 | `GET /platform/v1/last-leg/orders/container-summaries`;`GET /container-summaries/{sourceContainerBookingId}` | summary:`sourceContainerBookingId,containerNo?,totalOrderCount,inboundCodeCount,missingInboundCodeCount,planCount,plannedInboundCodeCount,pendingPlanCount,plannedArrivalAt?,allPlansReady,blockingReason?`;list query:`page,size,containerNo?,createdFrom?,createdTo?`;detail:`summary,orders[]`,订单与计划 ID 均为字符串 | 独立“柜子”Tab 消费服务端全柜分页,默认按计划到仓升序且 null 最后;详情抽屉显示柜内全部订单、入仓码和独立计划状态;同一摘要也供按柜生成选择框,不从订单分页本地拼柜 |
|
||||
| 入库列表 | `GET /platform/v1/last-leg/inbound-plans` | query:`page,size,planNo,inboundCode,status,warehouse,fbaWarehouseCode,appointmentStatus,dispatchStatus,createdFrom,createdTo`;平台跨客户;每行返回计划级 `assigned/mergeAllowed/urgent` | `...:inbound-order:list`;`lineId/planId/orderId/customerId` 均为字符串;合托展示为可选提示,不作为完成门禁 |
|
||||
| 入库详情 | `GET /platform/v1/last-leg/inbound-plans/{planNo}` | query:显式 `customerId`;path 编码 | 同 list;缺客户范围时页面明确报错;`sourceOrderId` 字符串化 |
|
||||
| 入库详情 | `GET /platform/v1/last-leg/inbound-plans/{planNo}` | query:显式 `customerId`;path 编码;`allocations[]` 直接返回实际 `palletNo`、`palletOwnerPlanNo`、`palletOwnedByCurrentPlan` | 同 list;缺客户范围时页面明确报错;`sourceOrderId` 字符串化;跨计划合托按分配事实展示真实托盘码、所属计划和本单箱数,不把外部托盘计入当前计划自有托盘数 |
|
||||
| FBA 预约新增 | `POST /platform/v1/last-leg/inbound-plans/fba-appointments/batch` | body:`customerId,lineIds:string[],appointmentNo,appointmentAt,expectedLoadingAt,appointmentStatus,appointmentRemark?` | `...:inbound-order:fba-appointment`;只允许同客户、同 FBA 仓勾选 |
|
||||
| FBA 预约详情/编辑 | `GET /fba-appointments/{appointmentNo}?customerId=...`;`PUT` 同 path | PUT body:`customerId,appointmentAt,expectedLoadingAt,appointmentStatus,appointmentRemark?` | GET 同 list;PUT 同 appointment;失败保留弹框和错误元数据 |
|
||||
| 排车列表/详情 | `GET /platform/v1/last-leg/dispatches`;`GET /dispatches/{dispatchNo}` | list query:`page,size,dispatchNo,vehiclePlate,status,fbaAppointmentNo`;不传客户 | `...:inbound-order:list`;Controller 受 `lastleg.dispatch-query-api.enabled` 控制 |
|
||||
|
||||
@@ -64,7 +64,7 @@ export interface InboundPlanDetailDto {
|
||||
remark?: string | null
|
||||
lines: Array<{ id: string; orderId: string; inboundCode?: string | null; fbaWarehouseCode?: string | null; destinationWarehouseId?: string | null; destinationWarehouseCode?: string | null; destinationWarehouseName?: string | null; volumeCbm?: number | null; requiredPackageCount: number; plannedPalletCount: number }>
|
||||
pallets: Array<{ id: string; palletNo?: string | null; destinationWarehouseId?: string | null; destinationWarehouseCode?: string | null; destinationWarehouseName?: string | null; capacityCbm?: number | null; allocatedVolumeCbm?: number | null; sequenceNo: number; warehousePalletId?: string | null; warehouseLocationId?: string | null; warehouseResourceLockId?: string | null; assignmentStatus?: string | null; lockedAt?: string | null; actualVolumeCbm?: number | null; actualPackageCount: number }>
|
||||
allocations: Array<{ lineId: string; palletId: string; allocatedVolumeCbm?: number | null; plannedPackageCount: number; countedPackageCount: number; pickedPackageCount: number; actualVolumeCbm?: number | null; status?: string | null; version: number }>
|
||||
allocations: Array<{ lineId: string; palletId: string; palletNo: string; palletOwnerPlanNo: string; palletOwnedByCurrentPlan: boolean; allocatedVolumeCbm?: number | null; plannedPackageCount: number; countedPackageCount: number; pickedPackageCount: number; actualVolumeCbm?: number | null; status?: string | null; version: number }>
|
||||
}
|
||||
|
||||
export async function listInboundPlans(query: InboundPlanQuery) {
|
||||
|
||||
@@ -38,15 +38,16 @@ const feeColumns: DataTableColumn[] = [{ key: 'index', label: '序号', width: 7
|
||||
const palletColumns: DataTableColumn[] = [
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 150 },
|
||||
{ key: 'palletNo', prop: 'palletNo', label: '托盘号', minWidth: 150 },
|
||||
{ key: 'palletOwner', prop: 'palletOwner', label: '托盘所属计划', minWidth: 190 },
|
||||
{ key: 'packageCount', prop: 'packageCount', label: '对应箱数', width: 110, align: 'right' },
|
||||
]
|
||||
const palletRows = computed(() => (detail.value?.allocations || []).map((allocation, index) => {
|
||||
const line = detail.value?.lines.find(item => item.id === allocation.lineId)
|
||||
const pallet = detail.value?.pallets.find(item => item.id === allocation.palletId)
|
||||
return {
|
||||
id: `${allocation.lineId}-${allocation.palletId}-${index}`,
|
||||
inboundCode: text(line?.inboundCode),
|
||||
palletNo: text(pallet?.palletNo),
|
||||
palletNo: text(allocation.palletNo),
|
||||
palletOwner: allocation.palletOwnedByCurrentPlan ? `${allocation.palletOwnerPlanNo}(本计划)` : `${allocation.palletOwnerPlanNo}(跨计划合托)`,
|
||||
packageCount: Number(allocation.pickedPackageCount ?? allocation.countedPackageCount ?? allocation.plannedPackageCount ?? 0),
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -50,6 +50,17 @@ describe('overseas warehouse mounted real API pages', () => {
|
||||
expect(rows[1]).toMatchObject({ warehouse: '—', fbaWarehouse: 'ONT8', destinationWarehouseName: 'ONT8目的仓' })
|
||||
})
|
||||
it('mounts inbound detail with business references and independent receiving and FBA warehouse references', 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('FL-WH-20260911-000001'); expect(wrapper.text()).toContain('客户甲'); expect(wrapper.text()).toContain('一件代发'); expect(wrapper.text()).toContain('2026-09-11 10:00'); expect(wrapper.text()).toContain('页面验收'); expect(wrapper.text()).toContain('US-LAX-01 · 洛杉矶接柜仓'); expect(wrapper.text()).toContain('ABE3'); expect(wrapper.text()).not.toContain('ABE3目的仓') })
|
||||
it('shows the real pallet and owner plan for a cross-plan merged allocation', async () => {
|
||||
vi.mocked(getInboundPlan).mockResolvedValueOnce({ id: '101', planNo: 'IP-CURRENT', customerId: '10001', sourceOrderId: '1', overseasWarehouseCode: 'US-LAX-01', overseasWarehouseName: '洛杉矶接柜仓', status: 'PICKING', lineCount: 1, palletCount: 1, totalVolumeCbm: 2, lines: [{ id: '201', orderId: '1', inboundCode: 'EXXHK2609100014', fbaWarehouseCode: 'ACY2', requiredPackageCount: 150, plannedPalletCount: 1 }], pallets: [{ id: '301', palletNo: 'DRYU2309389-101-005', sequenceNo: 1, actualPackageCount: 0 }], allocations: [{ lineId: '201', palletId: '302', palletNo: 'DRYU2309389-101-006', palletOwnerPlanNo: 'IP-OWNER', palletOwnedByCurrentPlan: false, plannedPackageCount: 150, countedPackageCount: 150, pickedPackageCount: 150, status: 'PICKED', version: 0 }] })
|
||||
routing.route.params = { orderId: 'IP-CURRENT' }; routing.route.query = { customerId: '10001' }
|
||||
|
||||
const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
const rows = wrapper.findComponent(DataTable).props('data') as Array<{ palletNo: string; palletOwner: string; packageCount: number }>
|
||||
expect(rows).toEqual([expect.objectContaining({ palletNo: 'DRYU2309389-101-006', palletOwner: 'IP-OWNER(跨计划合托)', packageCount: 150 })])
|
||||
expect(wrapper.text()).not.toContain('DRYU2309389-101-005(跨计划合托)')
|
||||
})
|
||||
it('shows an empty receiving warehouse when either frozen field is missing without falling back to destination data', async () => {
|
||||
vi.mocked(getInboundPlan).mockResolvedValueOnce({ id: '1', planNo: 'IP-2', customerId: '10001', sourceOrderId: '2', overseasWarehouseCode: null, overseasWarehouseName: '不充整接柜仓', status: 'PENDING_ASSIGNMENT', lineCount: 1, palletCount: 0, totalVolumeCbm: 0, lines: [{ id: '3', orderId: '2', inboundCode: 'IN-2', fbaWarehouseCode: 'ABE3', destinationWarehouseCode: 'ABE3', destinationWarehouseName: 'ABE3目的仓', requiredPackageCount: 1, plannedPalletCount: 0 }], pallets: [], allocations: [] })
|
||||
routing.route.params = { orderId: 'IP-2' }; routing.route.query = { customerId: '10001' }
|
||||
|
||||
Reference in New Issue
Block a user