diff --git a/src/modules/overseas-warehouse/AGENTS.md b/src/modules/overseas-warehouse/AGENTS.md
index a3d6d78a..56b1b3ca 100644
--- a/src/modules/overseas-warehouse/AGENTS.md
+++ b/src/modules/overseas-warehouse/AGENTS.md
@@ -20,7 +20,7 @@
| 仓储转运审核、通过与驳回 | `pages/LastMileOrdersView.vue`、`components/WarehouseTransferReviewDialog.vue` | `api/warehouseTransfers.ts` 的列表、详情及 `/warehouse-transfers/{transferNo}/review`;审核按钮要求 `permissions.ts` 的 `warehouseTransferApprove` 且行类型为 `WAREHOUSE_TRANSFER`、状态为 `PENDING_REVIEW`。请求携带该行客户范围和转运单号,审核结论为 `APPROVE/REJECT`,驳回原因必填;这是尾程订单内的审核弹框,不是独立审核路由或排车转派送入口 |
| 尾程订单控制与柜级入仓计划转换 | `pages/LastMileOrdersView.vue`、`pages/LastMileOrderDetailView.vue` | 柜子工作台按 `sourceContainerBookingId` 消费 `/orders/container-summaries`;服务端分页返回全部稳定来源柜,计划已经齐全的柜仍保留用于接柜仓查询和修改,并按冻结 `plannedArrivalAt` 升序、空值最后排序。详情返回全柜订单且不受列表分页截断;单票手工计划保存后,由服务端在整柜最后一票计划齐全时自动生成并绑定全柜托盘码,一票柜立即生成;`resourcePendingPlanCount` 只表示自动生成异常的重试入口,不是正常人工步骤。柜级确认弹框先调用 `/inbound-plans/container-preview`,按服务端 FBA/一件代发/私人地址分组展示头程已有、海外仓补充和最终托数以及 `readiness/blockers`;散货最终托数不得少于头程已有托盘码,差额连续补码且不阻断流程;一件代发必须严格按不可变头程封托托盘数整体入库,单票表单托数锁定且服务端再次失败关闭校验。前端不计算托数、不逐票建计划、不预占托盘,再把同一 `snapshotVersion/snapshotHash` 原样提交 `/inbound-plans/convert-container`,快照冲突后重新获取预览。服务端只为剩余订单逐票生成独立计划,并为异常遗留的人工计划补齐真实托盘,整批原子且重放不得产生第二批资源。单票完整入库计划、暂扣/解除和八行人工节点均从 `api/lastMileOrders.ts` 提交订单版本与幂等键;预计到仓时间只读展示来源柜冻结值,不进入人工计划请求,服务端按整柜校验并写入同一日期;入仓码只读展示并提交订单冻结值,不允许人工改写,暂扣资格、接柜仓锁定原因和操作能力只消费服务端结果,人工节点一经保存不得回退柜级节点。转仓候选只取 `../iam/api/platformIam.ts` 的平台公共 `ACTIVE + FBA` 仓主档;Backend `lastleg-service/` |
| 入仓单、入仓计划、FBA 预约 | `pages/InboundOrdersView.vue`、`pages/InboundOrderDetailView.vue` | 列表/预约 `api/inboundOrders.ts`,计划列表/详情 `api/inboundPlans.ts`;Backend `lastleg-service/inboundplan/` |
-| 排车查询、智能预排与转派送 | `pages/DispatchesView.vue`、`pages/DispatchDetailView.vue`、`components/SmartDispatchDialog.vue` | `api/dispatches.ts`;Backend `lastleg-service/dispatch/`、`delivery/`;覆盖详情、候选、草稿柜、托盘锁、`pre-arrange` 和 `transfer-delivery`。一次转派送仍生成一张车辆级出库单,响应的 `deliveries` 按目的 FBA 仓返回多张派送单。页面能力必须由正式路由、API 调用和测试共同证明 |
+| 排车查询、智能预排与转派送 | `pages/DispatchesView.vue`、`pages/DispatchDetailView.vue`、`components/SmartDispatchDialog.vue` | `api/dispatches.ts`;Backend `lastleg-service/dispatch/`、`delivery/`;覆盖详情、候选、草稿柜、托盘锁、`pre-arrange` 和 `transfer-delivery`。路线只读并由已排托盘关联的 FBA 仓去重后以英文逗号连接,预排和普通编辑均不接收人工路线。一次转派送仍生成一张车辆级出库单,响应的 `deliveries` 按目的 FBA 仓返回多张派送单。页面能力必须由正式路由、API 调用和测试共同证明 |
| 海外出库与派送 | `pages/OutboundsView.vue`、`pages/OutboundDetailView.vue`、`pages/DeliveryOrdersView.vue`、`pages/DeliveryOrderDetailView.vue` | `api/outbounds.ts`、`api/deliveryOrders.ts`;出库详情使用 `deliveries` 展示按目的 FBA 仓拆出的全部派送单,派送列表和详情必须显示目的 FBA 仓。人工状态入口不提供完成派送;签收资料保存成功后提示自动完成并刷新列表。出库列表按类型筛选并展示中文类型。一件代发新建弹窗直接跨客户加载服务端可用候选,不单独展示客户筛选,海外仓必选并优先默认洛杉矶仓;订单条件可按订单号、入库单号、客户、托盘号、SKU 或品名查询/重置,以托盘+SKU 行分页多选。提交时从所选行解析唯一客户,并校验所选行属于当前唯一海外仓,不要求先选客户。退货出库客户下拉同时展示客户名称与联系电话并支持手机号检索,客户和仓库选项独立容错加载;选择客户后须立即按当前默认/已选海外仓加载库存。SKU 与箱号查询均使用输入态/已查询态分离,只允许点击查询按钮后生效;支持英文逗号批量条件,查询时清除空格并按任一条件命中,左右列表分别提供每页 5/10/20/50 条选择。SKU 列表只展示 `availableQuantity > 0` 的未占用可用数量,合箱后已扣减为零的 SKU 不再作为可选库存显示,实际作业以可用箱唛选择和箱内 SKU 明细扣减为准。取消、派送列表/详情及退回入口对应 Backend `lastleg-service/delivery/`、`dropship/` |
| 异常退回与客户回访 | `pages/ExceptionOrdersView.vue`、`pages/CustomerVisitsView.vue` | `api/exceptionOrders.ts`、`api/customerVisits.ts`;异常关系解析、异常建档、派送退回、完成/状态和回访附件对应 Backend `lastleg-service/exceptionorder/`、`customervisit/` |
| 库存余额、库位与流水 | `pages/InventoryView.vue`、`pages/InventoryDetailView.vue`、`pages/InventoryTransactionsView.vue` | `api/inventory.ts`;业务库存归 `lastleg-service/inventory/`,实物资源归 `warehouse-overseas-service/` |
diff --git a/src/modules/overseas-warehouse/api/dispatches.test.ts b/src/modules/overseas-warehouse/api/dispatches.test.ts
index 51c81899..4468258d 100644
--- a/src/modules/overseas-warehouse/api/dispatches.test.ts
+++ b/src/modules/overseas-warehouse/api/dispatches.test.ts
@@ -48,9 +48,9 @@ describe('dispatch overseas API', () => {
vi.mocked(overseasHttp.get).mockResolvedValue({ code: '200', message: 'success', data: [] })
vi.mocked(overseasHttp.post).mockResolvedValue({ code: '200', message: 'success', data: { id: '1', dispatchNo: 'PC-1' } })
await listDispatchCandidates({ planIds: '101,102', fbaWarehouseCode: 'ONT8', sourceStage: 'PLAN' })
- await preArrangeDispatch({ planPalletIds: ['190000000000000099'], route: 'LAX -> ONT8' })
+ await preArrangeDispatch({ planPalletIds: ['190000000000000099'] })
expect(overseasHttp.get).toHaveBeenCalledWith('/platform/v1/last-leg/dispatches/candidates', { params: { planIds: '101,102', fbaWarehouseCode: 'ONT8', sourceStage: 'PLAN' } })
- expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/dispatches/pre-arrange', { planPalletIds: ['190000000000000099'], route: 'LAX -> ONT8' })
+ expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/dispatches/pre-arrange', { planPalletIds: ['190000000000000099'] })
})
it('loads platform candidates without selected inbound plans', async () => {
@@ -78,9 +78,9 @@ describe('dispatch overseas API', () => {
it('edits and cancels only by dispatchNo without customer scope or status mutation', async () => {
vi.mocked(overseasHttp.put).mockResolvedValue({ code: '200', message: 'success', data: { id: '1', dispatchNo: 'PC/1' } })
vi.mocked(overseasHttp.delete).mockResolvedValue({ code: '200', message: 'success', data: null })
- await updateDispatch('PC/1', { vehiclePlate: 'CA-100', route: 'LAX → ONT8' })
+ await updateDispatch('PC/1', { vehiclePlate: 'CA-100' })
await cancelDispatch('PC/1')
- expect(overseasHttp.put).toHaveBeenCalledWith('/platform/v1/last-leg/dispatches/PC%2F1', { vehiclePlate: 'CA-100', route: 'LAX → ONT8' })
+ expect(overseasHttp.put).toHaveBeenCalledWith('/platform/v1/last-leg/dispatches/PC%2F1', { vehiclePlate: 'CA-100' })
expect(overseasHttp.put).not.toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ status: expect.anything() }))
expect(overseasHttp.delete).toHaveBeenCalledWith('/platform/v1/last-leg/dispatches/PC%2F1')
})
diff --git a/src/modules/overseas-warehouse/api/dispatches.ts b/src/modules/overseas-warehouse/api/dispatches.ts
index 6cfd0f6b..b5df3545 100644
--- a/src/modules/overseas-warehouse/api/dispatches.ts
+++ b/src/modules/overseas-warehouse/api/dispatches.ts
@@ -53,7 +53,6 @@ export interface DispatchDraftDto { draftToken: string; containers?: DispatchDra
export interface PreArrangeDispatchPayload {
planPalletIds: string[]
- route?: string
fbaAppointmentNo?: string
fbaAppointmentStatus?: string
vehicleAppointmentStatus?: string
@@ -106,7 +105,6 @@ export interface UpdateDispatchPayload {
driverName?: string | null
driverPhone?: string | null
carrierName?: string | null
- route?: string | null
departAt?: string | null
fbaAppointmentNo?: string | null
fbaAppointmentStatus?: string | null
diff --git a/src/modules/overseas-warehouse/pages/DispatchesView.test.ts b/src/modules/overseas-warehouse/pages/DispatchesView.test.ts
index 2929daf6..a8d64658 100644
--- a/src/modules/overseas-warehouse/pages/DispatchesView.test.ts
+++ b/src/modules/overseas-warehouse/pages/DispatchesView.test.ts
@@ -136,6 +136,8 @@ describe('DispatchesView', () => {
expect(source).toContain('removeDispatchItem')
expect(source).not.toContain('托盘箱唛确认')
expect(source).not.toContain('palletCartonMarks')
+ expect(source).toContain('v-model="editForm.route" readonly')
+ expect(source).not.toContain('route: editForm.route.trim()')
expect(source).toContain('dispatchVehicleSizeOptions')
expect(source).not.toContain('departDispatch')
diff --git a/src/modules/overseas-warehouse/pages/DispatchesView.vue b/src/modules/overseas-warehouse/pages/DispatchesView.vue
index b58c56ca..656a8939 100644
--- a/src/modules/overseas-warehouse/pages/DispatchesView.vue
+++ b/src/modules/overseas-warehouse/pages/DispatchesView.vue
@@ -411,7 +411,6 @@ async function submitEdit() {
driverName: editForm.driverName.trim() || null,
driverPhone: editForm.driverPhone.trim() || null,
carrierName: editForm.carrierName.trim() || null,
- route: editForm.route.trim() || null,
departAt: editForm.departAt || null,
fbaAppointmentNo: editForm.fbaAppointmentNo.trim() || null,
fbaAppointmentStatus: editForm.fbaAppointmentStatus || null,
@@ -444,12 +443,14 @@ async function removePallet(itemValue: unknown) {
try {
const updated = await removeDispatchItem(editRow.value.dispatchNo, String(item.id))
editItems.value = [...(updated.items || [])]
+ editForm.route = updated.route || ''
dispatchSnapshots.set(editRow.value.dispatchNo, updated)
const row = rows.value.find((value) => value.dispatchNo === editRow.value?.dispatchNo)
if (row) {
row.palletCount = number(updated.palletCount)
row.packageCount = number(updated.packageCount)
row.orderNos = text(updated.orderNos)
+ row.route = text(updated.route)
}
ElMessage.success('托盘已移除,可重新排车')
} catch (cause) {
@@ -559,7 +560,7 @@ onMounted(load)
共 {{ total }} 条排车记录
派送信息
费用信息(USD)
-
已排车托盘
移除
+
已排车托盘
移除