From 3abcdcbd8958e1ab5810fd44c1407754acfe4201 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Thu, 29 Oct 2026 15:49:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/business-index.md | 2 ++ .../api/warehouseServices.ts | 2 +- .../views/WarehouseServiceView.vue | 27 ++++++++++++------- .../views/warehouseDisposalForm.test.mjs | 10 +++++-- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/docs/business-index.md b/docs/business-index.md index 656ffe9..f98f01a 100644 --- a/docs/business-index.md +++ b/docs/business-index.md @@ -38,3 +38,5 @@ | 履约、海外仓与财务 | 入库计费、柜次进度、船期、海外仓贴标/转运/废弃/退货换标、消息和客户账单 | `src/modules/first-leg/`、`overseas-warehouse/`、`finance/`、`notifications/`;按服务端权限核对客户可见字段、客户隔离和只读/写入边界 | 客户侧边界:仍以服务端权限和客户可见字段为准;PDA、平台后台操作和内部证据不因全量验收自动暴露到 Client。财务按 `../../docs/财务管理/` 与 `src/modules/finance/routes.ts` 定位。 + +- 货物废弃:有箱唛按具体箱选择,备注选填;无箱唛但有托盘时只填申请总箱数与必填备注,预留相关托盘供PDA扫描现场箱唛自动计数。详情展示废弃备注、托盘、实际废弃箱唛与实际数量;现场扫描清单与申请选箱快照分别展示。 diff --git a/src/modules/overseas-warehouse/api/warehouseServices.ts b/src/modules/overseas-warehouse/api/warehouseServices.ts index e7a2da2..ac3abd5 100644 --- a/src/modules/overseas-warehouse/api/warehouseServices.ts +++ b/src/modules/overseas-warehouse/api/warehouseServices.ts @@ -132,7 +132,7 @@ export function getDiscard(discardNo: string) { export function listDiscardCandidates(inboundCode?: string) { return apiRequest({ method: 'GET', url: '/merchant/v1/last-leg/cargo-discards/inventory-candidates', params: { inboundCode: inboundCode || undefined } }) } -export function createDiscard(payload: { items: Array<{ orderId?: string; returnCartonId?: string; inventoryPositionId?: string; cartonIds?: string[]; skuCode: string; discardPackages: number }>; idempotencyKey: string; remark?: string }) { +export function createDiscard(payload: { items: Array<{ orderId?: string; returnCartonId?: string; inventoryPositionId?: string; cartonIds?: string[]; skuCode: string; discardPackages: number }>; idempotencyKey: string; remark?: string; requestedPackages?: number }) { return apiRequest>({ method: 'POST', url: '/merchant/v1/last-leg/cargo-discards', data: payload }) } export function cancelDiscard(discardNo: string) { diff --git a/src/modules/overseas-warehouse/views/WarehouseServiceView.vue b/src/modules/overseas-warehouse/views/WarehouseServiceView.vue index a583831..34e9fab 100644 --- a/src/modules/overseas-warehouse/views/WarehouseServiceView.vue +++ b/src/modules/overseas-warehouse/views/WarehouseServiceView.vue @@ -153,7 +153,7 @@ interface ServiceDetail extends Record { relabelContactPhone?: string; relabelDeliveryMethod?: string; relabelNo?: string relabelStatus?: string; relabelFulfillmentStatus?: string; createdAt?: string inboundCode?: string; sourceOrders?: Array<{ lastlegOrderId?: string | number }> - items?: Array<{ id?: string | number; cartonMarks?: string[]; inboundCode?: string; productName?: string; discardPackages?: number; quantityUnit?: string; actualDiscardPackages?: number | null; skuCode?: string; productNameZh?: string; quantity?: number | null; packageCount?: number; shippingMark?: string }> + items?: Array<{ id?: string | number; actualCartonMarks?: string[]; palletNo?: string; cartonMarks?: string[]; inboundCode?: string; productName?: string; discardPackages?: number; quantityUnit?: string; actualDiscardPackages?: number | null; skuCode?: string; productNameZh?: string; quantity?: number | null; packageCount?: number; shippingMark?: string }> relabelBindings?: Array<{ operationType?: string; customerCartonMark?: string; originalSku?: string; newSku?: string; quantity?: number; fbaCartonMark?: string; destinationWarehouseCode?: string; remark?: string }> cartons?: TransferCarton[]; pallets?: TransferPallet[]; measurementPhotos?: TransferMeasurementPhoto[]; customerConfirmedAt?: string } @@ -277,6 +277,8 @@ const listColumns = computed(() => config.value.columns.map(( : { key: String(index - 1), prop: String(index - 1), label, minWidth: label === '关联业务单号' ? 300 : index === 1 || label === '换标单号' ? 190 : 130, singleLine: label !== '关联业务单号', className: label === '关联业务单号' ? 'related-business-number-cell' : undefined, slotName: props.service === 'labeling' && ['SKU 数(种)', '箱数', '贴标件数'].includes(label) ? 'labelingQuantity' : props.service === 'labeling' && label === '换标类型' ? 'labelingType' : props.service === 'labeling' && label === '状态' ? 'labelingStatus' : props.service === 'transfer' && label === '换标状态' ? 'transferRelabelStatus' : props.service === 'transfer' && label === '状态' ? 'transferStatus' : undefined })) +const discardRequestedPackages = ref() +const discardQuantityMode = computed(() => Boolean(discardSelectedCandidate.value?.sources.length && discardSelectedCandidate.value.sources.every(source => source.sourceType === 'INVENTORY' && !source.cartonSelectionSupported && Boolean(source.palletNo)))) const discardSelectedBoxKeys = ref([]) const discardBoxSearch = ref('') const discardCandidates = ref([]) @@ -940,6 +942,7 @@ function importSelectedBindingsToDetails() { ElMessage.success(`已将 ${selected.length} 条绑定关系导入本次换标明细`) } function selectDiscardInbound(orderId: string) { + discardRequestedPackages.value = undefined discardSelectedBoxKeys.value = [] discardBoxSearch.value = '' const candidate = discardDocuments.value.find((item) => item.orderId === orderId) @@ -1144,7 +1147,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') { const primaryWarehouse = props.service === 'relabel' ? form.sourceWarehouse : form.warehouse const relabelRows = labelingMode.value === 'carton' ? cartonDetailRows.value : skuRelabelRows.value const invalidCargo = props.service === 'disposal' - ? !selectedDiscardOrderId.value || cargoRows.value.every((item) => item.quantity < 1) + ? !selectedDiscardOrderId.value || (discardQuantityMode.value && (!Number.isInteger(discardRequestedPackages.value) || Number(discardRequestedPackages.value) < 1 || Number(discardRequestedPackages.value) > Number(discardSelectedCandidate.value?.availablePackages || 0))) || cargoRows.value.every((item) => item.quantity < 1) || cargoRows.value.some((item) => item.quantity < 0 || item.quantity > Number(item.availableQuantity || 0)) : (props.service === 'labeling' || props.service === 'relabel') && (relabelRows.length === 0 || (props.service === 'labeling' && labelingMode.value !== 'carton' ? skuRelabelRows.value.some((item) => !item.cartonMark || !item.originalSku || !item.newSku || item.quantity < 1 @@ -1182,6 +1185,9 @@ async function submit(action: 'draft' | 'submit' = 'submit') { if (props.service === 'disposal' && !form.reason) { ElMessage.warning('请选择废弃原因'); return } + if (props.service === 'disposal' && discardQuantityMode.value && !form.remark.trim()) { + ElMessage.warning('请填写废弃备注'); return + } if (props.service === 'relabel' && (!form.destination || !form.address || !form.deliveryMethod || !form.estimatedArrivalAt || !form.fbaShipmentNo || form.volumeCbm <= 0)) { ElMessage.warning('请填写完整的派送信息、体积、预计到达时间和 FBA 货件号'); return } @@ -1259,7 +1265,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') { returnCartonId: item.sourceType === 'RETURN_CARTON' ? item.returnCartonId : undefined, skuCode: item.sku, discardPackages: item.quantity, cartonIds: item.discardCartonIds } }) - await createDiscard({ items, idempotencyKey, remark: [form.reason, form.remark].filter(Boolean).join(';') || undefined }) + await createDiscard({ items, idempotencyKey, requestedPackages: discardQuantityMode.value ? discardRequestedPackages.value : undefined, remark: [form.reason, form.remark].filter(Boolean).join(';') || undefined }) } else { const packageCount = cargoRows.value.reduce((sum, item) => sum + Number(item.expectedPackageCount || 0), 0) const customerCartonMarks = (form.relabel ? transferRelabelRows.value : cargoRows.value).flatMap(transferCustomerMarks) @@ -1724,7 +1730,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) { - +
*是否贴标不需要贴标需要贴标
@@ -1781,14 +1787,15 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {