This commit is contained in:
2026-10-20 23:49:55 +08:00
parent 35a1b835fe
commit af5cf1e3bd
37 changed files with 714 additions and 184 deletions
@@ -462,8 +462,9 @@ class SmartLoadingServiceTest {
verify(repository).occupiedOrderIds(eq(query), anySet());
}
/** 待审核订单来自其他客户时仍可拼柜,客户归属和费用重算维持原值。 */
@Test
void manuallyAssignsCrossChannelPendingReviewOrderAsAProvisionalContainerAllocation() {
void manuallyAssignsCrossCustomerAndChannelPendingReviewOrderAsAProvisionalContainerAllocation() {
long customerId = 2092797167699816450L;
long orderId = 2095052002489335810L;
String inboundCode = "EXXHK2609020361";
@@ -503,6 +504,7 @@ class SmartLoadingServiceTest {
assertThat(result.version()).isEqualTo(1);
ArgumentCaptor<LoadingPlanItemEntity> item = ArgumentCaptor.forClass(LoadingPlanItemEntity.class);
verify(repository).insert(item.capture());
assertThat(item.getValue().getAccountId()).isEqualTo(customerId);
assertThat(item.getValue().getInboundId()).isEqualTo(10L);
assertThat(item.getValue().getInboundDetailId()).isEqualTo(20L);
assertThat(item.getValue().getPackageCount()).isEqualTo(5);
@@ -1378,20 +1380,31 @@ class SmartLoadingServiceTest {
verify(repository, never()).touch(anyLong(), anyInt(), anyLong(), any());
}
/** 订舱客户仅为关联资料;平台可将其他客户的货物加入同一柜次。 */
@Test
void rejectsManualAssignmentOutsideLinkedBookingCustomerScope() {
LoadingPlanEntity plan = plan("EDITING", 0);
ContainerBookingEntity linked = booking();
linked.setCustomerId(88L);
when(repository.plan(1L)).thenReturn(plan);
when(repository.booking(99L)).thenReturn(linked);
when(repository.candidate(1L, 10L, 20L)).thenReturn(candidate(5));
void allowsManualAssignmentFromAnotherCustomerWithoutRewritingCargoOwnership() {
stubAssignment();
BizException error = catchThrowableOfType(() -> service.assign(1L,
new AssignRequest(10L, 20L, 1, 0, "assign-customer-mismatch"), 7L, "u"), BizException.class);
Plan result = service.assign(1L,
new AssignRequest(10L, 20L, 1, 0, "assign-cross-customer"), 7L, "u");
assertThat(error.getResultCode()).isEqualTo(ResultCode.FORBIDDEN);
verify(repository, never()).touch(anyLong(), anyInt(), anyLong(), any());
assertThat(result.version()).isEqualTo(1);
ArgumentCaptor<LoadingPlanItemEntity> inserted = ArgumentCaptor.forClass(LoadingPlanItemEntity.class);
verify(repository).insert(inserted.capture());
assertThat(inserted.getValue().getAccountId()).isEqualTo(9L);
assertThat(inserted.getValue().getOrderId()).isEqualTo(30L);
verify(cargoClient).detail(anyString(), eq(9L), eq(30L), eq(20L));
}
/** 同渠道候选包含不同客户,不能被订舱客户快照裁剪。 */
@Test
void listsCandidatesFromBothCustomersInALinkedBooking() {
when(repository.booking(99L)).thenReturn(booking());
when(repository.candidates(100L)).thenReturn(List.of(
candidate(9L, 10L, 20L, 30L, 5), candidate(88L, 11L, 21L, 31L, 4)));
assertThat(service.candidates(99L, 100L)).extracting(Candidate::accountId)
.containsExactly(9L, 88L);
}
@Test
@@ -2075,9 +2088,11 @@ class SmartLoadingServiceTest {
return group;
}
/** 所有流程默认带历史订舱客户,覆盖候选、占位、整托、调整和推荐的跨客户拼柜。 */
private static ContainerBookingEntity booking() {
ContainerBookingEntity value = new ContainerBookingEntity();
value.setId(99L); value.setWarehouseId(100L); value.setDestinationWarehouseCode("FBA1");
value.setCustomerId(88L);
value.setChannelGroupCode("MATSON_CLX"); value.setChannelCode(null);
value.setContainerNo("MSCU1234567");
value.setCapacityCbm(new BigDecimal("60")); value.setMaxWeightKg(new BigDecimal("20000"));