This commit is contained in:
2026-08-27 14:30:33 +08:00
parent 0a3ec0b8a6
commit ed3c1dafcc
2 changed files with 103 additions and 20 deletions
@@ -50,6 +50,10 @@ class InitialWarehouseResourceSeedSqlContractTest {
.contains("(" + idPrefix + "204,0," + dongguanId + ",'A','A区','A0'") .contains("(" + idPrefix + "204,0," + dongguanId + ",'A','A区','A0'")
.contains("shared_overflow") .contains("shared_overflow")
.contains("'AVAILABLE'") .contains("'AVAILABLE'")
.doesNotContain("status='AVAILABLE',\n remark=VALUES(remark)")
.doesNotContain("status = 'AVAILABLE',\n remark = VALUES(remark)")
.doesNotContain("remark=VALUES(remark),\n deleted=0")
.doesNotContain("remark = VALUES(remark),\n deleted = 0")
.doesNotContain("INSERT INTO wh_location_reservation") .doesNotContain("INSERT INTO wh_location_reservation")
.doesNotContain("INSERT INTO wh_location_occupancy"); .doesNotContain("INSERT INTO wh_location_occupancy");
} }
@@ -257,9 +257,7 @@ class LocationResourceServiceTest {
item.setBusinessId(901L); item.setBusinessId(901L);
}); });
when(repository.lockOccupiedByBusiness(0L, "firstleg-service", 901L)).thenReturn(active); when(repository.lockOccupiedByBusiness(0L, "firstleg-service", 901L)).thenReturn(active);
when(repository.findOwned(0L, 88L, 10L)).thenReturn(occupiedLocation(10L, "A2")); stubSuccessfulOccupiedRelease(active.get(1), occupiedLocation(10L, "A2"), "PDA partial pick", 7L);
when(repository.releaseOccupancy(active.get(1), "PDA partial pick")).thenReturn(1);
when(repository.changeStatus(0L, 88L, 10L, "OCCUPIED", "AVAILABLE", 0, 7L)).thenReturn(1);
var result = service.releaseSelected(new SelectiveReleaseCommand(0L, "firstleg-service", 901L, 7L, var result = service.releaseSelected(new SelectiveReleaseCommand(0L, "firstleg-service", 901L, 7L,
"PDA partial pick", 10, List.of(10L), null)); "PDA partial pick", 10, List.of(10L), null));
@@ -280,9 +278,7 @@ class LocationResourceServiceTest {
LocationOccupancyEntity second = occupancy(202L, 102L, 10L, "A2", 502L); LocationOccupancyEntity second = occupancy(202L, 102L, 10L, "A2", 502L);
second.setAllocatedPackageCount(7); second.setAllocatedPackageCount(7);
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(first, second)); when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(first, second));
when(repository.findOwned(10001L, 88L, 10L)).thenReturn(occupiedLocation(10L, "A2")); stubSuccessfulOccupiedRelease(second, occupiedLocation(10L, "A2"), "PDA full pick", 7L);
when(repository.releaseOccupancy(second, "PDA full pick")).thenReturn(1);
when(repository.changeStatus(10001L, 88L, 10L, "OCCUPIED", "AVAILABLE", 0, 7L)).thenReturn(1);
var result = service.releaseSelectedExactly(new com.baiyue.warehouse.dto.LocationDtos.ExactSelectiveReleaseCommand( var result = service.releaseSelectedExactly(new com.baiyue.warehouse.dto.LocationDtos.ExactSelectiveReleaseCommand(
10001L, "FIRSTLEG", 301L, 7L, "PDA full pick", List.of(10L))); 10001L, "FIRSTLEG", 301L, 7L, "PDA full pick", List.of(10L)));
@@ -299,9 +295,7 @@ class LocationResourceServiceTest {
LocationOccupancyEntity occupancy = occupancy(201L, 101L, 9L, "A1", 501L); LocationOccupancyEntity occupancy = occupancy(201L, 101L, 9L, "A1", 501L);
occupancy.setAllocatedPackageCount(0); occupancy.setAllocatedPackageCount(0);
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(occupancy)); when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(occupancy));
when(repository.findOwned(10001L, 88L, 9L)).thenReturn(occupiedLocation(9L, "A1")); stubSuccessfulOccupiedRelease(occupancy, occupiedLocation(9L, "A1"), "PDA full pick", 7L);
when(repository.releaseOccupancy(occupancy, "PDA full pick")).thenReturn(1);
when(repository.changeStatus(10001L, 88L, 9L, "OCCUPIED", "AVAILABLE", 0, 7L)).thenReturn(1);
var result = service.releaseSelectedExactly(new com.baiyue.warehouse.dto.LocationDtos.ExactSelectiveReleaseCommand( var result = service.releaseSelectedExactly(new com.baiyue.warehouse.dto.LocationDtos.ExactSelectiveReleaseCommand(
10001L, "FIRSTLEG", 301L, 7L, "PDA full pick", List.of(9L))); 10001L, "FIRSTLEG", 301L, 7L, "PDA full pick", List.of(9L)));
@@ -323,11 +317,8 @@ class LocationResourceServiceTest {
}); });
when(repository.lockOccupiedByBusiness(0L, "firstleg-service", 901L)).thenReturn(active); when(repository.lockOccupiedByBusiness(0L, "firstleg-service", 901L)).thenReturn(active);
for (LocationOccupancyEntity item : List.of(allocationOne, allocationTwo)) { for (LocationOccupancyEntity item : List.of(allocationOne, allocationTwo)) {
when(repository.findOwned(0L, 88L, item.getLocationId())) stubSuccessfulOccupiedRelease(item, occupiedLocation(item.getLocationId(), item.getLocationCode()),
.thenReturn(occupiedLocation(item.getLocationId(), item.getLocationCode())); "PDA default pick", 7L);
when(repository.releaseOccupancy(item, "PDA default pick")).thenReturn(1);
when(repository.changeStatus(0L, 88L, item.getLocationId(), "OCCUPIED", "AVAILABLE", 0, 7L))
.thenReturn(1);
} }
var result = service.releaseSelected(new SelectiveReleaseCommand(0L, "firstleg-service", 901L, 7L, var result = service.releaseSelected(new SelectiveReleaseCommand(0L, "firstleg-service", 901L, 7L,
@@ -348,11 +339,8 @@ class LocationResourceServiceTest {
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(active); when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(active);
for (int index = 0; index < 2; index++) { for (int index = 0; index < 2; index++) {
LocationOccupancyEntity item = active.get(index); LocationOccupancyEntity item = active.get(index);
when(repository.findOwned(10001L, 88L, item.getLocationId())) stubSuccessfulOccupiedRelease(item, occupiedLocation(item.getLocationId(), item.getLocationCode()),
.thenReturn(occupiedLocation(item.getLocationId(), item.getLocationCode())); "PDA default pick", 7L);
when(repository.releaseOccupancy(item, "PDA default pick")).thenReturn(1);
when(repository.changeStatus(10001L, 88L, item.getLocationId(), "OCCUPIED", "AVAILABLE", 0, 7L))
.thenReturn(1);
} }
var result = service.releaseSelected(new SelectiveReleaseCommand(10001L, "FIRSTLEG", 301L, 7L, var result = service.releaseSelected(new SelectiveReleaseCommand(10001L, "FIRSTLEG", 301L, 7L,
@@ -379,7 +367,8 @@ class LocationResourceServiceTest {
void shouldFailOnConcurrentOccupancyUpdate() { void shouldFailOnConcurrentOccupancyUpdate() {
LocationOccupancyEntity active = occupancy(201L, 101L, 9L, "A1", 501L); LocationOccupancyEntity active = occupancy(201L, 101L, 9L, "A1", 501L);
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(active)); when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(active));
when(repository.findOwned(10001L, 88L, 9L)).thenReturn(occupiedLocation(9L, "A1")); when(repository.lockOwned(10001L, 88L, 9L)).thenReturn(occupiedLocation(9L, "A1"));
when(repository.lockReservation(10001L, 101L)).thenReturn(confirmedReservationFor(active));
when(repository.releaseOccupancy(active, "PDA pick")).thenReturn(0); when(repository.releaseOccupancy(active, "PDA pick")).thenReturn(0);
assertThatThrownBy(() -> service.releaseSelected(new SelectiveReleaseCommand( assertThatThrownBy(() -> service.releaseSelected(new SelectiveReleaseCommand(
@@ -389,6 +378,64 @@ class LocationResourceServiceTest {
any(String.class), any(String.class), anyInt(), anyLong()); any(String.class), any(String.class), anyInt(), anyLong());
} }
@Test
void shouldConvergeDriftedAvailableLocationWhenLastOccupancyIsReleasedByReservation() {
LocationOccupancyEntity active = occupancy(201L, 101L, 9L, "A1", 501L);
active.setAllocatedPackageCount(6);
LocationEntity drifted = location(9L, "A1");
drifted.setVersion(2);
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(active));
stubSuccessfulOccupiedRelease(active, drifted, "PDA complete", 7L);
var result = service.releaseSelectedReservations(new com.baiyue.warehouse.dto.LocationDtos.ReservationReleaseCommand(
10001L, "FIRSTLEG", 301L, 7L, "PDA complete", List.of(101L)));
assertThat(result).singleElement().satisfies(item -> {
assertThat(item.reservationId()).isEqualTo(101L);
assertThat(item.locationId()).isEqualTo(9L);
});
verify(repository).releaseConfirmedReservation(any(LocationReservationEntity.class), org.mockito.Mockito.eq("PDA complete"));
verify(repository, never()).changeStatus(anyLong(), anyLong(), anyLong(),
any(String.class), any(String.class), anyInt(), anyLong());
}
@Test
void shouldRejectAvailableNonSharedLocationWhenAnotherOccupancyRemains() {
LocationOccupancyEntity active = occupancy(201L, 101L, 9L, "A1", 501L);
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(active));
when(repository.lockOwned(10001L, 88L, 9L)).thenReturn(location(9L, "A1"));
LocationReservationEntity reservation = confirmedReservationFor(active);
when(repository.lockReservation(10001L, 101L)).thenReturn(reservation);
when(repository.releaseOccupancy(active, "PDA complete")).thenReturn(1);
when(repository.releaseConfirmedReservation(reservation, "PDA complete")).thenReturn(1);
when(repository.countActiveOccupancies(10001L, 9L)).thenReturn(1);
assertThatThrownBy(() -> service.releaseSelectedReservations(
new com.baiyue.warehouse.dto.LocationDtos.ReservationReleaseCommand(
10001L, "FIRSTLEG", 301L, 7L, "PDA complete", List.of(101L))))
.isInstanceOf(com.baiyue.common.core.exception.BizException.class);
verify(repository, never()).changeStatus(anyLong(), anyLong(), anyLong(),
any(String.class), any(String.class), anyInt(), anyLong());
}
@Test
void shouldReleaseOneSharedOverflowAllocationWithoutOpeningOrMergingTheOther() {
LocationOccupancyEntity active = occupancy(201L, 101L, 99L, "A0", 501L);
active.setSharedOverflow(true);
active.setAllocatedPackageCount(4);
LocationEntity shared = overflow();
when(repository.lockOccupiedByBusiness(10001L, "FIRSTLEG", 301L)).thenReturn(List.of(active));
stubSuccessfulOccupiedRelease(active, shared, "PDA complete", 7L);
assertThat(service.releaseSelectedReservations(
new com.baiyue.warehouse.dto.LocationDtos.ReservationReleaseCommand(
10001L, "FIRSTLEG", 301L, 7L, "PDA complete", List.of(101L))))
.singleElement().extracting(item -> item.reservationId()).isEqualTo(101L);
verify(repository, never()).countActiveOccupancies(anyLong(), anyLong());
verify(repository, never()).changeStatus(anyLong(), anyLong(), anyLong(),
any(String.class), any(String.class), anyInt(), anyLong());
}
@Test @Test
void shouldTransferConfirmedOccupiedResourcesToChildInbound() { void shouldTransferConfirmedOccupiedResourcesToChildInbound() {
LocationOccupancyEntity occupancy = occupancy(201L, 101L, 9L, "A1", 501L); LocationOccupancyEntity occupancy = occupancy(201L, 101L, 9L, "A1", 501L);
@@ -514,4 +561,36 @@ class LocationResourceServiceTest {
reservation.setVersion(0); reservation.setVersion(0);
return reservation; return reservation;
} }
private void stubSuccessfulOccupiedRelease(LocationOccupancyEntity occupancy, LocationEntity location,
String reason, long operatorId) {
location.setTenantId(occupancy.getTenantId());
LocationReservationEntity reservation = confirmedReservationFor(occupancy);
when(repository.lockOwned(occupancy.getTenantId(), occupancy.getWarehouseId(), occupancy.getLocationId()))
.thenReturn(location);
when(repository.lockReservation(occupancy.getTenantId(), occupancy.getReservationId())).thenReturn(reservation);
when(repository.releaseOccupancy(occupancy, reason)).thenReturn(1);
when(repository.releaseConfirmedReservation(reservation, reason)).thenReturn(1);
if (!Boolean.TRUE.equals(location.getSharedOverflow())) {
when(repository.countActiveOccupancies(occupancy.getTenantId(), occupancy.getLocationId())).thenReturn(0);
if ("OCCUPIED".equals(location.getStatus())) {
when(repository.changeStatus(occupancy.getTenantId(), occupancy.getWarehouseId(), occupancy.getLocationId(),
"OCCUPIED", "AVAILABLE", location.getVersion(), operatorId)).thenReturn(1);
}
}
}
private static LocationReservationEntity confirmedReservationFor(LocationOccupancyEntity occupancy) {
LocationReservationEntity reservation = reservation("CONFIRMED");
reservation.setId(occupancy.getReservationId());
reservation.setTenantId(occupancy.getTenantId());
reservation.setWarehouseId(occupancy.getWarehouseId());
reservation.setSourceService(occupancy.getSourceService());
reservation.setBusinessId(occupancy.getBusinessId());
reservation.setPalletId(occupancy.getPalletId());
reservation.setLocationId(occupancy.getLocationId());
reservation.setLocationCode(occupancy.getLocationCode());
reservation.setSharedOverflow(occupancy.getSharedOverflow());
return reservation;
}
} }