支持私人地址人工头程报价以闭合预装费用前置

This commit is contained in:
2026-11-04 12:10:14 +08:00
parent 78ecd2e039
commit 6e451a82da
4 changed files with 116 additions and 21 deletions
@@ -483,6 +483,83 @@ class FbaPricingServiceTest {
assertThat(feeItem.getValue().getRemark()).isEqualTo("FBA价格未维护");
}
@Test
void previewsPrivateAddressManualQuantityWithoutLookingUpWarehouseRates() {
InboundPricingEntity inbound = inbound("62.500", "0.3840");
inbound.setDestinationType("PRIVATE");
inbound.setDestinationCode(null);
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
var result = service.preview(TENANT, INBOUND, new FeePreviewRequest(NOW,
new BigDecimal("62.500"), new BigDecimal("0.3840"), null, null, new BigDecimal("12.2")));
assertThat(result.priceMatched()).isFalse();
assertThat(result.chargeableQuantity()).isEqualByComparingTo("64.128000");
assertThat(result.totalAmount()).isEqualByComparingTo("782.36");
org.mockito.Mockito.verifyNoInteractions(priceBookEstimator, feeDetails, financeOutbox);
verify(repository, never()).insertFee(any());
}
@Test
void createsPrivateAddressManualQuoteAndPreservesWaitSplit() {
readyRepository();
InboundPricingEntity inbound = inbound("62.500", "0.3840");
inbound.setDestinationType("PRIVATE");
inbound.setDestinationCode(null);
inbound.setProcessStatus("WAIT_SPLIT");
InboundMeasurementEntity measurement = new InboundMeasurementEntity();
measurement.setId(301L);
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
when(measurementRepository.findLatestMeasurement(TENANT, INBOUND)).thenReturn(measurement);
when(measurementRepository.listItems(TENANT, 301L)).thenReturn(List.of(new InboundMeasurementItemEntity()));
var request = new ManualQuoteRequest(new BigDecimal("62.500"), new BigDecimal("0.3840"),
new BigDecimal("12.2"), "私人地址人工报价");
var result = service.manualQuote(TENANT, OPERATOR, INBOUND, request, "private-manual");
assertThat(result.rateCode()).isEqualTo("MANUAL_QUOTE");
assertThat(result.feeStatus()).isEqualTo("CALCULATED");
assertThat(result.nextProcessStatus()).isEqualTo("WAIT_SPLIT");
assertThat(result.totalAmount()).isEqualByComparingTo("782.36");
ArgumentCaptor<InboundFeeEntity> fee = ArgumentCaptor.forClass(InboundFeeEntity.class);
verify(repository).insertFee(fee.capture());
assertThat(fee.getValue().getDestinationType()).isEqualTo("PRIVATE");
assertThat(fee.getValue().getCustomerId()).isEqualTo(TENANT);
assertThat(fee.getValue().getAdditionalAmount()).isEqualByComparingTo("0");
ArgumentCaptor<InboundFeeItemEntity> item = ArgumentCaptor.forClass(InboundFeeItemEntity.class);
verify(repository).insertItem(item.capture());
assertThat(item.getValue().getFeeName()).isEqualTo("头程基础运费");
assertThat(item.getValue().getAutomaticFlag()).isFalse();
org.mockito.Mockito.verifyNoInteractions(priceBookEstimator, financeOutbox);
ArgumentCaptor<InboundPricingIdempotencyEntity> command =
ArgumentCaptor.forClass(InboundPricingIdempotencyEntity.class);
verify(repository).insertIdempotency(command.capture());
command.getValue().setStatus("COMPLETED");
command.getValue().setResultResourceId(result.feeId());
when(repository.findIdempotency(TENANT, OPERATOR, "FEE_MANUAL_QUOTE", "private-manual"))
.thenReturn(command.getValue());
when(repository.findFee(TENANT, result.feeId())).thenReturn(fee.getValue());
when(repository.findBaseItem(TENANT, result.feeId())).thenReturn(item.getValue());
var replay = service.manualQuote(TENANT, OPERATOR, INBOUND, request, "private-manual");
assertThat(replay.feeId()).isEqualTo(result.feeId());
verify(repository, times(1)).insertFee(any());
verify(repository, times(1)).insertIdempotency(any());
}
@Test
void rejectsAutomaticPricingForPrivateAddress() {
readyRepository();
InboundPricingEntity inbound = inbound("62.500", "0.3840");
inbound.setDestinationType("PRIVATE");
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
assertThatThrownBy(() -> service.calculate(TENANT, OPERATOR, INBOUND, NOW, "private-auto"))
.isInstanceOf(BizException.class).hasMessageContaining("私人地址请使用人工头程报价");
verify(repository, never()).insertFee(any());
org.mockito.Mockito.verifyNoInteractions(priceBookEstimator);
}
@Test
@DisplayName("已有匹配价格时拒绝绕过价格表创建人工报价")
void rejectsManualQuoteWhenRateExists() {