修复私人地址逐箱收货的目的地匹配

This commit is contained in:
2026-11-04 13:45:59 +08:00
parent a01598bd21
commit 22e8939f1f
2 changed files with 79 additions and 10 deletions
@@ -956,28 +956,35 @@ public class InboundPdaWorkflowService {
}
private boolean sameWarehouse(InboundPlanPallet left, InboundPlanPallet right) {
String leftKey = left.getDestinationWarehouseId() != null
? left.getDestinationWarehouseId() : left.getDestinationWarehouseCode();
String rightKey = right.getDestinationWarehouseId() != null
? right.getDestinationWarehouseId() : right.getDestinationWarehouseCode();
String leftKey = destinationKey(left.getDestinationWarehouseId(), left.getDestinationWarehouseCode(),
left.getDestinationWarehouseName());
String rightKey = destinationKey(right.getDestinationWarehouseId(), right.getDestinationWarehouseCode(),
right.getDestinationWarehouseName());
return leftKey != null && leftKey.equals(rightKey);
}
private boolean sameWarehouse(InboundPlanCarton carton, InboundPlanPallet pallet) {
String cartonKey = carton.getDestinationWarehouseId() != null
? carton.getDestinationWarehouseId() : carton.getDestinationWarehouseCode();
String palletKey = pallet.getDestinationWarehouseId() != null
? pallet.getDestinationWarehouseId() : pallet.getDestinationWarehouseCode();
String cartonKey = destinationKey(carton.getDestinationWarehouseId(), carton.getDestinationWarehouseCode(),
carton.getDestinationWarehouseName());
String palletKey = destinationKey(pallet.getDestinationWarehouseId(), pallet.getDestinationWarehouseCode(),
pallet.getDestinationWarehouseName());
return cartonKey != null && cartonKey.equals(palletKey);
}
private String destinationKey(InboundPlanPallet pallet) {
String value = pallet.getDestinationWarehouseId() != null
? pallet.getDestinationWarehouseId() : pallet.getDestinationWarehouseCode();
String value = destinationKey(pallet.getDestinationWarehouseId(), pallet.getDestinationWarehouseCode(),
pallet.getDestinationWarehouseName());
if (value == null || value.isBlank()) throw conflict("Planned pallet destination warehouse is empty");
return value;
}
private String destinationKey(String warehouseId, String warehouseCode, String privateAddressName) {
if (warehouseId != null) return warehouseId;
if (warehouseCode != null) return warehouseCode;
if (privateAddressName == null || privateAddressName.isBlank()) return null;
return "PRIVATE:" + privateAddressName.trim();
}
private void validateDestinationWarehouse(InboundPlanPallet pallet, long warehouseId) {
String destinationId = pallet.getDestinationWarehouseId();
if (destinationId == null || destinationId.isBlank()) return;
@@ -308,6 +308,68 @@ class InboundPdaWorkflowServiceTest {
verify(warehouse, never()).reserve(any(), anyLong(), anyLong(), anyLong(), any(), any(), any());
}
@Test
void privateAddressCartonCanValidateAndScanBeforeItsPlanBindingExists() {
InboundPlanCarton carton = carton("BOX-PRIVATE", "UNASSIGNED", null, null);
carton.setPlanId(null);
carton.setDestinationWarehouseName("私人地址 A");
targetPallet.setDestinationWarehouseId(null);
targetPallet.setDestinationWarehouseName("私人地址 A");
activeTask();
when(repository.lockCarton("100", "BOX-PRIVATE")).thenReturn(carton);
when(repository.lineForCarton(plan.getId(), carton)).thenReturn(targetLine);
when(repository.lockPallet(plan.getId(), targetPallet.getId())).thenReturn(targetPallet);
when(repository.lockAllocation(plan.getId(), targetLine.getId(), targetPallet.getId())).thenReturn(target);
service.validateCartonResolved("100", plan, targetLine, targetPallet, carton.getCartonMark());
var scanned = service.scanCarton("100", "LIP1",
new PdaRequests.ScanCarton(carton.getCartonMark(), targetPallet.getId(), "private-scan", "7", "worker"));
assertEquals("SCANNED", scanned.result());
assertEquals(plan.getId(), carton.getPlanId());
assertEquals(targetLine.getId(), carton.getLineId());
assertEquals(1, target.getPickedPackageCount());
verify(repository).update(carton);
}
@Test
void privateAddressCartonRejectsDifferentAndEmptyDestinationNames() {
InboundPlanCarton carton = carton("BOX-PRIVATE", "UNASSIGNED", targetLine.getId(), null);
targetPallet.setDestinationWarehouseId(null);
targetPallet.setDestinationWarehouseName("私人地址 A");
activeTask();
when(repository.lockCarton("100", carton.getCartonMark())).thenReturn(carton);
when(repository.line(plan.getId(), targetLine.getId())).thenReturn(targetLine);
for (String name : List.of("私人地址 B", " ")) {
carton.setDestinationWarehouseName(name);
ResponseStatusException failure = assertThrows(ResponseStatusException.class,
() -> service.validateCartonResolved("100", plan, targetLine, targetPallet, carton.getCartonMark()));
assertEquals("Carton and pallet destination warehouses must match", failure.getReason());
}
verify(repository, never()).update(carton);
}
@Test
void privateAddressPalletCanReserveResourcesForItsNamedDestination() {
targetPallet.setDestinationWarehouseId(null);
targetPallet.setDestinationWarehouseName("私人地址 A");
targetPallet.setWarehouseResourceLockId(null);
WarehouseResourceGateway.WarehousePalletReference resource =
new WarehouseResourceGateway.WarehousePalletReference(188L, "A002", 88L, 288L,
"IDLE", "", 1);
when(warehouse.reserve("100", 88L, 188L, 288L, "INBOUND_CONTAINER_DESTINATION_PALLET",
"CONT-1:PRIVATE:私人地址 A", "INBOUND_CONTAINER_DESTINATION_PALLET:CONT-1:PRIVATE:私人地址 A:188"))
.thenReturn(999L);
when(repository.pallets(plan.getId())).thenReturn(List.of(targetPallet));
service.assignResolvedPallet("100", plan, targetPallet, resource);
assertEquals("ASSIGNED", targetPallet.getAssignmentStatus());
assertEquals(999L, targetPallet.getWarehouseResourceLockId());
assertEquals("A002", targetPallet.getPalletNo());
}
@Test
void cartonScanBindsOnlyExpectedCartonAndRemoveMakesItScannableAgain() {
InboundPlanCarton carton = carton("BOX-001", "UNASSIGNED", targetLine.getId(), "WH-A");