优化
This commit is contained in:
+12
-2
@@ -24,7 +24,8 @@ class InboundMeasurementEvidenceOpenApiContractTest {
|
||||
Map<String, Object> schemas = stringMap(stringMap(document.get("components")).get("schemas"));
|
||||
Map<String, Object> detailProperties = properties(schemas, "InboundMeasurementDetail");
|
||||
assertThat(detailProperties).containsKeys(
|
||||
"actualGrossWeightKg", "actualNetWeightKg", "latestDeclaration", "items", "weightResults");
|
||||
"actualGrossWeightKg", "actualNetWeightKg", "latestDeclaration", "items", "weightResults",
|
||||
"palletLocationGuide");
|
||||
assertThat(stringMap(detailProperties.get("latestDeclaration")))
|
||||
.containsEntry("$ref", "#/components/schemas/InboundMeasurementLatestDeclaration");
|
||||
assertThat(properties(schemas, "InboundMeasurementLatestDeclaration"))
|
||||
@@ -52,7 +53,16 @@ class InboundMeasurementEvidenceOpenApiContractTest {
|
||||
assertThat(itemProperties).containsKeys("id", "intelligentImageFileId");
|
||||
Map<String, Object> weightProperties = properties(schemas, "InboundWeightResult");
|
||||
assertThat(weightProperties).containsKeys(
|
||||
"id", "fileId", "recognitionStatus", "grossWeightKg", "netWeightKg", "confirmedAt");
|
||||
"id", "fileId", "recognitionStatus", "grossWeightKg", "netWeightKg", "confirmedAt",
|
||||
"palletSequence", "blockId", "slotNo", "reservationId", "locationId", "locationCode",
|
||||
"sharedOverflow", "slotStatus");
|
||||
assertThat(stringMap(weightProperties.get("slotStatus"))).containsEntry(
|
||||
"enum", java.util.List.of("RESERVED_EMPTY", "BOUND", "TRANSFERRED", "RELEASED"));
|
||||
assertThat(properties(schemas, "InboundPalletLocationGuide")).containsKeys(
|
||||
"blockId", "reservedSlotCount", "actualPalletCount", "reservedEmptySlotCount", "slots");
|
||||
assertThat(properties(schemas, "InboundPalletLocationSlot")).containsKeys(
|
||||
"slotNo", "reservationId", "locationId", "locationCode", "sharedOverflow", "slotStatus",
|
||||
"boundWeightResultId", "palletSequence", "palletId", "palletCode");
|
||||
assertThat(stringMap(detailProperties.get("actualGrossWeightKg")).get("description").toString())
|
||||
.contains("CONFIRMED", "grossWeightKg", "含托盘毛重");
|
||||
assertThat(stringMap(detailProperties.get("actualNetWeightKg")).get("description").toString())
|
||||
|
||||
+5
@@ -49,6 +49,9 @@ class PdaConvergenceOpenApiContractTest {
|
||||
String batchRequest = yaml.substring(
|
||||
yaml.indexOf(" BatchRequest:"),
|
||||
yaml.indexOf(" BatchItemRequest:"));
|
||||
String batchItemRequest = yaml.substring(
|
||||
yaml.indexOf(" BatchItemRequest:"),
|
||||
yaml.indexOf(" ReviewRequest:"));
|
||||
assertThat(taskLookup)
|
||||
.contains("name: cabinetNo", "name: loadingPlanId")
|
||||
.doesNotContain("name: containerNo");
|
||||
@@ -59,5 +62,7 @@ class PdaConvergenceOpenApiContractTest {
|
||||
assertThat(batchRequest)
|
||||
.contains("loadingPlanId:", "containerBookingId:")
|
||||
.doesNotContain("containerNo:");
|
||||
assertThat(batchItemRequest)
|
||||
.doesNotContain("releaseMode:", "reservationIds:", "locationIds:");
|
||||
}
|
||||
}
|
||||
|
||||
+71
-51
@@ -1,84 +1,104 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baiyue.firstleg.entity.InboundPalletAllocationEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundPalletAllocationMapper;
|
||||
import com.baiyue.firstleg.client.WarehousePalletLocationClient;
|
||||
import com.baiyue.firstleg.entity.InboundPalletAllocationEntity;
|
||||
import com.baiyue.firstleg.entity.InboundWeightResultEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundPalletAllocationMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
import java.math.BigDecimal;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InboundPalletAllocationRepositoryTest {
|
||||
@Mock
|
||||
private InboundPalletAllocationMapper mapper;
|
||||
@InjectMocks
|
||||
private InboundPalletAllocationRepository repository;
|
||||
@Mock private InboundPalletAllocationMapper mapper;
|
||||
@InjectMocks private InboundPalletAllocationRepository repository;
|
||||
|
||||
@Test
|
||||
void acceptsReleasedSnapshotWhenRetryUpdateAffectsNoRows() {
|
||||
InboundPalletAllocationEntity current = allocation(1L, 91L, "RELEASED", 10, 10);
|
||||
when(mapper.markReleased(10001L, 31L, 91L)).thenReturn(0);
|
||||
when(mapper.selectOne(any())).thenReturn(current);
|
||||
void savesEveryBlockSlotIncludingReservedEmptySlots() {
|
||||
var result = new WarehousePalletLocationClient.BlockResult(700L, "OCCUPIED", List.of(
|
||||
resource(700L, 91L, 11L, "A01", 1, false),
|
||||
resource(700L, 92L, 12L, "A02", 2, false),
|
||||
resource(700L, 93L, 99L, "A0", 3, true)));
|
||||
|
||||
assertThatCode(() -> repository.markReleased(10001L, 31L, List.of(91L)))
|
||||
.doesNotThrowAnyException();
|
||||
repository.replaceBlock(10001L, 31L, 88L, 20, new BigDecimal("2.5000"), result);
|
||||
|
||||
ArgumentCaptor<InboundPalletAllocationEntity> inserted =
|
||||
ArgumentCaptor.forClass(InboundPalletAllocationEntity.class);
|
||||
verify(mapper, times(3)).insert(inserted.capture());
|
||||
assertThat(inserted.getAllValues()).extracting(InboundPalletAllocationEntity::getSlotNo)
|
||||
.containsExactly(1, 2, 3);
|
||||
assertThat(inserted.getAllValues()).extracting(InboundPalletAllocationEntity::getWeightResultId)
|
||||
.containsOnlyNulls();
|
||||
assertThat(inserted.getAllValues()).extracting(InboundPalletAllocationEntity::getSlotStatus)
|
||||
.containsOnly("RESERVED_EMPTY");
|
||||
assertThat(inserted.getAllValues().get(2).getSharedOverflow()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsTransferredSnapshotWhenRetryUpdateAffectsNoRows() {
|
||||
InboundPalletAllocationEntity source = allocation(1L, 91L, "CONFIRMED", 10, 2);
|
||||
InboundPalletAllocationEntity current = allocation(1L, 91L, "TRANSFERRED", 10, 2);
|
||||
when(mapper.markTransferred(1L, 10001L, 31L)).thenReturn(0);
|
||||
when(mapper.selectOne(any())).thenReturn(current);
|
||||
void bindsConfirmedWeightsByConfirmationTimeToTheNextEmptySlot() {
|
||||
InboundPalletAllocationEntity firstEmpty = allocation(1L, 1);
|
||||
InboundPalletAllocationEntity secondEmpty = allocation(2L, 2);
|
||||
when(mapper.selectNextEmptyForUpdate(10001L, 31L)).thenReturn(firstEmpty, secondEmpty);
|
||||
when(mapper.bindWeight(any(Long.class), any(Long.class), any(Long.class), any(Long.class),
|
||||
any(Integer.class), any(Long.class), any(String.class), any(BigDecimal.class))).thenReturn(1);
|
||||
InboundWeightResultEntity later = weight(102L, LocalDateTime.of(2026, 9, 4, 10, 2));
|
||||
InboundWeightResultEntity earlier = weight(101L, LocalDateTime.of(2026, 9, 4, 10, 1));
|
||||
|
||||
assertThatCode(() -> repository.markTransferred(10001L, 31L, List.of(source), List.of()))
|
||||
.doesNotThrowAnyException();
|
||||
repository.bindConfirmedWeights(10001L, 31L, List.of(later, earlier));
|
||||
|
||||
org.mockito.InOrder order = org.mockito.Mockito.inOrder(mapper);
|
||||
order.verify(mapper).bindWeight(1L, 10001L, 31L, 101L, 1, 501L, "P-501",
|
||||
new BigDecimal("1.2000"));
|
||||
order.verify(mapper).bindWeight(2L, 10001L, 31L, 102L, 2, 501L, "P-501",
|
||||
new BigDecimal("1.2000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void restoresReservationNumbersByReservationIdWhenPalletIsReused() {
|
||||
InboundPalletAllocationEntity first = allocation(1L, 91L, "RESERVED", 1, 0);
|
||||
first.setPalletId(501L);
|
||||
first.setReservationNo("LR-91");
|
||||
InboundPalletAllocationEntity second = allocation(2L, 92L, "RESERVED", 1, 0);
|
||||
second.setPalletId(501L);
|
||||
second.setReservationNo("LR-92");
|
||||
when(mapper.selectList(any())).thenReturn(List.of(first, second));
|
||||
void unbindRestoresOnlyTheBoundSlotToReservedEmpty() {
|
||||
InboundPalletAllocationEntity bound = allocation(1L, 1);
|
||||
bound.setWeightResultId(101L);
|
||||
when(mapper.selectOne(any())).thenReturn(bound);
|
||||
when(mapper.unbindWeight(1L, 10001L, 31L, 101L)).thenReturn(1);
|
||||
|
||||
var result = new WarehousePalletLocationClient.BatchResult(2, List.of(
|
||||
new WarehousePalletLocationClient.AllocationResult(91L, null, 1, 501L, "P1", new BigDecimal("1.2"), 11L, "A1",
|
||||
new BigDecimal("1.2"), "OCCUPIED"),
|
||||
new WarehousePalletLocationClient.AllocationResult(92L, null, 2, 501L, "P1", new BigDecimal("1.2"), 12L, "A2",
|
||||
new BigDecimal("1.2"), "OCCUPIED")));
|
||||
repository.unbindWeight(10001L, 31L, 101L);
|
||||
|
||||
repository.replace(10001L, 31L, 88L, 0L, 2, result);
|
||||
|
||||
ArgumentCaptor<InboundPalletAllocationEntity> inserted = ArgumentCaptor.forClass(InboundPalletAllocationEntity.class);
|
||||
verify(mapper, org.mockito.Mockito.times(2)).insert(inserted.capture());
|
||||
assertThat(inserted.getAllValues()).extracting(InboundPalletAllocationEntity::getReservationNo)
|
||||
.containsExactly("LR-91", "LR-92");
|
||||
verify(mapper).unbindWeight(1L, 10001L, 31L, 101L);
|
||||
}
|
||||
|
||||
private static InboundPalletAllocationEntity allocation(long id, long reservationId, String status,
|
||||
int allocated, int picked) {
|
||||
InboundPalletAllocationEntity entity = new InboundPalletAllocationEntity();
|
||||
entity.setId(id);
|
||||
entity.setReservationId(reservationId);
|
||||
entity.setAllocationStatus(status);
|
||||
entity.setAllocatedPackageCount(allocated);
|
||||
entity.setPickedPackageCount(picked);
|
||||
return entity;
|
||||
private static WarehousePalletLocationClient.BlockResource resource(
|
||||
long blockId, long reservationId, long locationId, String code, int slotNo, boolean overflow) {
|
||||
return new WarehousePalletLocationClient.BlockResource(
|
||||
blockId, reservationId, locationId, code, slotNo, overflow, "OCCUPIED");
|
||||
}
|
||||
|
||||
private static InboundPalletAllocationEntity allocation(long id, int slotNo) {
|
||||
InboundPalletAllocationEntity value = new InboundPalletAllocationEntity();
|
||||
value.setId(id);
|
||||
value.setSlotNo(slotNo);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static InboundWeightResultEntity weight(long id, LocalDateTime confirmedAt) {
|
||||
InboundWeightResultEntity value = new InboundWeightResultEntity();
|
||||
value.setId(id);
|
||||
value.setRecognitionStatus("CONFIRMED");
|
||||
value.setConfirmedAt(confirmedAt);
|
||||
value.setPalletId(501L);
|
||||
value.setPalletCode("P-501");
|
||||
value.setPalletCapacityCbm(new BigDecimal("1.2000"));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ import com.baiyue.firstleg.dto.InboundMeasurementResponses;
|
||||
import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
||||
import com.baiyue.firstleg.entity.InboundMeasurementItemEntity;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.InboundPalletAllocationEntity;
|
||||
import com.baiyue.firstleg.entity.InboundWeightResultEntity;
|
||||
import com.baiyue.firstleg.entity.InboundWeightRevisionEntity;
|
||||
import com.baiyue.firstleg.repository.InboundMeasurementRepository;
|
||||
import com.baiyue.firstleg.repository.InboundPalletAllocationRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@@ -58,6 +60,7 @@ class InboundMeasurementServiceTest {
|
||||
@MockBean private OrderCargoClient orderCargoClient;
|
||||
@MockBean private MeasurementImageClient imageClient;
|
||||
@MockBean private WarehousePalletLocationClient warehousePallets;
|
||||
@MockBean private InboundPalletAllocationRepository palletAllocations;
|
||||
@Resource private InboundMeasurementService service;
|
||||
|
||||
@BeforeEach
|
||||
@@ -205,6 +208,10 @@ class InboundMeasurementServiceTest {
|
||||
when(repository.findLatestMeasurement(1L, 10L)).thenReturn(measurement);
|
||||
when(repository.listItems(1L, 30L)).thenReturn(List.of(item));
|
||||
when(repository.listWeights(1L, 10L)).thenReturn(List.of(weight));
|
||||
InboundPalletAllocationEntity bound = locationSlot(1, 801L, "A01", "BOUND", 50L);
|
||||
InboundPalletAllocationEntity secondBound = locationSlot(2, 802L, "A02", "BOUND", 51L);
|
||||
InboundPalletAllocationEntity empty = locationSlot(3, 803L, "A03", "RESERVED_EMPTY", null);
|
||||
when(palletAllocations.list(1L, 10L)).thenReturn(List.of(empty, secondBound, bound));
|
||||
|
||||
InboundMeasurementResponses.Detail result = service.detail(1L, 10L);
|
||||
|
||||
@@ -224,6 +231,16 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(confirmedWeight.grossWeightKg()).isEqualByComparingTo("30.000");
|
||||
assertThat(confirmedWeight.netWeightKg()).isEqualByComparingTo("11.500");
|
||||
assertThat(confirmedWeight.confirmedAt()).isNotNull();
|
||||
assertThat(confirmedWeight.locationCode()).isEqualTo("A01");
|
||||
});
|
||||
assertThat(result.palletLocationGuide()).satisfies(guide -> {
|
||||
assertThat(guide.blockId()).isEqualTo(700L);
|
||||
assertThat(guide.reservedSlotCount()).isEqualTo(3);
|
||||
assertThat(guide.actualPalletCount()).isEqualTo(2);
|
||||
assertThat(guide.reservedEmptySlotCount()).isEqualTo(1);
|
||||
assertThat(guide.slots()).extracting(InboundMeasurementResponses.PalletLocationSlot::locationCode)
|
||||
.containsExactly("A01", "A02", "A03");
|
||||
assertThat(guide.slots().get(2).boundWeightResultId()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -537,6 +554,23 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(revision.getValue().getNetWeightKg()).isEqualByComparingTo("11.500");
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmingAWeightBindsItToTheNextEmptySlotWhenTheTicketAlreadyHasABlock() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_REJECTED");
|
||||
InboundWeightResultEntity weight = weight(20L, "30.000", "KG", "PENDING_CONFIRM", 0);
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findWeight(1L, 10L, 20L)).thenReturn(weight);
|
||||
when(repository.updateWeight(weight)).thenReturn(true);
|
||||
when(repository.listWeights(1L, 10L)).thenReturn(List.of(weight));
|
||||
when(palletAllocations.list(1L, 10L)).thenReturn(List.of(
|
||||
locationSlot(1, 801L, "A01", "RESERVED_EMPTY", null)));
|
||||
|
||||
service.confirmWeightResult(1L, 10L, 20L, 9L, "操作员",
|
||||
new InboundMeasurementRequests.ConfirmWeightResultRequest(0, null, null, null));
|
||||
|
||||
verify(palletAllocations).bindNextEmpty(1L, 10L, weight);
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmingFifthPalletSeparatelyAggregatesGrossAndCargoNetWeight() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
@@ -834,6 +868,26 @@ class InboundMeasurementServiceTest {
|
||||
verify(repository).updateInboundWeightSummary(inbound);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingBoundWeightUnbindsTheSlotBeforeDeletingTheResultAndFailsOnFileError() {
|
||||
InboundOrderEntity inbound = inbound("MEASURING");
|
||||
InboundWeightResultEntity weight = weight(20L, "20.000", "KG", "CONFIRMED", 1);
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findWeight(1L, 10L, 20L)).thenReturn(weight);
|
||||
when(repository.deleteWeight(1L, 10L, 20L, 1)).thenReturn(true);
|
||||
when(imageClient.delete("internal-token", 9L, 1L, 10L, 120L))
|
||||
.thenReturn(ApiResponse.failure(com.baiyue.common.core.api.ResultCode.SERVICE_UNAVAILABLE,
|
||||
"file unavailable", null, null));
|
||||
|
||||
assertThatThrownBy(() -> service.deleteWeightResult(1L, 10L, 20L, 1, 9L))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("file unavailable");
|
||||
|
||||
org.mockito.InOrder order = org.mockito.Mockito.inOrder(palletAllocations, repository, imageClient);
|
||||
order.verify(palletAllocations).unbindWeight(1L, 10L, 20L);
|
||||
order.verify(repository).deleteWeight(1L, 10L, 20L, 1);
|
||||
order.verify(imageClient).delete("internal-token", 9L, 1L, 10L, 120L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingLastConfirmedWeightClearsInboundTotal() {
|
||||
InboundOrderEntity inbound = inbound("MEASURING");
|
||||
@@ -1051,6 +1105,21 @@ class InboundMeasurementServiceTest {
|
||||
verify(batchService, never()).saveBatch(any());
|
||||
}
|
||||
|
||||
private static InboundPalletAllocationEntity locationSlot(
|
||||
int slotNo, long locationId, String locationCode, String slotStatus, Long weightResultId) {
|
||||
InboundPalletAllocationEntity value = new InboundPalletAllocationEntity();
|
||||
value.setBlockId(700L);
|
||||
value.setSlotNo(slotNo);
|
||||
value.setReservationId(900L + slotNo);
|
||||
value.setLocationId(locationId);
|
||||
value.setLocationCode(locationCode);
|
||||
value.setSharedOverflow(false);
|
||||
value.setSlotStatus(slotStatus);
|
||||
value.setWeightResultId(weightResultId);
|
||||
value.setPalletSequence(weightResultId == null ? null : slotNo);
|
||||
return value;
|
||||
}
|
||||
|
||||
private InboundOrderEntity inbound(String status) {
|
||||
InboundOrderEntity inbound = new InboundOrderEntity();
|
||||
inbound.setId(10L);
|
||||
|
||||
@@ -342,7 +342,7 @@ class LoadingPickServiceTest {
|
||||
preparePick(task, item, "pick-1");
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(false);
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "pick-1"), 9L, "operator");
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, 0, "pick-1"), 9L, "operator");
|
||||
|
||||
verify(repository).touchTask(20L, 0);
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
@@ -358,7 +358,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "pick-2"), 9L, "operator");
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, 0, "pick-2"), 9L, "operator");
|
||||
|
||||
verify(repository).touchTask(20L, 0);
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
@@ -374,7 +374,7 @@ class LoadingPickServiceTest {
|
||||
preparePick(task, item, "pick-start-loading");
|
||||
when(smartLoadingRepository.startPicking(10L, 9L, "operator")).thenReturn(1);
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "pick-start-loading"),
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, 0, "pick-start-loading"),
|
||||
9L, "operator");
|
||||
|
||||
verify(smartLoadingRepository).startPicking(10L, 9L, "operator");
|
||||
@@ -393,7 +393,7 @@ class LoadingPickServiceTest {
|
||||
new LoadingInboundMapper.Snapshot(50L, 101L, "WAIT_SPLIT", 4));
|
||||
when(repository.beginInboundLoading(101L, 50L, 9L, 4)).thenReturn(1);
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "pick-start-inbound"),
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, 0, "pick-start-inbound"),
|
||||
9L, "operator");
|
||||
|
||||
verify(repository).beginInboundLoading(101L, 50L, 9L, 4);
|
||||
@@ -419,7 +419,7 @@ class LoadingPickServiceTest {
|
||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 5));
|
||||
when(repository.beginInboundLoading(101L, 50L, 9L, 4)).thenReturn(0);
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "pick-concurrent-inbound"),
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, 0, "pick-concurrent-inbound"),
|
||||
9L, "operator");
|
||||
|
||||
verify(repository).beginInboundLoading(101L, 50L, 9L, 4);
|
||||
@@ -518,7 +518,8 @@ class LoadingPickServiceTest {
|
||||
void claimRechecksSameOperationAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickOperationEntity completed = taskOperation("claim-race", "CLAIM");
|
||||
when(repository.operation("claim-race")).thenReturn(null, completed);
|
||||
when(repository.operation("claim-race")).thenReturn(null);
|
||||
when(repository.operationForUpdate("claim-race")).thenReturn(completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.items(20L)).thenReturn(List.of());
|
||||
@@ -535,7 +536,8 @@ class LoadingPickServiceTest {
|
||||
void assignRechecksSameOperationAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickOperationEntity completed = taskOperation("assign-race", "ASSIGN");
|
||||
when(repository.operation("assign-race")).thenReturn(null, completed);
|
||||
when(repository.operation("assign-race")).thenReturn(null);
|
||||
when(repository.operationForUpdate("assign-race")).thenReturn(completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.items(20L)).thenReturn(List.of());
|
||||
@@ -554,17 +556,18 @@ class LoadingPickServiceTest {
|
||||
void pickRechecksSameHistoryAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickHistoryEntity completed = itemHistory("pick-race", "PICK",
|
||||
"20|30|101|2|MANUAL|[]");
|
||||
when(repository.history("pick-race")).thenReturn(null, completed);
|
||||
"20|30|101|2");
|
||||
when(repository.history("pick-race")).thenReturn(null);
|
||||
when(repository.historyForUpdate("pick-race")).thenReturn(completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
|
||||
Task replay = service.pick(20L, 30L,
|
||||
new PickRequest(101L, 2, List.of(), 0, "pick-race"), 9L, "operator");
|
||||
new PickRequest(101L, 2, 0, "pick-race"), 9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).pick(anyLong(), anyInt(), anyString(), anyString(), anyInt(), anyLong());
|
||||
verify(repository, never()).pick(anyLong(), anyInt(), anyInt(), anyLong());
|
||||
verify(repository, never()).insert(any(LoadingPickHistoryEntity.class));
|
||||
verify(repository, never()).touchTask(anyLong(), anyInt());
|
||||
}
|
||||
@@ -573,17 +576,18 @@ class LoadingPickServiceTest {
|
||||
void partialRechecksSameHistoryAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickHistoryEntity completed = itemHistory("partial-race", "PARTIAL",
|
||||
"PARTIAL|20|30|101|1|MANUAL|[]");
|
||||
when(repository.history("partial-race")).thenReturn(null, completed);
|
||||
"PARTIAL|20|30|101|1");
|
||||
when(repository.history("partial-race")).thenReturn(null);
|
||||
when(repository.historyForUpdate("partial-race")).thenReturn(completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
|
||||
Task replay = service.partial(20L, 30L,
|
||||
new PartialPickRequest(101L, 1, List.of(), 0, "partial-race"), 9L, "operator");
|
||||
new PartialPickRequest(101L, 1, 0, "partial-race"), 9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).partial(anyLong(), anyInt(), anyString(), anyString(), anyInt(), anyLong());
|
||||
verify(repository, never()).partial(anyLong(), anyInt(), anyInt(), anyLong());
|
||||
verify(repository, never()).insert(any(LoadingPickHistoryEntity.class));
|
||||
verify(repository, never()).touchTask(anyLong(), anyInt());
|
||||
}
|
||||
@@ -593,7 +597,8 @@ class LoadingPickServiceTest {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickHistoryEntity completed = itemHistory("skip-race", "SKIP",
|
||||
"SKIP|20|30|101|damaged");
|
||||
when(repository.history("skip-race")).thenReturn(null, completed);
|
||||
when(repository.history("skip-race")).thenReturn(null);
|
||||
when(repository.historyForUpdate("skip-race")).thenReturn(completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
@@ -615,7 +620,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
|
||||
service.partial(20L, 30L, new PartialPickRequest(101L, 1, List.of(), 0, "partial-1"),
|
||||
service.partial(20L, 30L, new PartialPickRequest(101L, 1, 0, "partial-1"),
|
||||
9L, "operator");
|
||||
|
||||
verify(repository).touchTask(20L, 0);
|
||||
@@ -1113,7 +1118,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L)).thenReturn(items);
|
||||
when(repository.items(20L, 101L)).thenReturn(items);
|
||||
when(repository.pick(30L, 2, "MANUAL", "[]", 0, 101L)).thenReturn(1);
|
||||
when(repository.pick(30L, 2, 0, 101L)).thenReturn(1);
|
||||
when(repository.skip(31L, 0, 101L)).thenReturn(1);
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
@@ -1123,13 +1128,13 @@ class LoadingPickServiceTest {
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 2))));
|
||||
String snapshotToken = service.inventorySnapshot(20L, 101L).snapshotToken();
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, snapshotToken, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "MANUAL", List.of(), 0, null),
|
||||
new BatchItemRequest(31L, "SKIP", null, null, null, null, 0, "damaged carton")));
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null),
|
||||
new BatchItemRequest(31L, "SKIP", null, null, 0, "damaged carton")));
|
||||
|
||||
service.batch(20L, "batch-1", request, 9L, "operator");
|
||||
service.batch(20L, "batch-1", request, 9L, "operator");
|
||||
|
||||
verify(repository, times(1)).pick(30L, 2, "MANUAL", "[]", 0, 101L);
|
||||
verify(repository, times(1)).pick(30L, 2, 0, 101L);
|
||||
verify(repository, times(1)).skip(31L, 0, 101L);
|
||||
verify(repository, times(1)).touchTask(20L, 0);
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
@@ -1154,7 +1159,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L)).thenReturn(List.of(item));
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item));
|
||||
when(repository.pick(30L, 100, "AUTO", "[]", 0, 101L)).thenReturn(1);
|
||||
when(repository.pick(30L, 100, 0, 101L)).thenReturn(1);
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(smartLoadingRepository.startPicking(10L, 9L, "operator")).thenReturn(1);
|
||||
@@ -1164,11 +1169,11 @@ class LoadingPickServiceTest {
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 100))));
|
||||
String snapshotToken = service.inventorySnapshot(20L, 101L).snapshotToken();
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, snapshotToken, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 100, null, "AUTO", List.of(), 0, null)));
|
||||
new BatchItemRequest(30L, "PICK", 100, null, 0, null)));
|
||||
|
||||
service.batch(20L, "batch-reset-plan", request, 9L, "operator");
|
||||
|
||||
verify(repository).pick(30L, 100, "AUTO", "[]", 0, 101L);
|
||||
verify(repository).pick(30L, 100, 0, 101L);
|
||||
verify(smartLoadingRepository).startPicking(10L, 9L, "operator");
|
||||
verify(smartLoadingRepository, never()).insert(any(LoadingPlanHistoryEntity.class));
|
||||
verify(repository).touchTask(20L, 0);
|
||||
@@ -1190,14 +1195,14 @@ class LoadingPickServiceTest {
|
||||
when(repository.items(20L)).thenReturn(items);
|
||||
when(repository.items(20L, 101L)).thenReturn(items);
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, "stale-token", List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "MANUAL", List.of(), 0, null),
|
||||
new BatchItemRequest(31L, "PARTIAL", null, 4, "AUTO", null, 0, null)));
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null),
|
||||
new BatchItemRequest(31L, "PARTIAL", null, 4, 0, null)));
|
||||
|
||||
assertThatThrownBy(() -> service.batch(20L, "batch-invalid", request, 9L, "operator"))
|
||||
.isInstanceOf(BizException.class);
|
||||
|
||||
verify(repository, never()).pick(any(Long.class), any(Integer.class), any(), any(), any(Integer.class), any(Long.class));
|
||||
verify(repository, never()).partial(any(Long.class), any(Integer.class), any(), any(), any(Integer.class), any(Long.class));
|
||||
verify(repository, never()).pick(any(Long.class), any(Integer.class), any(Integer.class), any(Long.class));
|
||||
verify(repository, never()).partial(any(Long.class), any(Integer.class), any(Integer.class), any(Long.class));
|
||||
verify(repository, never()).touchTask(any(Long.class), any(Integer.class));
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
}
|
||||
@@ -1214,13 +1219,13 @@ class LoadingPickServiceTest {
|
||||
ApiResponse.success(List.of(new WarehousePalletLocationClient.OccupiedResource(
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 2))));
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, "0".repeat(64), List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "MANUAL", List.of(), 0, null)));
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null)));
|
||||
|
||||
assertThatThrownBy(() -> service.batch(20L, "batch-inventory-stale", request, 9L, "operator"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("库存已变化");
|
||||
|
||||
verify(repository, never()).pick(any(Long.class), any(Integer.class), any(), any(),
|
||||
verify(repository, never()).pick(any(Long.class), any(Integer.class),
|
||||
any(Integer.class), any(Long.class));
|
||||
verify(repository, never()).touchTask(any(Long.class), any(Integer.class));
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
@@ -1232,7 +1237,7 @@ class LoadingPickServiceTest {
|
||||
task.setVersion(3);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 2, "stale-token", List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "AUTO", null, 0, null)));
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null)));
|
||||
|
||||
assertThatThrownBy(() -> service.batch(20L, "batch-stale", request, 9L, "operator"))
|
||||
.isInstanceOf(BizException.class);
|
||||
@@ -1250,15 +1255,14 @@ class LoadingPickServiceTest {
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L)).thenReturn(List.of(item));
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item));
|
||||
when(repository.pick(30L, 2, "MANUAL", "[]", 0, 101L)).thenReturn(1);
|
||||
when(repository.pick(30L, 2, 0, 101L)).thenReturn(1);
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(warehouse.occupiedResources(any(), any(Long.class), any(), any(Long.class))).thenReturn(
|
||||
ApiResponse.success(List.of(new WarehousePalletLocationClient.OccupiedResource(
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 2))));
|
||||
String snapshotToken = service.inventorySnapshot(20L, 101L).snapshotToken();
|
||||
BatchItemRequest line = new BatchItemRequest(
|
||||
30L, "PICK", 2, null, "MANUAL", List.of(), 0, null);
|
||||
BatchItemRequest line = new BatchItemRequest(30L, "PICK", 2, null, 0, null);
|
||||
|
||||
assertThatThrownBy(() -> service.batch(20L, "batch-stale", new BatchRequest(
|
||||
101L, 10L, 99L, 1, snapshotToken, List.of(line)), 9L, "operator"))
|
||||
@@ -1270,7 +1274,7 @@ class LoadingPickServiceTest {
|
||||
service.batch(20L, "batch-valid", new BatchRequest(
|
||||
101L, 10L, 99L, 0, snapshotToken, List.of(line)), 9L, "operator");
|
||||
|
||||
verify(repository, times(1)).pick(30L, 2, "MANUAL", "[]", 0, 101L);
|
||||
verify(repository, times(1)).pick(30L, 2, 0, 101L);
|
||||
verify(repository, times(1)).touchTask(20L, 0);
|
||||
}
|
||||
|
||||
@@ -1291,7 +1295,7 @@ class LoadingPickServiceTest {
|
||||
|
||||
BatchRequest staleRequest = new BatchRequest(101L, 10L, 99L, 1,
|
||||
"stale-token", List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "AUTO", null, 0, null)));
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null)));
|
||||
assertThatThrownBy(() -> service.batch(20L, "batch-stale-after-resync", staleRequest,
|
||||
9L, "operator")).isInstanceOf(BizException.class);
|
||||
|
||||
@@ -1299,7 +1303,7 @@ class LoadingPickServiceTest {
|
||||
assertThat(pending.getPickedPackageCount()).isZero();
|
||||
assertThat(pending.getItemStatus()).isEqualTo("PENDING");
|
||||
verify(repository, never()).update(any(LoadingPickItemEntity.class));
|
||||
verify(repository, never()).pick(any(Long.class), any(Integer.class), any(), any(),
|
||||
verify(repository, never()).pick(any(Long.class), any(Integer.class),
|
||||
any(Integer.class), any(Long.class));
|
||||
verify(repository, never()).touchTask(any(Long.class), any(Integer.class));
|
||||
}
|
||||
@@ -1318,7 +1322,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.item(20L, 30L, 101L)).thenReturn(item);
|
||||
when(repository.pick(30L, 2, "MANUAL", "[]", 0, 101L)).thenReturn(1);
|
||||
when(repository.pick(30L, 2, 0, 101L)).thenReturn(1);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item));
|
||||
}
|
||||
@@ -1329,7 +1333,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.item(20L, 30L, 101L)).thenReturn(item);
|
||||
when(repository.partial(30L, 1, "MANUAL", "[]", 0, 101L)).thenReturn(1);
|
||||
when(repository.partial(30L, 1, 0, 101L)).thenReturn(1);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item));
|
||||
}
|
||||
|
||||
+72
-54
@@ -52,37 +52,38 @@ class MeasurementLocationOccupationServiceTest {
|
||||
properties.setWarehouseResourceTenantId(90001L);
|
||||
when(workflow.findOwned(BUSINESS_TENANT, INBOUND)).thenReturn(inbound());
|
||||
when(measurements.listWeights(BUSINESS_TENANT, INBOUND)).thenReturn(confirmedWeights());
|
||||
when(warehouse.reserve(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(batch("RESERVED")));
|
||||
when(warehouse.confirm(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(batch("OCCUPIED")));
|
||||
when(warehouse.reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("RESERVED", 3)));
|
||||
when(warehouse.confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("OCCUPIED", 3)));
|
||||
|
||||
service.occupy(BUSINESS_TENANT, INBOUND, OPERATOR, MEASUREMENT, 3);
|
||||
|
||||
ArgumentCaptor<WarehousePalletLocationClient.ReserveRequest> reserve =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.ReserveRequest.class);
|
||||
verify(warehouse).reserve(org.mockito.ArgumentMatchers.eq("internal-token"), reserve.capture());
|
||||
assertThat(reserve.getValue().tenantId()).isEqualTo(PLATFORM_RESOURCE_TENANT);
|
||||
ArgumentCaptor<WarehousePalletLocationClient.BlockReserveRequest> reserve =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.BlockReserveRequest.class);
|
||||
verify(warehouse).reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"), reserve.capture());
|
||||
assertThat(reserve.getValue().businessId()).isEqualTo(INBOUND);
|
||||
assertThat(reserve.getValue().idempotencyKey()).startsWith("measure-88-30-3-");
|
||||
assertThat(reserve.getValue().pallets())
|
||||
.extracting(WarehousePalletLocationClient.PalletReference::palletId)
|
||||
.containsExactly(601L, 601L, 601L);
|
||||
assertThat(reserve.getValue().idempotencyKey()).startsWith("measure-block-88-30-3-");
|
||||
assertThat(reserve.getValue().requiredSlotCount()).isEqualTo(3);
|
||||
|
||||
ArgumentCaptor<WarehousePalletLocationClient.ConfirmRequest> confirm =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.ConfirmRequest.class);
|
||||
verify(warehouse).confirm(org.mockito.ArgumentMatchers.eq("internal-token"), confirm.capture());
|
||||
assertThat(confirm.getValue().tenantId()).isEqualTo(PLATFORM_RESOURCE_TENANT);
|
||||
ArgumentCaptor<WarehousePalletLocationClient.BlockConfirmRequest> confirm =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.BlockConfirmRequest.class);
|
||||
verify(warehouse).confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"), confirm.capture());
|
||||
assertThat(confirm.getValue().businessId()).isEqualTo(INBOUND);
|
||||
assertThat(confirm.getValue().packageCount()).isEqualTo(20);
|
||||
assertThat(confirm.getValue().blockId()).isEqualTo(500L);
|
||||
assertThat(confirm.getValue().idempotencyKey())
|
||||
.isEqualTo(reserve.getValue().idempotencyKey() + "-confirm");
|
||||
|
||||
verify(allocations, times(2)).replace(
|
||||
verify(allocations, times(2)).replaceBlock(
|
||||
org.mockito.ArgumentMatchers.eq(BUSINESS_TENANT),
|
||||
org.mockito.ArgumentMatchers.eq(INBOUND),
|
||||
org.mockito.ArgumentMatchers.eq(WAREHOUSE),
|
||||
org.mockito.ArgumentMatchers.eq(PLATFORM_RESOURCE_TENANT),
|
||||
org.mockito.ArgumentMatchers.eq(20),
|
||||
org.mockito.ArgumentMatchers.eq(new BigDecimal("1.0000")),
|
||||
org.mockito.ArgumentMatchers.any());
|
||||
verify(allocations, times(2)).bindConfirmedWeights(
|
||||
org.mockito.ArgumentMatchers.eq(BUSINESS_TENANT),
|
||||
org.mockito.ArgumentMatchers.eq(INBOUND), org.mockito.ArgumentMatchers.anyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,33 +91,51 @@ class MeasurementLocationOccupationServiceTest {
|
||||
properties.setWarehouseResourceTenantId(0L);
|
||||
when(workflow.findOwned(BUSINESS_TENANT, INBOUND)).thenReturn(inbound());
|
||||
when(measurements.listWeights(BUSINESS_TENANT, INBOUND)).thenReturn(confirmedWeights());
|
||||
when(warehouse.reserve(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(batch("RESERVED")));
|
||||
when(warehouse.confirm(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(batch("OCCUPIED")));
|
||||
when(warehouse.reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("RESERVED", 3)));
|
||||
when(warehouse.confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("OCCUPIED", 3)));
|
||||
|
||||
service.occupy(BUSINESS_TENANT, INBOUND, OPERATOR, MEASUREMENT, 3);
|
||||
|
||||
ArgumentCaptor<WarehousePalletLocationClient.ReserveRequest> reserve =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.ReserveRequest.class);
|
||||
verify(warehouse).reserve(org.mockito.ArgumentMatchers.eq("internal-token"), reserve.capture());
|
||||
assertThat(reserve.getValue().tenantId()).isEqualTo(PLATFORM_RESOURCE_TENANT);
|
||||
ArgumentCaptor<WarehousePalletLocationClient.BlockReserveRequest> reserve =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.BlockReserveRequest.class);
|
||||
verify(warehouse).reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"), reserve.capture());
|
||||
assertThat(reserve.getValue().warehouseId()).isEqualTo(WAREHOUSE);
|
||||
|
||||
ArgumentCaptor<WarehousePalletLocationClient.ConfirmRequest> confirm =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.ConfirmRequest.class);
|
||||
verify(warehouse).confirm(org.mockito.ArgumentMatchers.eq("internal-token"), confirm.capture());
|
||||
assertThat(confirm.getValue().tenantId()).isEqualTo(PLATFORM_RESOURCE_TENANT);
|
||||
assertThat(confirm.getValue().packageCount()).isEqualTo(20);
|
||||
ArgumentCaptor<WarehousePalletLocationClient.BlockConfirmRequest> confirm =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.BlockConfirmRequest.class);
|
||||
verify(warehouse).confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"), confirm.capture());
|
||||
assertThat(confirm.getValue().businessId()).isEqualTo(INBOUND);
|
||||
|
||||
verify(allocations, times(2)).replace(
|
||||
verify(allocations, times(2)).replaceBlock(
|
||||
org.mockito.ArgumentMatchers.eq(BUSINESS_TENANT),
|
||||
org.mockito.ArgumentMatchers.eq(INBOUND),
|
||||
org.mockito.ArgumentMatchers.eq(WAREHOUSE),
|
||||
org.mockito.ArgumentMatchers.eq(PLATFORM_RESOURCE_TENANT),
|
||||
org.mockito.ArgumentMatchers.eq(20),
|
||||
org.mockito.ArgumentMatchers.eq(new BigDecimal("1.0000")),
|
||||
org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void reservesThreeSlotsForVolumeWhileBindingOnlyTwoConfirmedWeightImages() {
|
||||
InboundOrderEntity inbound = inbound();
|
||||
inbound.setActualVolumeCbm(new BigDecimal("2.5000"));
|
||||
List<InboundWeightResultEntity> twoWeights = confirmedWeights().subList(0, 2);
|
||||
when(workflow.findOwned(BUSINESS_TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(measurements.listWeights(BUSINESS_TENANT, INBOUND)).thenReturn(twoWeights);
|
||||
when(warehouse.reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("RESERVED", 3)));
|
||||
when(warehouse.confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("OCCUPIED", 3)));
|
||||
|
||||
service.occupy(BUSINESS_TENANT, INBOUND, OPERATOR, MEASUREMENT, 3);
|
||||
|
||||
verify(warehouse).reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.argThat(request -> request.requiredSlotCount() == 3));
|
||||
verify(allocations, times(2)).bindConfirmedWeights(BUSINESS_TENANT, INBOUND, twoWeights);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsConfirmedWeightWithoutConfirmationTimeInsteadOfDroppingIt() {
|
||||
InboundWeightResultEntity weight = confirmedWeights().get(0);
|
||||
@@ -128,7 +147,7 @@ class MeasurementLocationOccupationServiceTest {
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("缺少确认时间");
|
||||
|
||||
verify(warehouse, never()).reserve(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
|
||||
verify(warehouse, never()).reserveBlock(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,7 +161,7 @@ class MeasurementLocationOccupationServiceTest {
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("缺少有效净重");
|
||||
|
||||
verify(warehouse, never()).reserve(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
|
||||
verify(warehouse, never()).reserveBlock(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,7 +175,7 @@ class MeasurementLocationOccupationServiceTest {
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("缺少有效托盘");
|
||||
|
||||
verify(warehouse, never()).reserve(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
|
||||
verify(warehouse, never()).reserveBlock(org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -164,24 +183,23 @@ class MeasurementLocationOccupationServiceTest {
|
||||
properties.setWarehouseResourceTenantId(90001L);
|
||||
when(workflow.findOwned(BUSINESS_TENANT, INBOUND)).thenReturn(inbound());
|
||||
when(measurements.listWeights(BUSINESS_TENANT, INBOUND)).thenReturn(confirmedWeights());
|
||||
when(warehouse.reserve(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(batch("RESERVED")));
|
||||
when(warehouse.reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("RESERVED", 3)));
|
||||
IllegalStateException failure = new IllegalStateException("confirm failed");
|
||||
when(warehouse.confirm(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
when(warehouse.confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenThrow(failure);
|
||||
when(warehouse.release(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(List.of()));
|
||||
when(warehouse.releaseBlock(org.mockito.ArgumentMatchers.eq("internal-token"),
|
||||
org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("RELEASED", 3)));
|
||||
|
||||
assertThatThrownBy(() -> service.occupy(BUSINESS_TENANT, INBOUND, OPERATOR, MEASUREMENT, 3))
|
||||
.isSameAs(failure);
|
||||
|
||||
ArgumentCaptor<WarehousePalletLocationClient.ReleaseRequest> release =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.ReleaseRequest.class);
|
||||
verify(warehouse).release(org.mockito.ArgumentMatchers.eq("internal-token"), release.capture());
|
||||
assertThat(release.getValue().tenantId()).isEqualTo(PLATFORM_RESOURCE_TENANT);
|
||||
ArgumentCaptor<WarehousePalletLocationClient.BlockReleaseRequest> release =
|
||||
ArgumentCaptor.forClass(WarehousePalletLocationClient.BlockReleaseRequest.class);
|
||||
verify(warehouse).releaseBlock(org.mockito.ArgumentMatchers.eq("internal-token"), release.capture());
|
||||
assertThat(release.getValue().businessId()).isEqualTo(INBOUND);
|
||||
verify(allocations).replace(BUSINESS_TENANT, INBOUND, WAREHOUSE, PLATFORM_RESOURCE_TENANT,
|
||||
20, batch("RESERVED"));
|
||||
verify(allocations).replaceBlock(BUSINESS_TENANT, INBOUND, WAREHOUSE,
|
||||
20, new BigDecimal("1.0000"), block("RESERVED", 3));
|
||||
}
|
||||
|
||||
private static InboundProperties properties() {
|
||||
@@ -209,12 +227,12 @@ class MeasurementLocationOccupationServiceTest {
|
||||
return value;
|
||||
}
|
||||
|
||||
private static WarehousePalletLocationClient.BatchResult batch(String state) {
|
||||
return new WarehousePalletLocationClient.BatchResult(3, java.util.stream.IntStream.rangeClosed(1, 3)
|
||||
.mapToObj(index -> new WarehousePalletLocationClient.AllocationResult(
|
||||
500L + index, "RES-00" + index, index, 601L, "PL-001", new BigDecimal("1.200000"),
|
||||
700L + index, "A-00" + index, new BigDecimal("0.333333"), state))
|
||||
.toList());
|
||||
private static WarehousePalletLocationClient.BlockResult block(String state, int count) {
|
||||
return new WarehousePalletLocationClient.BlockResult(500L, state,
|
||||
java.util.stream.IntStream.rangeClosed(1, count)
|
||||
.mapToObj(index -> new WarehousePalletLocationClient.BlockResource(
|
||||
500L, 600L + index, 700L + index, "A-00" + index, index, false, state))
|
||||
.toList());
|
||||
}
|
||||
|
||||
private static List<InboundWeightResultEntity> confirmedWeights() {
|
||||
|
||||
+31
-58
@@ -56,9 +56,6 @@ class PdaPickSettlementServiceTest {
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
|
||||
when(allocations.lockConfirmed(10001L, 31L)).thenAnswer(invocation ->
|
||||
List.of(allocation(1L, 1, 501L, 10), allocation(2L, 2, 502L, 10)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(), List.of(
|
||||
transfer(1, 501L, 2), transfer(2, 502L, 10)))));
|
||||
writes(12, 8);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
@@ -71,14 +68,13 @@ class PdaPickSettlementServiceTest {
|
||||
verify(splits).normalizeLoadedCartonLabels(10001L, 31L, 8);
|
||||
verify(splits).markSourceLoaded(10001L, 31L, 8, 9L, 3);
|
||||
verify(allocations).consume(10001L, 31L, 1L, 8);
|
||||
verify(warehouse).settle(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.warehouseId() == 701L && request.sourceBusinessId() == 31L
|
||||
&& request.idempotencyKey().equals("PDA-SETTLE-1-31")
|
||||
&& request.releasedReservationIds().isEmpty()
|
||||
&& request.remainingItems().equals(List.of(new WarehousePalletLocationClient.SettlementTransferItem(91L, 2),
|
||||
new WarehousePalletLocationClient.SettlementTransferItem(92L, 10)))));
|
||||
verify(allocations).copyRemainingToChild(eq(10001L), anyLong(), any());
|
||||
verify(allocations).markTransferred(eq(10001L), eq(31L), any(), eq(List.of()));
|
||||
verify(warehouse).transferBlock(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.blockId() == 800L && request.sourceBusinessId() == 31L
|
||||
&& request.targetBusinessId() > 0
|
||||
&& request.idempotencyKey().equals("PDA-BLOCK-1-31")));
|
||||
verify(warehouse, never()).releaseBlock(anyString(), any());
|
||||
verify(allocations).copyBlockToChild(eq(10001L), anyLong(), any());
|
||||
verify(allocations).markBlockTransferred(10001L, 31L, 800L);
|
||||
verify(splits).convergePartialPlanItem(21L, 9L, "operator");
|
||||
}
|
||||
|
||||
@@ -92,8 +88,6 @@ class PdaPickSettlementServiceTest {
|
||||
new MeasurementItemRow(52L, 61L, 41L, 1)));
|
||||
when(allocations.lockConfirmed(10001L, 31L)).thenAnswer(invocation ->
|
||||
List.of(allocation(1L, 1, 501L, 2)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(), List.of(transfer(1, 501L, 1)))));
|
||||
writes(1, 1);
|
||||
when(splits.insertSplitItem(anyLong(), eq(10001L), anyLong(), eq(31L), anyLong(), anyInt(),
|
||||
eq(41L), eq(61L), eq(52L), eq(1))).thenReturn(1);
|
||||
@@ -114,7 +108,7 @@ class PdaPickSettlementServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void reusesWarehouseSettlementTargetWhenRetryingAfterLocalRollback() {
|
||||
void transfersTheWholeBlockToTheGeneratedRemainderChild() {
|
||||
LoadingPickItemEntity item = item(11L, 21L, 31L, 41L, 3, 1, "PARTIAL");
|
||||
resolved(List.of(item));
|
||||
when(splits.lockParent(31L)).thenReturn(parent(3));
|
||||
@@ -122,19 +116,16 @@ class PdaPickSettlementServiceTest {
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 3)));
|
||||
when(allocations.lockConfirmed(10001L, 31L)).thenAnswer(invocation ->
|
||||
List.of(allocation(1L, 1, 501L, 3)));
|
||||
when(warehouse.settlementTarget("internal-token", 31L, "PDA-SETTLE-1-31"))
|
||||
.thenReturn(ApiResponse.success(999L));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(), List.of(transfer(1, 501L, 2)))));
|
||||
writes(2, 1);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
verify(splits).cloneChildWithPurpose(eq(10001L), eq(31L), eq(31L), eq(999L),
|
||||
verify(splits).cloneChildWithPurpose(eq(10001L), eq(31L), eq(31L), anyLong(),
|
||||
eq(1002L), eq("ORDER-REMAINDER"), eq("IN-001-1"), eq(2),
|
||||
eq(1), eq("REMAINDER"), eq(9L), eq("operator"));
|
||||
verify(warehouse).settle(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.targetBusinessId() == 999L && request.targetPackageCount() == 2));
|
||||
verify(warehouse).transferBlock(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.blockId() == 800L && request.sourceBusinessId() == 31L
|
||||
&& request.targetBusinessId() > 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,12 +137,13 @@ class PdaPickSettlementServiceTest {
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
verify(warehouse, never()).settle(anyString(), any());
|
||||
verify(warehouse, never()).transferBlock(anyString(), any());
|
||||
verify(warehouse, never()).releaseBlock(anyString(), any());
|
||||
verify(splits, never()).convergePartialPlanItem(anyLong(), anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void releasesFullyPickedAllocationBeforeTransferringChildResources() {
|
||||
void partialPickTransfersTheWholeBlockWithoutReleasingPickedEmptySlots() {
|
||||
LoadingPickItemEntity item = item(11L, 21L, 31L, 41L, 10, 10, "PICKED");
|
||||
resolved(List.of(item));
|
||||
when(splits.lockParent(31L)).thenReturn(parent(20));
|
||||
@@ -159,17 +151,13 @@ class PdaPickSettlementServiceTest {
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
|
||||
when(allocations.lockConfirmed(10001L, 31L)).thenAnswer(invocation ->
|
||||
List.of(allocation(1L, 1, 501L, 10), allocation(2L, 2, 502L, 10)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(released(91L, 501L)),
|
||||
List.of(transfer(2, 502L, 10)))));
|
||||
writes(10, 10);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
verify(warehouse).settle(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.idempotencyKey().equals("PDA-SETTLE-1-31")
|
||||
&& request.releasedReservationIds().equals(List.of(91L))
|
||||
&& request.remainingItems().equals(List.of(new WarehousePalletLocationClient.SettlementTransferItem(92L, 10)))));
|
||||
verify(warehouse).transferBlock(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.blockId() == 800L && request.sourceBusinessId() == 31L));
|
||||
verify(warehouse, never()).releaseBlock(anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -184,9 +172,6 @@ class PdaPickSettlementServiceTest {
|
||||
first.setPalletId(801L);
|
||||
second.setPalletId(801L);
|
||||
when(allocations.lockConfirmed(10001L, 31L)).thenReturn(List.of(first, second));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(released(91L, 501L), released(92L, 501L)),
|
||||
List.of())));
|
||||
writes(0, 20);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
@@ -196,10 +181,10 @@ class PdaPickSettlementServiceTest {
|
||||
verify(splits, never()).cloneChildWithPurpose(anyLong(), anyLong(), anyLong(), anyLong(),
|
||||
anyLong(), anyString(), anyString(), anyInt(), anyInt(), anyString(), anyLong(), anyString());
|
||||
verify(splits).markSourceLoaded(10001L, 31L, 20, 9L, 3);
|
||||
verify(warehouse).settle(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.targetBusinessId() == 0L && request.targetPackageCount() == 0
|
||||
&& request.releasedReservationIds().equals(List.of(91L, 92L))
|
||||
&& request.remainingItems().isEmpty()));
|
||||
verify(warehouse).releaseBlock(eq("internal-token"), org.mockito.ArgumentMatchers.argThat(request ->
|
||||
request.blockId() == 800L && request.businessId() == 31L
|
||||
&& request.reason().equals("ALL_PACKAGES_PICKED")));
|
||||
verify(warehouse, never()).transferBlock(anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -222,9 +207,6 @@ class PdaPickSettlementServiceTest {
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
|
||||
when(allocations.lockConfirmed(10001L, 31L))
|
||||
.thenReturn(List.of(allocation(1L, 1, 501L, 10), allocation(2L, 2, 502L, 10)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(
|
||||
List.of(released(91L, 501L), released(92L, 502L)), List.of())));
|
||||
writes(0, 20);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
@@ -243,9 +225,6 @@ class PdaPickSettlementServiceTest {
|
||||
.thenReturn(List.of(new MeasurementItemRow(51L, 61L, 41L, 20)));
|
||||
when(allocations.lockConfirmed(10001L, 31L))
|
||||
.thenReturn(List.of(allocation(1L, 1, 501L, 10), allocation(2L, 2, 502L, 10)));
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(
|
||||
List.of(released(91L, 501L), released(92L, 502L)), List.of())));
|
||||
writes(0, 20);
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
@@ -294,9 +273,6 @@ class PdaPickSettlementServiceTest {
|
||||
|
||||
stubOrderSplitSuccess();
|
||||
writes(0, 20);
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(
|
||||
List.of(released(91L, 501L), released(92L, 502L)), List.of())));
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
@@ -312,8 +288,6 @@ class PdaPickSettlementServiceTest {
|
||||
resolvedChild(item, 32L, 42L, 62L, 52L, 12, 2);
|
||||
when(splits.nextSplitSequence(10001L, 31L)).thenReturn(2);
|
||||
childWrites(32L, 42L, 62L, 52L, 5, 2);
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(), List.of(transfer(1, 501L, 7)))));
|
||||
|
||||
service.settle(1L, 9L, "operator");
|
||||
|
||||
@@ -336,8 +310,6 @@ class PdaPickSettlementServiceTest {
|
||||
when(splits.nextSplitSequence(10001L, 31L)).thenReturn(3);
|
||||
childWrites(33L, 43L, 63L, 53L, 4, 3);
|
||||
when(splits.markSourceLoaded(10001L, 33L, 4, 9L, 3)).thenReturn(0, 1);
|
||||
when(warehouse.settle(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new WarehousePalletLocationClient.SettlementResult(List.of(), List.of(transfer(1, 501L, 5)))));
|
||||
|
||||
assertThatThrownBy(() -> service.settle(1L, 9L, "operator"))
|
||||
.hasMessageContaining("来源分片状态已被并发修改");
|
||||
@@ -360,6 +332,10 @@ class PdaPickSettlementServiceTest {
|
||||
task.setId(1L); task.setLoadingPlanId(701L);
|
||||
when(picks.task(1L)).thenReturn(task);
|
||||
when(splits.containerBookingId(701L)).thenReturn(801L);
|
||||
when(warehouse.transferBlock(eq("internal-token"), any()))
|
||||
.thenReturn(ApiResponse.success(block("OCCUPIED")));
|
||||
when(warehouse.releaseBlock(eq("internal-token"), any()))
|
||||
.thenReturn(ApiResponse.success(block("RELEASED")));
|
||||
stubOrderSplitSuccess();
|
||||
}
|
||||
|
||||
@@ -456,19 +432,16 @@ class PdaPickSettlementServiceTest {
|
||||
private static InboundPalletAllocationEntity allocation(long id, int no, long locationId, int allocated) {
|
||||
InboundPalletAllocationEntity value = new InboundPalletAllocationEntity();
|
||||
value.setId(id); value.setAllocationNo(no); value.setLocationId(locationId); value.setWarehouseId(701L);
|
||||
value.setBlockId(800L); value.setSlotNo(no); value.setAllocationStatus("CONFIRMED");
|
||||
value.setReservationId(90L + no);
|
||||
value.setAllocatedPackageCount(allocated); value.setPickedPackageCount(0);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static WarehousePalletLocationClient.TransferResult transfer(int no, long locationId, int remaining) {
|
||||
return new WarehousePalletLocationClient.TransferResult(90L + no, "RES-" + no, 92L + no, no,
|
||||
0L, 701L, 801L + no, "PALLET-" + no, null, locationId, "L-" + no, null, remaining, "OCCUPIED");
|
||||
}
|
||||
|
||||
private static WarehousePalletLocationClient.SelectiveReleaseResult released(long reservationId, long locationId) {
|
||||
return new WarehousePalletLocationClient.SelectiveReleaseResult(reservationId, 92L, 1, 801L,
|
||||
locationId, "L-1", 10);
|
||||
private static WarehousePalletLocationClient.BlockResult block(String status) {
|
||||
return new WarehousePalletLocationClient.BlockResult(800L, status, List.of(
|
||||
new WarehousePalletLocationClient.BlockResource(
|
||||
800L, 91L, 501L, "L-1", 1, false, status)));
|
||||
}
|
||||
|
||||
private static LoadingPickItemEntity item(long id, long planItemId, long inboundId, long detailId,
|
||||
|
||||
@@ -1413,6 +1413,44 @@ class SmartLoadingServiceTest {
|
||||
assertThat(updated.getValue().getWarehouseRemark()).isEqualTo("装柜时注意外箱");
|
||||
}
|
||||
|
||||
@Test
|
||||
void quantityAdjustmentCanDecreaseWhenCalculatedRemainingQuantityIsNegative() {
|
||||
LoadingPlanEntity plan = plan("LOADING", 93);
|
||||
LoadingPlanItemEntity line = item(8, 9, 20, 50);
|
||||
when(repository.plan(1L)).thenReturn(plan, plan("LOADING", 94));
|
||||
when(repository.item(1L, 8L)).thenReturn(line);
|
||||
when(repository.booking(99L)).thenReturn(booking());
|
||||
when(repository.candidate(1L, 100L, 10L, 20L)).thenReturn(candidate(-25));
|
||||
when(repository.items(1L)).thenReturn(List.of(line));
|
||||
when(repository.touch(1L, 93, 7L, "u")).thenReturn(1);
|
||||
when(repository.update(line)).thenReturn(1);
|
||||
|
||||
service.adjust(1L, 8L, new AdjustRequest(30, 93, 0, "adjust-down"), 7L, "u");
|
||||
|
||||
ArgumentCaptor<LoadingPlanItemEntity> updated = ArgumentCaptor.forClass(LoadingPlanItemEntity.class);
|
||||
verify(repository).update(updated.capture());
|
||||
assertThat(updated.getValue().getPackageCount()).isEqualTo(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
void quantityAdjustmentOnlyAppliesRemainingQuantityLimitToTheIncrease() {
|
||||
LoadingPlanEntity plan = plan("LOADING", 2);
|
||||
LoadingPlanItemEntity line = item(8, 9, 20, 50);
|
||||
when(repository.plan(1L)).thenReturn(plan, plan("LOADING", 3));
|
||||
when(repository.item(1L, 8L)).thenReturn(line);
|
||||
when(repository.booking(99L)).thenReturn(booking());
|
||||
when(repository.candidate(1L, 100L, 10L, 20L)).thenReturn(candidate(10));
|
||||
when(repository.items(1L)).thenReturn(List.of(line));
|
||||
when(repository.touch(1L, 2, 7L, "u")).thenReturn(1);
|
||||
when(repository.update(line)).thenReturn(1);
|
||||
|
||||
service.adjust(1L, 8L, new AdjustRequest(60, 2, 0, "adjust-up"), 7L, "u");
|
||||
|
||||
ArgumentCaptor<LoadingPlanItemEntity> updated = ArgumentCaptor.forClass(LoadingPlanItemEntity.class);
|
||||
verify(repository).update(updated.capture());
|
||||
assertThat(updated.getValue().getPackageCount()).isEqualTo(60);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedQuantityAndCargoSnapshotDoNotWriteAdjustmentHistory() {
|
||||
LoadingPlanEntity plan = plan("LOADING", 2);
|
||||
|
||||
+6
-4
@@ -97,11 +97,13 @@ class TaillegRealProducerAcceptanceTest {
|
||||
private void seedAllocation(long id, long inboundId, long palletId, long locationId,
|
||||
int packages, String suffix) {
|
||||
jdbc.update("INSERT IGNORE INTO fl_inbound_pallet_allocation(id,tenant_id,inbound_id,allocation_no," +
|
||||
"warehouse_id,warehouse_resource_tenant_id,pallet_id,pallet_code,pallet_capacity_cbm," +
|
||||
"block_id,slot_no,weight_result_id,pallet_sequence,warehouse_id,warehouse_resource_tenant_id,pallet_id,pallet_code,pallet_capacity_cbm," +
|
||||
"allocated_volume_cbm,allocated_package_count,picked_package_count,location_id,location_code," +
|
||||
"reservation_id,reservation_no,allocation_status) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'CONFIRMED')",
|
||||
id, CUSTOMER, inboundId, 1, 92L, 0L, palletId, MARKER + "-PALLET-" + suffix,
|
||||
1.2, 0.4, packages, packages, locationId, "A-01", id + 1000, MARKER + "-RES-" + suffix);
|
||||
"shared_overflow,reservation_id,slot_status,allocation_status) " +
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'BOUND','CONFIRMED')",
|
||||
id, CUSTOMER, inboundId, 1, id + 3000, 1, id + 2000, 1, 92L, 0L,
|
||||
palletId, MARKER + "-PALLET-" + suffix, 1.2, 0.4, packages, packages,
|
||||
locationId, "A-01", false, id + 1000);
|
||||
}
|
||||
|
||||
private void seedPickItem(long id, long planItemId, long inboundId, long detailId, int packages) {
|
||||
|
||||
Reference in New Issue
Block a user