This commit is contained in:
2026-10-08 17:07:57 +08:00
parent 16d2d9111a
commit c1ddb658bd
111 changed files with 2914 additions and 941 deletions
@@ -1,6 +1,7 @@
import { createPicker, disablePicker, resolvePickerAccess } from './phase4-overseas-pda.mjs'
import { run as createMerchant } from './merchant-qualification-fixture.mjs'
import { prepareFleetTrip, confirmFleetLoading, finishFleetTrip } from './fleet-trip-fulfillment.mjs'
import { verifyFleetTripFinance } from './fleet-trip-finance-acceptance.mjs'
const MODULE = 'phase5-overseas-fulfillment'
const SCENARIO = 'phase5-dispatch-outbound-delivery-exception-visit'
@@ -68,6 +69,8 @@ export function dispatchDestinationConservation(detail, fixture, deliveries = nu
const actualIds = items.map(row => id(row.planPalletId)), expectedIds=fixture.planPalletIds.map(id)
if (!actualIds.every(nonBlank) || actualIds.length !== expectedIds.length
|| new Set(actualIds).size !== actualIds.length || !sameSet(actualIds,expectedIds)) return false
if (items.some(row => nonBlank(row.fbaWarehouseCode) && nonBlank(row.destinationWarehouseCode)
&& id(row.fbaWarehouseCode).trim().toUpperCase() !== id(row.destinationWarehouseCode).trim().toUpperCase())) return false
const actual = codes(items.map(row => row.fbaWarehouseCode || row.destinationWarehouseCode))
const frozen = codes(fixture.destinationWarehouseCodes), route = codes(detail.route.split(','))
if (!sameSet(actual,frozen) || !sameSet(route,actual) || route.length !== new Set(route).size) return false
@@ -444,6 +447,26 @@ export async function verifyPartialOrderCompletion(context, { orderId, customerI
return sameOrder && blocked
}
export function returnedExceptionFactsMatch(detail, delivery, expected) {
const order = detail?.order, shipment = delivery?.order
const receipts = detail?.returnReceipts
if (order?.status !== 'COMPLETED' || order.outboundNo !== expected.outboundNo
|| order.deliveryNo !== expected.deliveryNo || order.inboundCode !== expected.inboundCode
|| id(order.customerId) !== expected.customerId
|| !order.evidenceFileIds?.some(fileId => id(fileId) === expected.evidenceFileId)
|| !Array.isArray(detail.returnSources) || detail.returnSources.length !== 0
|| !Array.isArray(receipts) || receipts.length === 0
|| new Set(receipts.map(row => row.receiptNo)).size !== receipts.length
|| receipts.some(row => !nonBlank(row.receiptNo) || !nonBlank(row.receivedAt)
|| !Number.isSafeInteger(Number(row.receivedPackages)) || Number(row.receivedPackages) <= 0)) return false
const received = receipts.reduce((sum, row) => sum + Number(row.receivedPackages), 0)
const sent = Number(shipment?.packageCount)
return received === Number(order.packageCount) && Number.isSafeInteger(sent) && sent >= received
&& shipment.outboundNo === expected.outboundNo && shipment.deliveryNo === expected.deliveryNo
&& id(shipment.customerId) === expected.customerId
&& shipment.status === (received === sent ? 'RETURNED' : 'RETURN_PENDING')
}
export async function completeReturnedException(context, { customerId, warehouseId, exceptionNo, outboundNo, resume = null }) {
const owned = context.createdResources?.phase5OverseasFulfillment
if (owned?.marker !== context.marker || owned?.returnExceptionNo !== exceptionNo || owned?.outboundNo !== outboundNo) {
@@ -566,13 +589,10 @@ export async function completeReturnedException(context, { customerId, warehouse
const final = await platform(context, 'GET', '回查同派送退回异常完成及来源证据', exceptionPath)
const delivery = await platform(context, 'GET', '回查退回实收后的原派送单',
query(`${LASTLEG_BASE}/delivery-orders/${encodeURIComponent(order.deliveryNo)}`, { customerId }))
context.assert(MODULE, '退回异常完成保留原出库库存来源与实收证据且派送RETURNED', final.response,
final.data?.order?.status === 'COMPLETED' && final.data.order.outboundNo === outboundNo
&& final.data.order.evidenceFileIds?.some(fileId => id(fileId) === evidenceFileId)
&& final.data.returnSources?.some(row => id(row.outboundPalletItemId) === id(source.outboundPalletItemId)
&& id(row.inventoryPositionId) === id(source.inventoryPositionId))
&& delivery.data?.order?.status === 'RETURNED' && delivery.data.order.outboundNo === outboundNo,
trace(final.response), OPTIONS)
context.assert(MODULE, '退回实收保留原出库和收货证据,派送状态按实际退回数量推进', final.response,
returnedExceptionFactsMatch(final.data, delivery.data, { customerId, outboundNo,
deliveryNo: order.deliveryNo, inboundCode: order.inboundCode, evidenceFileId }),
`approved=${final.data?.order?.packageCount}, sent=${delivery.data?.order?.packageCount}, status=${delivery.data?.order?.status}; ${trace(final.response)}`, OPTIONS)
return { exceptionNo, targetPalletId, targetPalletNo: palletCode, evidenceFileId,
inventoryAdjustmentId: id(completed.data.inventoryAdjustmentId), restoredPackages: Number(completed.data.restoredPackages),
targetPositionIds: targetPositions.map(row => id(row.id)) }
@@ -1232,6 +1252,8 @@ export async function run(context) {
exceptionNo: returnExceptionNo, outboundNo })
context.createdResources.phase5OverseasFulfillment.returnReceipt = returnReceipt
context.createdResources.phase5OverseasFulfillment.tripClosure = await finishFleetTrip(context, MODULE, tripProof)
await verifyFleetTripFinance(context, MODULE, { tripNo: tripProof.tripNo, dispatchNos: [dispatchNo],
businesses: deliveryNos.map(deliveryNo => ({ customerId, deliveryNo })), receiptsVisible: true })
context.createdResources.phase5OverseasFulfillment.finalStatus = returnReceipt
? 'RETURNED_WITH_COMPLETED_VISIT' : 'RETURN_PENDING_WITH_COMPLETED_VISIT'
}