From f6ee80d6776c8a30b85d4b2119cff7f3268730e7 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 9 Sep 2026 12:25:13 +0800 Subject: [PATCH] fix(pda): preserve authoritative overseas receiving reservation hints --- AGENTS.md | 1 + src/api/inbound.js | 8 ++--- src/api/receiving-adapter.js | 16 ++++++++-- src/pages/operation/operation.vue | 8 +++-- tests/receiving-adapter.test.mjs | 49 ++++++++++++++++++++++++++++++- 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 97e5561..8316ba2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,6 +56,7 @@ ## 海外业务接口边界 - `src/api/inbound.js` 已接入待卸柜候选、柜详情/prepare、current 恢复、三类照片签收、统一箱码定位入仓码、实物托盘解析、FBA bulk/OVERSEAS carton 收货及整柜完成;FBA bulk 不提交 `skuCode`,库存分配由服务端按计划明细完成;响应与 404/409 判定集中在 `src/api/receiving-adapter.js`,双 Token 刷新与海外 401 单次重放由 `src/api/overseas.js` 统一处理。 +- 入仓摘要、箱码解析和实收响应的 `assigned/mergeAllowed/urgent/mergeHint` 只消费 Lastleg 返回值,布尔标志只有 `true` 生效,`mergeAllowed` 缺省为 `false`;合托仅作提示,不能改变收货资格或触发分配。首扫实物托盘只确认服务端已有托盘、库位与资源锁预留,不在设备端重新分配托盘、库位或资源锁;只提交业务箱码、托盘码及当前收货数量,最终资格由服务端决定。 - `src/api/overseas.js` 的 `overseasUrl` 统一拼接普通请求、上传和下载地址:先移除运行时 `apiBase` 末尾斜杠;当基址已以 `/api` 或 `/overseas-api` 结尾时,再移除业务路径重复的 `/api` 前缀。各业务 API 不得自行拼接 `session.apiBase`,也不得依赖双斜杠或重复 `/api` 被网关容错。 - 待卸柜候选只消费服务端已按 Token 绑定仓过滤的结果,并严格校验每柜正式 `overseasWarehouseCode`、`overseasWarehouseName` 与 `warehouse-profile` 一致;字段缺失或仓不一致一律 fail-closed,不得从 FBA/业务目的仓或内部 ID 推导。候选保留服务端返回的未齐全柜并严格消费 `allPlansReady`、`operable`、`blockReason`、`plannedArrivalAt` 与柜级计划统计;`operable=false` 只能查看只读摘要,不得调用 prepare。候选顺序沿用服务端“可操作优先、同组计划到仓时间升序、时间为空最后”的排序,前端不得用入仓码/计划数组长度自行推导就绪或把预计到港冒充计划到仓。 - 候选详情、prepare 响应和 current 恢复都必须重复校验同一接柜仓编码;prepare 只建立已分配柜的作业会话,不得当作首次认领。服务端仓冲突的 `409/A0409` 中文错误保留 `requestId`/`traceId`;会话建立后仍按既有 current 恢复与完成前禁止切柜逻辑处理。 diff --git a/src/api/inbound.js b/src/api/inbound.js index ca941ce..307975d 100644 --- a/src/api/inbound.js +++ b/src/api/inbound.js @@ -1,5 +1,5 @@ import { overseasDownload, overseasRequest, overseasUpload } from '@/api/overseas.js' -import { normalizeReceivingCandidate, normalizeReceivingSession, normalizeReceivingWarehouseProfile } from '@/api/receiving-adapter.js' +import { normalizeReceivingAssignment, normalizeReceivingInbound, normalizeReceivingCandidate, normalizeReceivingSession, normalizeReceivingWarehouseProfile } from '@/api/receiving-adapter.js' const RECEIVING_BASE = '/pda/v1/last-leg/receiving-containers' const current = (suffix = '') => `${RECEIVING_BASE}/current${suffix}` @@ -20,8 +20,8 @@ export const uploadCurrentUnloadPhoto = (filePath) => overseasUpload(current('/u export const downloadCurrentUnloadPhoto = (downloadPath) => overseasDownload(downloadPath) export const signCurrentReceiving = async (request) => normalizeReceivingSession(await overseasRequest('POST', current('/sign'), request)) export const getCurrentReceiving = async () => normalizeReceivingSession(await overseasRequest('GET', current())) -export const resolveCurrentCarton = (cartonMark) => overseasRequest('POST', current('/cartons/resolve'), { cartonMark }) +export const resolveCurrentCarton = async (cartonMark) => normalizeReceivingInbound(await overseasRequest('POST', current('/cartons/resolve'), { cartonMark })) export const resolveCurrentPallet = (palletCode, inboundCode) => overseasRequest('POST', current('/pallets/resolve'), { palletCode, inboundCode }) -export const receiveCurrentBulk = (request) => overseasRequest('POST', current('/receipts/bulk'), request) -export const receiveCurrentCarton = (request) => overseasRequest('POST', current('/receipts/carton'), request) +export const receiveCurrentBulk = async (request) => normalizeReceivingAssignment(await overseasRequest('POST', current('/receipts/bulk'), request)) +export const receiveCurrentCarton = async (request) => normalizeReceivingAssignment(await overseasRequest('POST', current('/receipts/carton'), request)) export const completeCurrentReceiving = async (request) => normalizeReceivingSession(await overseasRequest('POST', current('/complete'), request)) diff --git a/src/api/receiving-adapter.js b/src/api/receiving-adapter.js index 9b73425..20a304d 100644 --- a/src/api/receiving-adapter.js +++ b/src/api/receiving-adapter.js @@ -38,15 +38,21 @@ export function assertReceivingWarehouse(value, profile, label = '当前柜') { export function normalizeReceivingInbound(value = {}) { return { - ...value, + ...normalizeReceivingAssignment(value), plannedPalletCount: number(value.plannedPalletCount), totalPackages: number(value.totalPackages), receivedPackages: number(value.receivedPackages), - pendingPackages: number(value.pendingPackages), + pendingPackages: number(value.pendingPackages) + } +} + +export function normalizeReceivingAssignment(value = {}) { + return { + ...value, assigned: value.assigned === true, mergeAllowed: value.mergeAllowed === true, urgent: value.urgent === true, - mergeHint: text(value.mergeHint) + mergeHint: value.mergeAllowed === true ? text(value.mergeHint) : '' } } @@ -104,6 +110,10 @@ export function toReceivingError(error, fallback = '海外仓作业失败') { const code = error?.code const rawMessage = String(error?.message || '').trim() const palletConflictMessages = { + 'Reserved pallet binding is incomplete': '预留托盘资料不完整,请联系管理员检查入仓计划', + 'Reserved pallet customer binding is invalid': '预留托盘与当前客户不匹配,请联系管理员检查入仓计划', + 'Scanned pallet does not match the reserved inbound plan resource': '扫描托盘与入仓计划预留资源不一致,请扫描已预留的托盘', + 'Pallet code matches multiple reserved resources in this inbound plan': '托盘码对应多个预留资源,请联系管理员检查入仓计划', 'Pallet is not idle': '该托盘已被占用,当前不可用于收货,请更换可用托盘', 'Pallet location was not found': '该托盘未关联有效库位,请联系管理员维护后重试', 'Pallet virtual location is not active': '该托盘所在虚拟库位不可用,请更换托盘或联系管理员', diff --git a/src/pages/operation/operation.vue b/src/pages/operation/operation.vue index b7f305a..5447603 100644 --- a/src/pages/operation/operation.vue +++ b/src/pages/operation/operation.vue @@ -75,13 +75,14 @@ {{ activeInbound.destinationType === 'FBA' ? 'FBA 按箱数收货' : '海外仓逐箱收货' }}{{ destinationLabel(activeInbound) }} {{ activeInbound.inboundCode }} {{ activeInbound.assigned ? '已分配' : '未分配' }}{{ activeInbound.mergeAllowed ? '可合托(现场自选)' : '不合托' }}{{ activeInbound.urgent ? '加急' : '普通' }} - {{ activeInbound.mergeHint }} + {{ activeInbound.mergeHint }} 总计 {{ activeInbound.totalPackages }} 箱已收 {{ activeInbound.receivedPackages }} 箱待收 {{ activeInbound.pendingPackages }} 箱 - 2. 扫描实物托盘码 + 2. 扫描预留实物托盘码 + 扫码确认服务端已预留的托盘及库位;合托提示不改变当前分配。 - {{ resolvedPallet.palletCode }}{{ resolvedPallet.compatible ? '目的仓匹配,可收货' : '托盘不匹配' }} + {{ resolvedPallet.palletCode }}{{ resolvedPallet.compatible ? '预留托盘校验通过,可收货' : '托盘不匹配' }}