feat(firstleg): add atomic PDA batch picking endpoint
This commit is contained in:
@@ -5,6 +5,8 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.PartialPickRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PickRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.ReviewRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.SkipRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.BatchRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.BatchItemRequest;
|
||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
||||
@@ -18,6 +20,7 @@ import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -25,6 +28,8 @@ import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
@SpringJUnitConfig(LoadingPickService.class)
|
||||
class LoadingPickServiceTest {
|
||||
@@ -133,6 +138,94 @@ class LoadingPickServiceTest {
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void atomicallySubmitsCompleteBatchAndReplaysSameResult() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity picked = item();
|
||||
LoadingPickItemEntity skipped = item();
|
||||
skipped.setId(31L);
|
||||
skipped.setLoadingPlanItemId(41L);
|
||||
skipped.setInboundId(51L);
|
||||
skipped.setInboundDetailId(61L);
|
||||
List<LoadingPickItemEntity> items = List.of(picked, skipped);
|
||||
AtomicReference<LoadingPickOperationEntity> operation = new AtomicReference<>();
|
||||
when(repository.history(any(String.class))).thenReturn(null);
|
||||
when(repository.operation("batch-1")).thenAnswer(invocation -> operation.get());
|
||||
doAnswer(invocation -> {
|
||||
Object value = invocation.getArgument(0);
|
||||
if (value instanceof LoadingPickOperationEntity entity && "BATCH".equals(entity.getActionType())) {
|
||||
operation.set(entity);
|
||||
}
|
||||
return null;
|
||||
}).when(repository).insert(any(LoadingPickOperationEntity.class));
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
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.skip(31L, 0, 101L)).thenReturn(1);
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
BatchRequest request = new BatchRequest(101L, "CSLU1234567", 0, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "MANUAL", List.of(), 0, null),
|
||||
new BatchItemRequest(31L, "SKIP", null, null, 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)).skip(31L, 0, 101L);
|
||||
verify(repository, times(1)).touchTask(20L, 0);
|
||||
verify(settlementService, times(1)).settle(20L, 9L, "operator");
|
||||
verify(loadingListService, times(1)).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(repository, times(1)).syncInboundProjections(20L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsWholeBatchBeforeWritingWhenAnyLineIsInvalid() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity first = item();
|
||||
LoadingPickItemEntity invalid = item();
|
||||
invalid.setId(31L);
|
||||
invalid.setLoadingPlanItemId(41L);
|
||||
invalid.setInboundId(51L);
|
||||
invalid.setInboundDetailId(61L);
|
||||
invalid.setPlannedPackageCount(4);
|
||||
List<LoadingPickItemEntity> items = List.of(first, invalid);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L)).thenReturn(items);
|
||||
when(repository.items(20L, 101L)).thenReturn(items);
|
||||
BatchRequest request = new BatchRequest(101L, "CSLU1234567", 0, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "MANUAL", List.of(), 0, null),
|
||||
new BatchItemRequest(31L, "PARTIAL", null, 4, "AUTO", null, 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()).touchTask(any(Long.class), any(Integer.class));
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsBatchTaskVersionConflictBeforeWriting() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
task.setVersion(3);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
BatchRequest request = new BatchRequest(101L, "CSLU1234567", 2, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, "AUTO", null, 0, null)));
|
||||
|
||||
assertThatThrownBy(() -> service.batch(20L, "batch-stale", request, 9L, "operator"))
|
||||
.isInstanceOf(BizException.class);
|
||||
|
||||
verify(repository, never()).items(20L);
|
||||
verify(repository, never()).touchTask(any(Long.class), any(Integer.class));
|
||||
}
|
||||
|
||||
private void preparePick(LoadingPickTaskEntity task, LoadingPickItemEntity item, String operationId) {
|
||||
when(repository.history(operationId)).thenReturn(null);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
|
||||
Reference in New Issue
Block a user