fix(lastleg): resolve mixed-pallet return sources by dispatch snapshots
This commit is contained in:
+10
-5
@@ -485,12 +485,17 @@ public class ExceptionOrderService {
|
||||
for (DispatchPdaPickOrder attribution : attributions) {
|
||||
LastlegOrder tail = lastMileOrders.selectById(attribution.getOrderId());
|
||||
if (tail == null || !customerId.equals(tail.getCustomerId())) continue;
|
||||
OutboundPalletItem outboundItem = outboundItemById.values().stream()
|
||||
List<OutboundPalletItem> matchingItems = outboundItemById.values().stream()
|
||||
.filter(item -> Objects.equals(item.getWarehousePalletId(), dispatchItem.getWarehousePalletId()))
|
||||
.filter(item -> !StringUtils.hasText(item.getInboundCode())
|
||||
|| Objects.equals(item.getInboundCode(), tail.getInboundCode()))
|
||||
.findFirst().orElse(null);
|
||||
if (outboundItem == null) continue;
|
||||
.filter(item -> Objects.equals(item.getInboundCode(), dispatchItem.getInboundCode()))
|
||||
.filter(item -> Objects.equals(item.getDestinationWarehouseId(), dispatchItem.getDestinationWarehouseId()))
|
||||
.filter(item -> Objects.equals(item.getDestinationWarehouseCode(), dispatchItem.getDestinationWarehouseCode()))
|
||||
.filter(item -> Objects.equals(item.getDestinationWarehouseName(), dispatchItem.getDestinationWarehouseName()))
|
||||
.filter(item -> Objects.equals(item.getOrderRefs(), dispatchItem.getOrderRefs())).toList();
|
||||
if (matchingItems.size() > 1) throw conflict("排车明细对应的原出库托盘项不唯一,拒绝退回来源");
|
||||
if (matchingItems.isEmpty()) continue;
|
||||
OutboundPalletItem outboundItem = matchingItems.get(0);
|
||||
// A mixed pallet has one physical outbound item; each source is identified by the actual pick attribution.
|
||||
String key = outboundItem.getId() + ":" + pick.getInventoryPositionId() + ":" + tail.getInboundCode();
|
||||
long packages = Objects.requireNonNullElse(attribution.getPackages(), 0L);
|
||||
aggregated.merge(key, new Source(outboundItem.getId(), pick.getInventoryPositionId(),
|
||||
|
||||
+113
@@ -79,6 +79,7 @@ class ExceptionOrderDispatchReturnSourceTest {
|
||||
DispatchItem dispatchItem = new DispatchItem();
|
||||
dispatchItem.setId(202L); dispatchItem.setCustomerId("1001"); dispatchItem.setDispatchOrderId(200L);
|
||||
dispatchItem.setWarehousePalletId(501L);
|
||||
dispatchItem.setInboundCode("IN-1");
|
||||
DispatchPdaPick pick = new DispatchPdaPick();
|
||||
pick.setId(203L); pick.setCustomerId("1001"); pick.setTaskId(201L);
|
||||
pick.setDispatchItemId(202L); pick.setInventoryPositionId(601L); pick.setPackages(2L);
|
||||
@@ -112,4 +113,116 @@ class ExceptionOrderDispatchReturnSourceTest {
|
||||
assertThat(source.maxReturnPackages()).isEqualTo(2L);
|
||||
});
|
||||
}
|
||||
@org.junit.jupiter.params.ParameterizedTest
|
||||
@org.junit.jupiter.params.provider.ValueSource(strings = {"SINGLE", "MULTI", "MULTI_REVERSED", "AMBIGUOUS"})
|
||||
void mixedPalletRetainsSecondInboundSourceAndSumsOnlyItsActualPickAttributions(String layout) {
|
||||
var metadata = new org.apache.ibatis.builder.MapperBuilderAssistant(
|
||||
new com.baomidou.mybatisplus.core.MybatisConfiguration(), "mixed-return-source-test");
|
||||
com.baomidou.mybatisplus.core.metadata.TableInfoHelper.initTableInfo(metadata, LastlegOrder.class);
|
||||
com.baomidou.mybatisplus.core.metadata.TableInfoHelper.initTableInfo(metadata, LastlegInboundOrder.class);
|
||||
DeliveryOrder delivery = new DeliveryOrder();
|
||||
delivery.setId(400L); delivery.setCustomerId("1001"); delivery.setDeliveryNo("DL-1");
|
||||
delivery.setOutboundNo("OUT-1"); delivery.setStatus("DELIVERED");
|
||||
OutboundOrder outbound = new OutboundOrder();
|
||||
outbound.setId(300L); outbound.setCustomerId("1001"); outbound.setOutboundNo("OUT-1");
|
||||
outbound.setDispatchOrderId(200L);
|
||||
OutboundPalletItem outboundItem = new OutboundPalletItem();
|
||||
outboundItem.setId(301L); outboundItem.setCustomerId("1001"); outboundItem.setOutboundOrderId(300L);
|
||||
outboundItem.setWarehousePalletId(501L); outboundItem.setPalletNo("PLT-1"); outboundItem.setInboundCode("IN-1");
|
||||
DispatchPdaTask task = new DispatchPdaTask();
|
||||
task.setId(201L); task.setCustomerId("1001"); task.setDispatchOrderId(200L);
|
||||
DispatchItem dispatchItem = new DispatchItem();
|
||||
dispatchItem.setId(202L); dispatchItem.setCustomerId("1001"); dispatchItem.setDispatchOrderId(200L);
|
||||
dispatchItem.setWarehousePalletId(501L);
|
||||
dispatchItem.setInboundCode("IN-1");
|
||||
DispatchPdaPick pick = new DispatchPdaPick();
|
||||
pick.setId(203L); pick.setCustomerId("1001"); pick.setTaskId(201L);
|
||||
pick.setDispatchItemId(202L); pick.setInventoryPositionId(601L); pick.setPackages(1L);
|
||||
DispatchPdaPickOrder attribution = new DispatchPdaPickOrder();
|
||||
attribution.setId(204L); attribution.setCustomerId("1001"); attribution.setPickId(203L);
|
||||
attribution.setOrderId(101L); attribution.setPackages(1L);
|
||||
LastlegOrder tail = new LastlegOrder();
|
||||
tail.setId(101L); tail.setCustomerId("1001"); tail.setOrderNo("LM-1"); tail.setInboundCode("IN-1");
|
||||
tail.setDeliveryOrderNo("DL-1"); tail.setOutboundOrderNo("OUT-1"); tail.setPackageCount(1);
|
||||
LastlegInboundOrder inbound = new LastlegInboundOrder();
|
||||
inbound.setId(102L); inbound.setCustomerId("1001"); inbound.setInboundCode("IN-1"); inbound.setPackageCount(1);
|
||||
|
||||
LastlegOrder secondTail = new LastlegOrder();
|
||||
secondTail.setId(111L); secondTail.setCustomerId("1001"); secondTail.setOrderNo("LM-2"); secondTail.setInboundCode("IN-2");
|
||||
secondTail.setDeliveryOrderNo("DL-1"); secondTail.setOutboundOrderNo("OUT-1"); secondTail.setPackageCount(3);
|
||||
LastlegInboundOrder secondInbound = new LastlegInboundOrder();
|
||||
secondInbound.setId(112L); secondInbound.setCustomerId("1001"); secondInbound.setInboundCode("IN-2"); secondInbound.setPackageCount(3);
|
||||
DispatchPdaPick secondPick = new DispatchPdaPick();
|
||||
secondPick.setId(213L); secondPick.setCustomerId("1001"); secondPick.setTaskId(201L);
|
||||
secondPick.setDispatchItemId(202L); secondPick.setInventoryPositionId(602L); secondPick.setPackages(3L);
|
||||
DispatchPdaPickOrder secondOne = new DispatchPdaPickOrder();
|
||||
secondOne.setId(214L); secondOne.setCustomerId("1001"); secondOne.setPickId(213L); secondOne.setOrderId(111L); secondOne.setPackages(1L);
|
||||
DispatchPdaPickOrder secondTwo = new DispatchPdaPickOrder();
|
||||
secondTwo.setId(215L); secondTwo.setCustomerId("1001"); secondTwo.setPickId(213L); secondTwo.setOrderId(111L); secondTwo.setPackages(2L);
|
||||
|
||||
when(deliveryOrders.findByNo("1001", "DL-1")).thenReturn(delivery);
|
||||
when(lastMileOrders.selectList(any())).thenAnswer(call -> {
|
||||
var query = (com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LastlegOrder>) call.getArgument(0);
|
||||
query.getSqlSegment();
|
||||
var parameters = query.getParamNameValuePairs().values();
|
||||
assertThat(parameters).contains("1001");
|
||||
if (parameters.contains("DL-1")) return List.of(tail, secondTail);
|
||||
if (parameters.contains("LM-1") && parameters.contains("IN-1")) return List.of(tail);
|
||||
if (parameters.contains("LM-2") && parameters.contains("IN-2")) return List.of(secondTail);
|
||||
throw new AssertionError("Unexpected tail source query: " + parameters);
|
||||
});
|
||||
when(inboundOrders.selectList(any())).thenAnswer(call -> {
|
||||
var query = (com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LastlegInboundOrder>) call.getArgument(0);
|
||||
query.getSqlSegment();
|
||||
var parameters = query.getParamNameValuePairs().values();
|
||||
assertThat(parameters).contains("1001");
|
||||
if (parameters.contains("IN-1")) return List.of(inbound);
|
||||
if (parameters.contains("IN-2")) return List.of(secondInbound);
|
||||
throw new AssertionError("Unexpected inbound source query: " + parameters);
|
||||
});
|
||||
when(outboundOrders.findByNo("1001", "OUT-1")).thenReturn(outbound);
|
||||
OutboundPalletItem otherItem = new OutboundPalletItem();
|
||||
otherItem.setId(302L); otherItem.setCustomerId("1001"); otherItem.setOutboundOrderId(300L);
|
||||
otherItem.setWarehousePalletId(501L); otherItem.setPalletNo("PLT-1"); otherItem.setInboundCode("IN-1");
|
||||
if (!"AMBIGUOUS".equals(layout)) {
|
||||
otherItem.setDestinationWarehouseId("OTHER"); otherItem.setDestinationWarehouseCode("OTHER");
|
||||
otherItem.setDestinationWarehouseName("OTHER"); otherItem.setOrderRefs("OTHER");
|
||||
}
|
||||
var outboundSnapshots = "SINGLE".equals(layout) ? List.of(outboundItem)
|
||||
: "MULTI_REVERSED".equals(layout) ? List.of(otherItem, outboundItem) : List.of(outboundItem, otherItem);
|
||||
when(outboundPalletItems.findByOutbound("1001", 300L)).thenReturn(outboundSnapshots);
|
||||
when(outboundPdaPicks.findByOutbound("1001", 300L)).thenReturn(List.of());
|
||||
when(dispatchPdaTasks.findByDispatch("1001", 200L)).thenReturn(task);
|
||||
when(dispatchItems.findByDispatchOrder("1001", 200L)).thenReturn(List.of(dispatchItem));
|
||||
when(dispatchPdaPicks.listTask("1001", 201L)).thenReturn(List.of(pick, secondPick));
|
||||
when(dispatchPdaPickOrders.listPick("1001", 203L)).thenReturn(List.of(attribution));
|
||||
when(lastMileOrders.selectById(101L)).thenReturn(tail);
|
||||
if (!"AMBIGUOUS".equals(layout)) {
|
||||
when(dispatchPdaPickOrders.listPick("1001", 213L)).thenReturn(List.of(secondOne, secondTwo));
|
||||
when(lastMileOrders.selectById(111L)).thenReturn(secondTail);
|
||||
}
|
||||
|
||||
if ("AMBIGUOUS".equals(layout)) {
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> service.returnCandidates("1001", "DL-1"))
|
||||
.isInstanceOf(org.springframework.web.server.ResponseStatusException.class).hasMessageContaining("原出库托盘项不唯一");
|
||||
return;
|
||||
}
|
||||
|
||||
var candidates = service.returnCandidates("1001", "DL-1");
|
||||
|
||||
assertThat(candidates).hasSize(2);
|
||||
var first = candidates.stream().filter(row -> "IN-1".equals(row.inboundCode())).findFirst().orElseThrow();
|
||||
var second = candidates.stream().filter(row -> "IN-2".equals(row.inboundCode())).findFirst().orElseThrow();
|
||||
assertThat(first.returnSources()).singleElement().satisfies(source -> {
|
||||
assertThat(source.outboundPalletItemId()).isEqualTo(301L);
|
||||
assertThat(source.inventoryPositionId()).isEqualTo(601L);
|
||||
assertThat(source.maxReturnPackages()).isEqualTo(1L);
|
||||
});
|
||||
assertThat(second.returnSources()).singleElement().satisfies(source -> {
|
||||
assertThat(source.outboundPalletItemId()).isEqualTo(301L);
|
||||
assertThat(source.inventoryPositionId()).isEqualTo(602L);
|
||||
assertThat(source.maxReturnPackages()).isEqualTo(3L);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user