第三期装柜验收优化修改
This commit is contained in:
+4
-2
@@ -9,7 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class PdaConvergenceOpenApiContractTest {
|
||||
@Test
|
||||
void publishesClaimAssignWorkAndReviewRoutes() throws Exception {
|
||||
void publishesClaimAssignWorkAndManualCompletionRoutes() throws Exception {
|
||||
String yaml = new ClassPathResource("openapi/phase3-pda-convergence-v1.yaml")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertThat(yaml)
|
||||
@@ -22,11 +22,13 @@ class PdaConvergenceOpenApiContractTest {
|
||||
.contains("/tasks/{taskId}/items/{itemId}/skip:")
|
||||
.contains("/tasks/{taskId}/batch:")
|
||||
.contains("/tasks/{taskId}/review:")
|
||||
.contains("/tasks/{taskId}/complete:")
|
||||
.contains("/tasks/{taskId}/operations:")
|
||||
.contains("expectedAssignmentVersion")
|
||||
.contains("BatchRequest")
|
||||
.contains("inventorySnapshotToken")
|
||||
.contains("InventorySnapshot")
|
||||
.contains("expectedTaskVersion");
|
||||
.contains("expectedTaskVersion")
|
||||
.contains("CompleteLoadingRequest");
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ class PlatformLoadingPdaControllerContractTest {
|
||||
authority("skip", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, long.class, SkipRequest.class);
|
||||
authority("batch", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, String.class, BatchRequest.class);
|
||||
authority("review", "platform:firstleg:pda:review", AuthenticatedUser.class, long.class, ReviewRequest.class);
|
||||
authority("complete", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, CompleteLoadingRequest.class);
|
||||
authority("operations", "platform:firstleg:pda:view", AuthenticatedUser.class, long.class);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ class Phase3FinalUnifiedSqlContractTest {
|
||||
.contains("trg_inbound_split_detail_before_insert")
|
||||
.contains("trg_inbound_split_detail_before_update")
|
||||
.contains("trg_inbound_split_detail_before_delete")
|
||||
.contains("CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW', 'BATCH'))")
|
||||
.contains("CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW', 'BATCH', 'COMPLETE_LOADING'))")
|
||||
.contains("phase3_loading_evidence_add_column")
|
||||
.contains("DROP INDEX uk_loading_evidence_type")
|
||||
.contains("DROP INDEX uk_loading_evidence_seal")
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ class SmartLoadingSqlContractTest {
|
||||
.contains("IF OLD.plan_status = 'EDITING' AND NEW.plan_status = 'LOADING' THEN")
|
||||
.contains("ELSEIF OLD.plan_status = 'LOADING' AND NEW.plan_status = 'COMPLETED' THEN")
|
||||
.contains("LEFT JOIN fl_loading_pick_item pick_item")
|
||||
.contains("pick_item.picked_package_count <> pick_item.planned_package_count")
|
||||
.contains("pick_item.picked_package_count <> item.package_count")
|
||||
.contains("IF OLD.plan_status = 'LOADING' AND NEW.plan_status = 'COMPLETED' THEN")
|
||||
.contains("SET frozen_at = COALESCE(frozen_at, NEW.confirmed_at, CURRENT_TIMESTAMP(3))")
|
||||
.contains("已有PDA拣货记录的装柜明细仅允许收敛部分拣货实际箱数")
|
||||
|
||||
@@ -7,7 +7,9 @@ import com.baiyue.firstleg.dto.ContainerDtos.FullUpdateRequest;
|
||||
import com.baiyue.firstleg.dto.ContainerDtos.SaveRequest;
|
||||
import com.baiyue.firstleg.dto.ContainerDtos.TransportUpdateRequest;
|
||||
import com.baiyue.firstleg.entity.ContainerBookingEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanEntity;
|
||||
import com.baiyue.firstleg.repository.ContainerRepository;
|
||||
import com.baiyue.firstleg.repository.SmartLoadingRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -33,6 +35,8 @@ import static org.mockito.Mockito.when;
|
||||
class ContainerServiceTest {
|
||||
@MockBean private ContainerRepository repository;
|
||||
@MockBean private WarehouseMasterDataClient warehouses;
|
||||
@MockBean private SmartLoadingRepository smartLoadingRepository;
|
||||
@MockBean private LoadingPickService loadingPickService;
|
||||
@Resource private ContainerService service;
|
||||
|
||||
@BeforeEach
|
||||
@@ -118,6 +122,27 @@ class ContainerServiceTest {
|
||||
.updateTransport(11L, "MSCU1234567", "V002", 2, 9L, "admin");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("预装清单推送后再绑定集装箱号会创建PDA拣货任务")
|
||||
void startsPdaTaskWhenContainerIsBoundAfterPreloadPush() {
|
||||
ContainerBookingEntity current = booking("EDITING", 2);
|
||||
LoadingPlanEntity plan = new LoadingPlanEntity();
|
||||
plan.setId(31L);
|
||||
plan.setPlanStatus("EDITING");
|
||||
plan.setPreloadPushedAt(LocalDateTime.of(2026, 8, 11, 20, 0));
|
||||
when(repository.find(11L)).thenReturn(current);
|
||||
when(repository.updateTransport(11L, "MSCU1234567", "V002", 2, 9L, "admin"))
|
||||
.thenReturn(1);
|
||||
when(smartLoadingRepository.planByBooking(11L)).thenReturn(plan);
|
||||
|
||||
service.updateTransport(11L, 9L, "admin",
|
||||
new TransportUpdateRequest("MSCU1234567", "V002", 2));
|
||||
|
||||
verify(loadingPickService).begin(org.mockito.ArgumentMatchers.eq(31L),
|
||||
org.mockito.ArgumentMatchers.eq("MSCU1234567"), org.mockito.ArgumentMatchers.eq(9L),
|
||||
org.mockito.ArgumentMatchers.eq("admin"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("已确认货柜全部只读")
|
||||
void rejectsConfirmedUpdate() {
|
||||
|
||||
@@ -109,7 +109,7 @@ class LoadingListServiceTest {
|
||||
verify(repository).advance(10L, "LOADING", "COMPLETED", 2, 9L, "operator");
|
||||
verify(repository).appendHistory(eq(10L), eq("PDA-COMPLETE-10-21"),
|
||||
eq("PDA_COMPLETE_LOADING"), eq("LOADING"), eq("COMPLETED"), anyString(), anyString(),
|
||||
eq(9L), eq("operator"), eq("全部箱数已完成PDA拣货"));
|
||||
eq(9L), eq("operator"), eq("PDA已人工确认装柜完成"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.dto.LoadingPickDtos.ClaimRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.CompleteLoadingRequest;
|
||||
import com.baiyue.firstleg.client.WarehousePalletLocationClient;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||
@@ -67,7 +68,7 @@ class LoadingPickServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void touchesTaskBeforeCompletingLastPickedItem() {
|
||||
void keepsResourcesLockedAfterLastPickedItemUntilManualCompletion() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity item = item();
|
||||
preparePick(task, item, "pick-2");
|
||||
@@ -76,11 +77,10 @@ class LoadingPickServiceTest {
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "pick-2"), 9L, "operator");
|
||||
|
||||
InOrder order = inOrder(repository, settlementService, loadingListService);
|
||||
order.verify(repository).touchTask(20L, 0);
|
||||
order.verify(settlementService).settle(20L, 9L, "operator");
|
||||
order.verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
order.verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
verify(repository).touchTask(20L, 0);
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
verify(loadingListService, never()).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
verify(repository).insert(any(LoadingPickHistoryEntity.class));
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class LoadingPickServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void touchesTaskBeforeCompletingLastPartialItem() {
|
||||
void keepsPartialPickLockedUntilManualCompletion() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity item = item();
|
||||
preparePartial(task, item, "partial-1");
|
||||
@@ -141,11 +141,10 @@ class LoadingPickServiceTest {
|
||||
service.partial(20L, 30L, new PartialPickRequest(101L, 1, List.of(), 0, "partial-1"),
|
||||
9L, "operator");
|
||||
|
||||
InOrder order = inOrder(repository, settlementService, loadingListService);
|
||||
order.verify(repository).touchTask(20L, 0);
|
||||
order.verify(settlementService).settle(20L, 9L, "operator");
|
||||
order.verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
order.verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
verify(repository).touchTask(20L, 0);
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
verify(loadingListService, never()).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,7 +170,7 @@ class LoadingPickServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void reviewSettlesResolvedItemsAndCompletesPlan() {
|
||||
void manualCompletionSettlesResolvedItemsAndCompletesPlan() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity item = item();
|
||||
item.setPlannedPackageCount(5);
|
||||
@@ -181,15 +180,15 @@ class LoadingPickServiceTest {
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.items(20L)).thenReturn(List.of(item));
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item));
|
||||
when(repository.settlePartial(40L, 30L, 3, 5, 9L, "operator")).thenReturn(1);
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
|
||||
|
||||
service.review(20L, new ReviewRequest(0, "review-1", "checked"), 9L, "operator");
|
||||
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
||||
|
||||
verify(repository).insert(any(LoadingPickOperationEntity.class));
|
||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||
verify(settlementService).settle(20L, 9L, "operator");
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
}
|
||||
|
||||
@@ -237,8 +236,8 @@ class LoadingPickServiceTest {
|
||||
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(settlementService, never()).settle(20L, 9L, "operator");
|
||||
verify(loadingListService, never()).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(repository, times(1)).syncInboundProjections(20L, 9L, "operator");
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,30 @@ class PdaPickSettlementServiceTest {
|
||||
verify(splits).convergePartialPlanItem(21L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void reusesWarehouseSettlementTargetWhenRetryingAfterLocalRollback() {
|
||||
LoadingPickItemEntity item = item(11L, 21L, 31L, 41L, 3, 1, "PARTIAL");
|
||||
resolved(List.of(item));
|
||||
when(splits.lockParent(31L)).thenReturn(parent(3));
|
||||
when(splits.lockLatestApprovedItemsForInbound(10001L, 31L))
|
||||
.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),
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
void repeatedSettleWithExistingChildDoesNotConsumeOrTransferAgain() {
|
||||
LoadingPickItemEntity item = item(11L, 21L, 31L, 41L, 10, 6, "PARTIAL");
|
||||
|
||||
Reference in New Issue
Block a user