fix: complete dropship container after pallet receipt

This commit is contained in:
2026-09-09 19:41:29 +08:00
parent f3bbe0e15f
commit b3e0ea9199
4 changed files with 46 additions and 1 deletions
@@ -927,6 +927,7 @@ public class ReceivingContainerService {
private void validateFbaSkuBalance(String customerId, String containerNo) {
for (InboundRef ref : refs(customerId, containerNo)) {
if (!"FBA".equals(destinationType(ref.line(), ref.order()))) continue;
if ("ONE_CLICK_DROPSHIP".equals(ref.order().getOrderType())) continue;
List<SkuExpectation> skuCodes = skuExpectations(ref);
int received = 0;
for (SkuExpectation sku : skuCodes) {
@@ -448,6 +448,24 @@ class ReceivingContainerServiceBehaviorTest {
verify(workflow, never()).completeContainer(any(), any());
}
@Test
void dropshipWholePalletCanCompleteWithoutBulkSkuReceiptRows() {
stubCurrent("FBA", "CONT-A");
LastlegOrder dropshipOrder = order("FBA");
dropshipOrder.setOrderType("ONE_CLICK_DROPSHIP");
when(orders.selectById(1L)).thenReturn(dropshipOrder);
InboundPlanPalletAllocation received = new InboundPlanPalletAllocation();
received.setLineId(10L); received.setPickedPackageCount(2);
when(allocations.selectList(any())).thenReturn(List.of(received));
when(sessions.updateById(any(ReceivingContainerSession.class))).thenReturn(1);
var completed = service.complete("7", new ReceivingContainerRequests.Action("dropship-complete"));
assertThat(completed.status()).isEqualTo("COMPLETED");
verifyNoInteractions(skuReceipts);
verify(workflow).completeContainer(any(), any());
}
@Test
void completedRequestReplaysWithoutAnActiveCustomerSession() {
ReceivingContainerOperation operation = new ReceivingContainerOperation();
@@ -41,6 +41,10 @@ test('module covers the public tail-order write surface before PDA receiving', (
assert.match(source, /currency === 'CNY'/)
assert.match(source, /reviewStatus === 'PENDING'/)
assert.match(source, /'Idempotency-Key': `\$\{context\.marker\}-tail-fee`/)
assert.match(source, /ensureCurrentUsdRate/)
assert.match(source, /monthFrom=\$\{month\}&monthTo=\$\{month\}/)
assert.match(source, /existing\?\.status === 'ACTIVE'/)
assert.match(source, /businessDate: `\$\{feeMonth\}-01`/)
assert.doesNotMatch(source, /supplierDescription/)
})
@@ -63,6 +63,26 @@ async function detail(context, row, name = '读取尾程订单即时详情') {
return api(context, 'GET', name,
query(`${ORDERS}/${encodeURIComponent(id(row.id))}`, { customerId: id(row.customerId) }))
}
async function ensureCurrentUsdRate(context) {
const month = new Date().toISOString().slice(0, 7)
const path = `/api/platform/v1/last-leg/master-data/exchange-rates?page=1&size=20&monthFrom=${month}&monthTo=${month}`
const listed = await api(context, 'GET', '查询尾程费用业务月汇率', path)
const existing = records(listed.data).find((row) => row?.month === month)
if (existing?.status === 'ACTIVE' && Number(existing?.usdToCnyAverage) > 0) return month
if (existing) {
blocker(context, '尾程费用业务月汇率', `业务月${month}汇率存在但不是ACTIVE,拒绝覆盖既有主数据`, listed.response)
return null
}
const created = await api(context, 'POST', '补齐尾程费用业务月汇率',
'/api/platform/v1/last-leg/master-data/exchange-rates', {
month, usdToCnyAverage: 7.1, remark: `${context.marker} acceptance rate`,
})
context.assert(MODULE, '尾程费用业务月汇率可用于USD兑CNY', created.response,
created.data?.month === month && created.data?.status === 'ACTIVE'
&& Number(created.data?.usdToCnyAverage) > 0,
`month=${month}, status=${created.data?.status || 'missing'}`, OPTIONS)
return created.data?.status === 'ACTIVE' ? month : null
}
async function verifyReceivingWarehouseChange(context, producer, details) {
const bookingId = id(producer.containerBookingId)
if (!bookingId) {
@@ -168,11 +188,13 @@ async function verifyDestinationMergeFeeAndExport(context, details, producer) {
{ orderIds: allIds, palletMergeRequired: false })
context.assert(MODULE, '批量合托分组取消成功', batchCleared.response, batchCleared.data == null, trace(batchCleared.response), OPTIONS)
const feeMonth = await ensureCurrentUsdRate(context)
if (!feeMonth) return false
const feeItem = `${context.marker}-TAIL-FEE`.slice(0, 128)
const fee = await api(context, 'POST', '新增本轮尾程订单费用',
query(`${ORDERS}/${encodeURIComponent(id(representative.id))}/fees`, { customerId: id(representative.customerId) }),
{ feeItemCode: feeItem.slice(0, 64), feeItemName: feeItem, direction: 'RECEIVABLE',
currency: 'USD', amount: 0.01, businessDate: new Date().toISOString().slice(0, 10) },
currency: 'USD', amount: 0.01, businessDate: `${feeMonth}-01` },
{ 'Idempotency-Key': `${context.marker}-tail-fee`.slice(0, 128) })
context.assert(MODULE, '尾程费用冻结币种金额和审核状态', fee.response,
id(fee.data?.orderId) === id(representative.id) && fee.data?.feeItemName === feeItem