This commit is contained in:
wujinliang
2026-08-27 21:48:47 +08:00
parent daf0ee2acd
commit 9d54c57b7c
38 changed files with 432 additions and 37 deletions
@@ -190,6 +190,20 @@ class LoadingModelBridgeServiceTest {
.hasMessageContaining("尚未全部完成装柜");
}
@Test
void bindsConfirmingPreloadToRunningPlanAndConfirmsIt() {
when(repository.byPair(10, 20, 40)).thenReturn(null);
when(repository.loadingPlan(40)).thenReturn(new LoadingPlanSnapshot(40L, 50L, "C1", "LOADING", 3, LocalDateTime.now()));
when(repository.allocatedPackages(10, 20, 40)).thenReturn(2);
when(repository.preload(10, 20)).thenReturn(new PreloadSnapshot(30L, 10L, 20L, "CONFIRMING", 4));
when(repository.confirmIncrementalPreload(30L, 10L, 20L, 4, 7L, "admin")).thenReturn(1);
service.bindIncrementalAllocation(40L, 10L, 20L, 7L, "admin", "assign-confirming");
verify(repository).insert(any());
verify(repository).confirmIncrementalPreload(30L, 10L, 20L, 4, 7L, "admin");
}
private static Link link(String preloadStatus, int preloadVersion, String loadingStatus,
int loadingVersion, int packages) {
return new Link(1L, 10L, 20L, 30L, preloadStatus, preloadVersion, 40L, 50L, "C1",
@@ -26,6 +26,8 @@ class ContainerBookingMapperContractTest {
.contains("fl_loading_plan")
.contains("p.id AS plan_id")
.contains("p.plan_no AS plan_no")
.contains("completed_loading_at")
.contains("p.confirmed_at")
.contains("fl_container_exception_history")
.contains("destination_clearance_document_status")
.contains("delivery_order_document_status")
@@ -0,0 +1,47 @@
package com.baiyue.firstleg.repository;
import com.baiyue.firstleg.entity.ContainerBookingEntity;
import com.baiyue.firstleg.mapper.ContainerBookingMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ContainerRepositoryLoadingTimeTest {
@Mock private ContainerBookingMapper mapper;
@InjectMocks private ContainerRepository repository;
@Test
void projectsPlanConfirmationTimeWhenCompletedBookingHasNoSchedule() {
LocalDateTime completedAt = LocalDateTime.of(2026, 8, 27, 10, 15);
ContainerBookingEntity booking = new ContainerBookingEntity();
booking.setPlanStatus("COMPLETED");
booking.setCompletedLoadingAt(completedAt);
when(mapper.selectBooking(7L)).thenReturn(booking);
ContainerBookingEntity result = repository.find(7L);
assertThat(result.getLoadingAt()).isEqualTo(completedAt);
}
@Test
void preservesExistingScheduleForUncompletedBooking() {
LocalDateTime plannedAt = LocalDateTime.of(2026, 8, 28, 9, 0);
ContainerBookingEntity booking = new ContainerBookingEntity();
booking.setPlanStatus("LOADING");
booking.setLoadingAt(plannedAt);
booking.setCompletedLoadingAt(LocalDateTime.of(2026, 8, 27, 10, 15));
when(mapper.selectBooking(8L)).thenReturn(booking);
ContainerBookingEntity result = repository.find(8L);
assertThat(result.getLoadingAt()).isEqualTo(plannedAt);
}
}
@@ -28,6 +28,9 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.Collection;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -184,6 +187,20 @@ class FbaPricingRepositoryRuleListTest {
.contains(10001L, 88L, "FEE_CONFIRMING", "CONFIRMED", "SENT", "WAIT_PALLET");
}
@Test
void feeConfirmationAllowsPostLoadingInboundStatuses() {
String source;
try {
source = Files.readString(Path.of(
"src/main/java/com/baiyue/firstleg/repository/FbaPricingRepository.java"),
StandardCharsets.UTF_8);
} catch (java.io.IOException exception) {
throw new AssertionError(exception);
}
assertThat(source)
.contains("List.of(\"FEE_CONFIRMING\", \"WAIT_SPLIT\", \"LOADING\", \"LOADED\", \"DELIVERED\")");
}
private Collection<Object> queryValues(Long tenantId, String ruleCode) {
when(rules.selectList(org.mockito.ArgumentMatchers.any())).thenReturn(List.of());
@@ -172,6 +172,33 @@ class FbaPricingServiceTest {
verify(repository).markInboundConfirmed(TENANT, INBOUND);
}
@Test
@DisplayName("装柜完成后的自动费用仍可确认")
void confirmsCalculatedFeeAfterLoadingCompleted() {
InboundPricingEntity inbound = inbound("10.000", "1.0000");
inbound.setProcessStatus("LOADED");
inbound.setFeeStatus("CALCULATED");
InboundFeeEntity fee = savedFee("CALCULATED");
when(repository.findLatestFee(TENANT, INBOUND)).thenReturn(fee);
when(repository.findIdempotency(TENANT, OPERATOR, "FEE_CONFIRM", "confirm-loaded"))
.thenReturn(null);
doAnswer(invocation -> {
((InboundPricingIdempotencyEntity) invocation.getArgument(0)).setId(900L);
return null;
}).when(repository).insertIdempotency(any());
when(repository.confirmFee(TENANT, 501L, OPERATOR, NOW)).thenReturn(1);
when(repository.markInboundConfirmed(TENANT, INBOUND)).thenReturn(1);
when(repository.completeIdempotency(any(), eq(501L), eq(NOW))).thenReturn(1);
when(repository.findFee(TENANT, 501L)).thenReturn(fee);
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
FeeSnapshotResponse response = service.confirm(
TENANT, OPERATOR, INBOUND, "confirm-loaded");
assertThat(response.nextProcessStatus()).isEqualTo("LOADED");
verify(repository).markInboundConfirmed(TENANT, INBOUND);
}
@Test
@DisplayName("审核未通过的测量快照禁止计费")
void rejectsUnapprovedMeasurementSnapshot() {
@@ -380,6 +380,30 @@ class SmartLoadingServiceTest {
anyLong(), anyLong(), anyLong(), anyString());
}
@Test
void reusesConfirmingPreloadWhenAddingToRunningPlan() {
LoadingPlanEntity loading = plan("LOADING", 5);
LoadingPlanEntity after = plan("LOADING", 6);
when(repository.plan(1)).thenReturn(loading, after);
when(repository.booking(99)).thenReturn(booking());
when(repository.touch(1, 5, 7, "u")).thenReturn(1);
when(repository.items(1)).thenReturn(List.of());
when(repository.candidate(1, 100, 10, 20)).thenReturn(candidate(10));
when(repository.activeItem(1L, 9L, 20L)).thenReturn(null);
when(loadingPickService.mutationSnapshot(1L)).thenReturn(
new LoadingPickService.MutationSnapshot(77L, "LOADING", 6, Map.of()));
when(cargoClient.detail(anyString(), anyLong(), anyLong(), anyLong())).thenReturn(ApiResponse.success(
new OrderCargoClient.CargoBinding(20L, 30L, 1, "goods", 1,
BigDecimal.ONE, BigDecimal.ONE, null, null, null, null, null, null)));
Plan result = service.assign(1, new AssignRequest(10L, 20L, 1, 5, "assign-confirming"), 7, "u");
assertThat(result.status()).isEqualTo("LOADING");
verify(splitPreloadService).ensureForRunningLoadingAllocation(9L, 10L, 7L, "u");
verify(loadingModelBridge).bindIncrementalAllocation(1L, 9L, 10L,
7L, "u", "assign-confirming");
}
@Test
void replaysBeforeVersionCheck() {
AssignRequest request = new AssignRequest(10L, 20L, 2, 0, "assign-replay");
@@ -129,13 +129,14 @@ class SplitPreloadServiceTest {
}
@Test
void runningLoadingAllocationStillRejectsConfirmingPreload() {
void runningLoadingAllocationMayReuseConfirmingPreload() {
when(repository.findInbound(10, 20)).thenReturn(new InboundSnapshot(20L, 10L, "LOADING", 3));
PreloadPlanEntity plan = new PreloadPlanEntity();
plan.setStatus("CONFIRMING");
when(repository.findPlan(10, 20)).thenReturn(plan);
assertThatThrownBy(() -> service.ensureForRunningLoadingAllocation(10, 20, 7, "admin"))
.hasMessageContaining("已进入确认流程");
service.ensureForRunningLoadingAllocation(10, 20, 7, "admin");
verify(repository, never()).createPlan(any());
}
}