This commit is contained in:
2026-08-28 13:54:32 +08:00
parent fe68551124
commit 6b5f2f0565
29 changed files with 959 additions and 178 deletions
@@ -39,7 +39,8 @@ class ReceivingContainerControllerTest {
@Test
void candidatesDerivesOperatorFromPlatformWarehousePickerIdentity() {
when(service.candidates("42", 900L)).thenReturn(List.of(new ReceivingContainerResponses.Candidate(
"CONT-1", "WAIT_SIGN", 2, 2, List.of("ONT8"), 4, 20, 0, false)));
"BOOKING-1", "CONT-1", "WAIT_SIGN", 2, 2, 0, 2, 2, 0,
List.of("ONT8"), 4, 20, 0, true, true, null, null, false)));
controller.candidates(picker());
@@ -87,11 +87,18 @@ class ReceivingContainerServiceBehaviorTest {
}
@Test
void containerIsHiddenAndPrepareRejectedBeforePickupReady() {
void containerRemainsVisibleButPrepareIsRejectedBeforePickupReady() {
when(plans.selectList(any())).thenReturn(List.of(plan("CONT-A")));
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
LastlegOrder order = containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1");
when(orders.selectList(any())).thenReturn(List.of(order));
when(shippingGates.receivingAvailable("100", "CONT-A")).thenReturn(0);
assertThat(service.candidates("7", 100L)).isEmpty();
assertThat(service.candidates("7", 100L)).singleElement().satisfies(candidate -> {
assertThat(candidate.operable()).isFalse();
assertThat(candidate.allPlansReady()).isTrue();
assertThat(candidate.blockReason()).contains("尚未达到");
});
assertThatThrownBy(() -> service.prepare("7", "receiver", 100L, "CONT-A",
new ReceivingContainerRequests.Action("not-ready")))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
@@ -101,7 +108,10 @@ class ReceivingContainerServiceBehaviorTest {
@Test
void prepareIdempotencyHashIncludesContainerPath() {
when(plans.selectList(any())).thenReturn(List.of(plan("CONT-A")));
when(lines.selectList(any())).thenReturn(List.of());
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
LastlegOrder order = containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1");
when(orders.selectList(any())).thenReturn(List.of(order));
when(orders.selectById(1L)).thenReturn(order);
when(sessions.insert(any(ReceivingContainerSession.class))).thenReturn(1);
when(operations.insert(any(ReceivingContainerOperation.class))).thenReturn(1);
when(warehouse.requireActiveSharedWarehouse(100L)).thenReturn(warehouseReference());
@@ -132,14 +142,23 @@ class ReceivingContainerServiceBehaviorTest {
InboundPlan second = plan("CONT-B"); second.setId(3L); second.setCustomerId("200");
when(plans.selectList(any())).thenReturn(List.of(first, second), List.of(first), List.of(second));
when(lines.selectList(any())).thenReturn(List.of());
when(orders.selectList(any())).thenReturn(List.of(
containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1"),
containerOrder(2L, "200", "CONT-B", "BOOKING-B", "IN-2")));
assertThat(service.candidates("7", 100L)).extracting("containerNo").containsExactly("CONT-A", "CONT-B");
InboundPlan ambiguous = plan("CONT-A"); ambiguous.setId(4L); ambiguous.setCustomerId("200");
org.mockito.Mockito.reset(plans, sessions);
when(plans.selectList(any())).thenReturn(List.of(first, ambiguous));
assertThatThrownBy(() -> service.candidates("7", 100L)).isInstanceOf(ResponseStatusException.class)
.hasMessageContaining("exactly one customer");
when(lines.selectList(any())).thenReturn(List.of());
when(orders.selectList(any())).thenReturn(List.of(
containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1"),
containerOrder(2L, "200", "CONT-A", "BOOKING-A", "IN-2")));
assertThat(service.candidates("7", 100L)).singleElement().satisfies(candidate -> {
assertThat(candidate.operable()).isFalse();
assertThat(candidate.blockReason()).contains("未操作/缺入仓码");
});
}
@Test
@@ -150,6 +169,12 @@ class ReceivingContainerServiceBehaviorTest {
when(plans.selectList(any())).thenReturn(List.of(unassigned, same, other),
List.of(unassigned), List.of(same));
when(lines.selectList(any())).thenReturn(List.of());
LastlegOrder orderA = containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1");
LastlegOrder orderB = containerOrder(2L, "100", "CONT-B", "BOOKING-B", "IN-2");
orderB.setOverseasWarehouseId("100");
LastlegOrder orderC = containerOrder(3L, "100", "CONT-C", "BOOKING-C", "IN-3");
orderC.setOverseasWarehouseId("200");
when(orders.selectList(any())).thenReturn(List.of(orderA, orderB, orderC));
assertThat(service.candidates("7", 100L)).extracting("containerNo")
.containsExactly("CONT-A", "CONT-B");
@@ -164,11 +189,36 @@ class ReceivingContainerServiceBehaviorTest {
other.setActiveContainerKey("CONTAINER:CONT-B");
when(plans.selectList(any())).thenReturn(List.of(available, reserved), List.of(available));
when(lines.selectList(any())).thenReturn(List.of());
when(orders.selectList(any())).thenReturn(List.of(
containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1"),
containerOrder(2L, "100", "CONT-B", "BOOKING-B", "IN-2")));
when(sessions.findActiveContainers()).thenReturn(List.of(other));
assertThat(service.candidates("7", 100L)).extracting("containerNo").containsExactly("CONT-A");
}
@Test
void partialContainerPlansRemainVisibleAndPrepareIsHardRejected() {
InboundPlan firstPlan = plan("CONT-A");
when(plans.selectList(any())).thenReturn(List.of(firstPlan));
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
when(orders.selectList(any())).thenReturn(List.of(
containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1"),
containerOrder(2L, "100", "CONT-A", "BOOKING-A", "IN-2")));
var candidate = service.candidates("7", 100L).get(0);
assertThat(candidate.totalOrderCount()).isEqualTo(2);
assertThat(candidate.plannedInboundCodeCount()).isEqualTo(1);
assertThat(candidate.pendingPlanCount()).isEqualTo(1);
assertThat(candidate.operable()).isFalse();
assertThat(candidate.blockReason()).isEqualTo("1个入仓码已生成计划,1个订单未操作/缺入仓码");
assertThatThrownBy(() -> service.prepare("7", "receiver", 100L, "CONT-A",
new ReceivingContainerRequests.Action("partial")))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
verify(sessions, never()).insert(any(ReceivingContainerSession.class));
}
@Test
void detailRejectsContainerReservedByAnotherOperator() {
when(plans.selectList(any())).thenReturn(List.of(plan("CONT-B")));
@@ -193,6 +243,9 @@ class ReceivingContainerServiceBehaviorTest {
@Test
void prepareEnforcesBothActiveAccountAndActiveContainerUniqueness() {
when(plans.selectList(any())).thenReturn(List.of(plan("CONT-B")));
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
LastlegOrder order = containerOrder(1L, "100", "CONT-B", "BOOKING-B", "IN-1");
when(orders.selectList(any())).thenReturn(List.of(order));
when(sessions.lockActiveByOperator("OPERATOR:7")).thenReturn(session("CONT-A", "WAIT_SIGN"));
assertThatThrownBy(() -> service.prepare("7", "receiver", 100L, "CONT-B",
new ReceivingContainerRequests.Action("account-lock"))).isInstanceOf(ResponseStatusException.class);
@@ -575,6 +628,14 @@ class ReceivingContainerServiceBehaviorTest {
return value;
}
private static LastlegOrder containerOrder(long id, String customerId, String containerNo,
String bookingId, String inboundCode) {
LastlegOrder value = order("FBA");
value.setId(id); value.setCustomerId(customerId); value.setContainerNo(containerNo);
value.setSourceContainerBookingId(bookingId); value.setInboundCode(inboundCode);
return value;
}
private static LastlegOrderDetail detail(String skuCode, int packages) {
LastlegOrderDetail value = new LastlegOrderDetail();
value.setId(11L); value.setCustomerId("100"); value.setOrderId(1L);