87 lines
7.5 KiB
JavaScript
87 lines
7.5 KiB
JavaScript
import { dispatchIo, replay, version } from './grouped-dispatch-acceptance.mjs'
|
|
|
|
const API = '/api/platform/v1/last-leg'
|
|
const id = value => String(value ?? '')
|
|
const rowFact = row => JSON.stringify([id(row.dispatchItemId), row.sourceKey, id(row.customerId), id(row.warehousePalletId),
|
|
row.inboundCode, Number(row.scheduledPackages), Number(row.pickedPackages)])
|
|
|
|
/** 把原散板实收货加入本车唯一主单,并核对既有货物和新增实托守恒。 */
|
|
export async function arrangeLooseboardPool(context, { demand, tripProof }) {
|
|
const receipt = demand?.receipt, binding = demand?.poolBinding
|
|
if (context.createdResources?.phase4OverseasBusiness?.looseboard !== demand || demand?.marker !== context.marker
|
|
|| !context.createdResources?.fleetTrips?.includes(tripProof) || binding?.tripNo !== tripProof?.tripNo
|
|
|| receipt?.status !== 'RECEIVED_READY_FOR_DISPATCH' || !receipt.incomingNo || receipt.dispatch
|
|
|| !receipt.pallets?.length || typeof context.checkpoint !== 'function') throw Error('散板排入原车缺少本轮确认实收证据')
|
|
const proof = receipt.dispatch = { marker: context.marker, status: 'ARRANGING', requests: [], dispatchNo: tripProof.dispatchNo }
|
|
const io = dispatchIo(context, 'phase4-overseas-business', proof, `LOOSEBOARD-${binding.bookingNo}`)
|
|
const tripPath = `${API}/fleet/trips/${encodeURIComponent(tripProof.tripNo)}`
|
|
const dispatchPath = `${API}/dispatches/${encodeURIComponent(tripProof.dispatchNo)}`
|
|
const check = (name, ok) => io.check(name, null, Boolean(ok))
|
|
try {
|
|
let current = await io.command('GET', dispatchPath, undefined, '散板加货前原车详情')
|
|
const before = await io.command('GET', `${dispatchPath}/items`, undefined, '冻结原车既有货物')
|
|
check('原车仍未扫码且原货完整', current?.dispatchNo === tripProof.dispatchNo && current.status === 'ACTIVE'
|
|
&& ['BEFORE_LOADING', 'LOADING'].includes(current.stage) && Number(current.counts?.pickedPackageCount) === 0
|
|
&& Number(current.counts?.shippedPackageCount) === 0 && before?.dispatchNo === tripProof.dispatchNo
|
|
&& Array.isArray(before.items) && before.items.length > 0
|
|
&& before.items.every(row => row.inboundCode !== receipt.incomingNo && Number(row.pickedPackages) === 0))
|
|
const originalRows = before.items.map(rowFact)
|
|
const originalPlan = await io.command('GET', `${tripPath}/capacity-plan`, undefined, '冻结原车容量计划')
|
|
check('本车加货前容量计划有效', originalPlan?.planValid === true && !originalPlan.blockReason && originalPlan.capacityPlan?.pallets?.length > 0)
|
|
if (current.stage === 'LOADING') {
|
|
const body = { recordVersion: version(current.recordVersion), idempotencyKey: io.key('return-review'), reason: '本轮零扫码明确退回,加入散板原单实际实收货物' }
|
|
const result = await io.command('POST', `${dispatchPath}/return-to-review`, body, '原车零扫码退回审核')
|
|
await replay(io, `${dispatchPath}/return-to-review`, body, result, '退回审核同键重放')
|
|
current = await io.command('GET', dispatchPath, undefined, '回查原车可加货阶段')
|
|
}
|
|
check('原车已处装车前', current?.dispatchNo === tripProof.dispatchNo && current.stage === 'BEFORE_LOADING')
|
|
const list = await io.command('GET', `${tripPath}/bookings`, undefined, '读取原散板承运最新版本')
|
|
const rows = list?.records?.filter(row => row.bookingNo === binding.bookingNo) || [], booking = rows[0]
|
|
check('仅把原承运原来货排入本车', rows.length === 1 && booking.tripNo === tripProof.tripNo
|
|
&& id(booking.customerId) === id(demand.customerId) && booking.incomingNo === receipt.incomingNo
|
|
&& booking.status === 'RECEIVED' && !booking.dispatchNo
|
|
&& booking.receipt?.source?.receiptHash === receipt.acceptedReceipt?.source?.receiptHash)
|
|
const body = proof.arrangeRequest = { tripRecordVersion: version(list.tripRecordVersion), recordVersion: version(booking.recordVersion), idempotencyKey: io.key('arrange') }
|
|
const path = `${API}/fleet/capacity-offers/inquiries/${encodeURIComponent(binding.bookingNo)}/arrange-dispatch`
|
|
const arranged = await io.command('POST', path, body, '正式接口排入本车唯一主单')
|
|
check('排入结果绑定同一主单和来源', arranged?.booking?.status === 'BOUND' && arranged.booking.bookingNo === binding.bookingNo
|
|
&& arranged.booking.tripNo === tripProof.tripNo && arranged.booking.dispatchNo === tripProof.dispatchNo
|
|
&& arranged.booking.incomingNo === receipt.incomingNo && id(arranged.booking.customerId) === id(demand.customerId))
|
|
const again = await replay(io, path, body, arranged, '排入本车同键重放')
|
|
check('排入重放明确返回原结果', again?.replayed === true)
|
|
const after = await io.command('GET', `${dispatchPath}/items`, undefined, '加货后回查全部真实货物')
|
|
const added = after?.items?.filter(row => row.inboundCode === receipt.incomingNo) || []
|
|
check('加货不删除或重复既有货物', after?.dispatchNo === tripProof.dispatchNo && after.items?.length === before.items.length + receipt.pallets.length
|
|
&& originalRows.every(fact => after.items.filter(row => rowFact(row) === fact).length === 1))
|
|
check('新增行精确对应原散板实收各托箱数', added.length === receipt.pallets.length
|
|
&& receipt.pallets.every(p => added.filter(row => id(row.warehousePalletId) === id(p.warehousePalletId)
|
|
&& id(row.customerId) === id(demand.customerId) && row.palletNo === p.palletNo
|
|
&& Number(row.scheduledPackages) === p.packages && Number(row.pickedPackages) === 0).length === 1))
|
|
const plan = await io.command('GET', `${tripPath}/capacity-plan`, undefined, '新增实收后读取容量依据')
|
|
const detail = await io.command('GET', tripPath, undefined, '读取原车真实卸货站')
|
|
check('容量货物仅属于本车唯一主单', plan?.planValid === false && plan.cargo?.every(row => row.dispatchNo === tripProof.dispatchNo))
|
|
const destinationKey = binding.createRequest.cargo.destinationKey
|
|
const stopIndexes = detail?.trip?.stops?.map((stop, index) => stop.destinationKey === destinationKey ? index : -1).filter(index => index > 0) || []
|
|
check('实收目的地对应唯一卸货站', detail?.trip?.tripNo === tripProof.tripNo && stopIndexes.length === 1)
|
|
const pallets = structuredClone(originalPlan.capacityPlan.pallets)
|
|
for (const p of receipt.pallets) {
|
|
const measured = receipt.acceptedReceipt?.source?.pallets?.find(row => id(row.warehousePalletId) === id(p.warehousePalletId))
|
|
check('新增容量仅用实际实托及实测', measured && Number(measured.weightKg) > 0 && Number(measured.volumeCbm) > 0
|
|
&& !pallets.some(row => id(row.warehousePalletId) === id(p.warehousePalletId)))
|
|
pallets.push({ warehousePalletId: p.warehousePalletId, palletSpaces: 1,
|
|
weightKg: Number(measured.weightKg), volumeCbm: Number(measured.volumeCbm), deliveryIndex: stopIndexes[0] })
|
|
}
|
|
const refreshed = await io.command('POST', `${tripPath}/capacity-plan`, { recordVersion: version(plan.recordVersion), manifestHash: plan.manifestHash,
|
|
pallets, reason: '保留原货预计占用,追加散板实际实收容量,仍须独立完成装车确认' }, '重核原车加货后的容量计划')
|
|
check('原车加货后容量有效', refreshed?.planValid === true && !refreshed.blockReason)
|
|
proof.status = 'BOUND_AWAITING_REVIEW'
|
|
proof.pallets = receipt.pallets
|
|
proof.addedItems = added
|
|
await context.checkpoint()
|
|
return proof
|
|
} catch (error) {
|
|
proof.status = 'FAILED'; proof.failure = error.message
|
|
await context.checkpoint(); throw error
|
|
}
|
|
}
|