fix(overseas): preserve shared overflow locations during pallet moves

This commit is contained in:
2026-09-29 14:39:11 +08:00
parent 098cc785d8
commit cb8e5fb145
3 changed files with 39 additions and 12 deletions
@@ -157,7 +157,7 @@ public class WarehousePalletTransitionService {
}
private void requireAvailableTarget(WarehouseLocationEntity location, String targetStatus) {
String expected = isVirtual(location) ? "ACTIVE" : "IDLE";
String expected = isNonExclusive(location) ? "ACTIVE" : "IDLE";
if (!expected.equals(location.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "目标库位不可用");
}
@@ -168,18 +168,18 @@ public class WarehousePalletTransitionService {
private void transitionSameLocation(long warehouseId, WarehouseLocationEntity location,
String beforeStatus, String afterStatus) {
if (beforeStatus.equals(afterStatus) || isVirtual(location)) return;
if (beforeStatus.equals(afterStatus) || isNonExclusive(location)) return;
changeLocationStatus(warehouseId, location.getId(), expectedLocationStatus(location, beforeStatus),
expectedLocationStatus(location, afterStatus));
}
private void releaseSourceLocation(long warehouseId, WarehouseLocationEntity source, String palletStatus) {
if (isVirtual(source) || "IDLE".equals(palletStatus)) return;
if (isNonExclusive(source) || "IDLE".equals(palletStatus)) return;
changeLocationStatus(warehouseId, source.getId(), "OCCUPIED", "IDLE");
}
private void occupyTargetLocation(long warehouseId, WarehouseLocationEntity target, String palletStatus) {
if (isVirtual(target) || "IDLE".equals(palletStatus)) return;
if (isNonExclusive(target) || "IDLE".equals(palletStatus)) return;
changeLocationStatus(warehouseId, target.getId(), "IDLE", "OCCUPIED");
}
@@ -191,11 +191,11 @@ public class WarehousePalletTransitionService {
}
private static String expectedLocationStatus(WarehouseLocationEntity location, String palletStatus) {
return isVirtual(location) ? "ACTIVE" : palletStatus;
return isNonExclusive(location) ? "ACTIVE" : palletStatus;
}
private static boolean isVirtual(WarehouseLocationEntity location) {
return "VIRTUAL".equals(location.getLocationType());
private static boolean isNonExclusive(WarehouseLocationEntity location) {
return "VIRTUAL".equals(location.getLocationType()) || WarehouseResourceService.isSharedOverflow(location);
}
private PalletTransitionResponse replay(WarehousePalletOperationEntity operation, String hash) {
@@ -64,6 +64,27 @@ class WarehousePalletTransitionServiceTest {
verify(mapper).transitionLocation(88L, locationId, "OCCUPIED", "IDLE");
}
@Test void movingIntoAndOutOfSharedOverflowKeepsItActive() {
WarehouseLocationEntity overflow = location(20L, "SHARED_OVERFLOW", "ACTIVE");
overflow.setLocationCode("A0"); overflow.setSharedOverflow(true);
WarehousePalletEntity pallet = pallet("P-1", "OCCUPIED", 4);
when(mapper.lockPublicPallet(88L, 10L)).thenReturn(pallet);
when(mapper.lockPublicLocation(88L, 11L)).thenReturn(location(11L, "STORAGE", "OCCUPIED"));
when(mapper.lockPublicLocation(88L, 20L)).thenReturn(overflow);
when(mapper.transitionLocation(88L, 11L, "OCCUPIED", "IDLE")).thenReturn(1);
when(mapper.transition(88L, 10L, "OCCUPIED", 20L, 4)).thenReturn(1);
service.apply(10L, new PalletTransitionRequests.Apply(88L, 4, "OCCUPIED", 20L, "into-overflow", "move"));
pallet.setLocationId(20L); pallet.setVersion(5);
when(mapper.lockPublicLocation(88L, 11L)).thenReturn(location(11L, "STORAGE", "IDLE"));
when(mapper.transitionLocation(88L, 11L, "IDLE", "OCCUPIED")).thenReturn(1);
when(mapper.transition(88L, 10L, "OCCUPIED", 11L, 5)).thenReturn(1);
service.apply(10L, new PalletTransitionRequests.Apply(88L, 5, "OCCUPIED", 11L, "out-of-overflow", "restore"));
verify(mapper, never()).transitionLocation(eq(88L), eq(20L), anyString(), anyString());
assertEquals("ACTIVE", overflow.getStatus());
}
@Test void exactReplayDoesNotMutatePhysicalPalletAgain() {
WarehousePalletOperationEntity existing = operation();
when(mapper.findOperation("operation-1")).thenReturn(existing);