feat(firstleg): support customer price books
This commit is contained in:
+3
@@ -25,11 +25,14 @@ class PriceBookQueryServiceTest {
|
||||
void delegatesStableReadOperationsToTheOutputPort() {
|
||||
ChannelPrefixRow prefix = new ChannelPrefixRow("SEA", "IN", "OUT");
|
||||
when(reader.findActiveVersionId()).thenReturn(9L);
|
||||
when(reader.findEffectiveVersionId(10001L)).thenReturn(10L);
|
||||
when(reader.findChannelPrefix("SEA")).thenReturn(prefix);
|
||||
|
||||
assertThat(service.activeVersionId()).isEqualTo(9L);
|
||||
assertThat(service.effectiveVersionId(10001L)).isEqualTo(10L);
|
||||
assertThat(service.channelPrefix("SEA")).isSameAs(prefix);
|
||||
verify(reader).findActiveVersionId();
|
||||
verify(reader).findEffectiveVersionId(10001L);
|
||||
verify(reader).findChannelPrefix("SEA");
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ class FbaPricingServiceTest {
|
||||
inbound.setCarrierId(null);
|
||||
inbound.setCarrierName(null);
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("KG", "167.017", "2.345678", "391.77", "10.00"));
|
||||
|
||||
FeeSnapshotResponse response = service.calculate(TENANT, OPERATOR, INBOUND, NOW, "calc-1");
|
||||
@@ -164,7 +164,7 @@ class FbaPricingServiceTest {
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(measurementRepository.findLatestMeasurement(TENANT, INBOUND)).thenReturn(measurement);
|
||||
when(measurementRepository.listItems(TENANT, 301L)).thenReturn(List.of(item));
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("CBM", "1.0019", "100", "100.19", "0"));
|
||||
|
||||
service.calculate(TENANT, OPERATOR, INBOUND, NOW,
|
||||
@@ -202,7 +202,7 @@ class FbaPricingServiceTest {
|
||||
InboundPricingEntity inbound = inbound("200.000", "0.2000");
|
||||
inbound.setCustomerId(null);
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("KG", "200.000", "2.000000", "400.00", "0.00"));
|
||||
|
||||
assertThat(service.calculate(TENANT, OPERATOR, INBOUND, NOW, "calc-no-customer"))
|
||||
@@ -223,7 +223,7 @@ class FbaPricingServiceTest {
|
||||
rule.setId(601L); rule.setRuleCode("CBM-RULE"); rule.setRuleVersion(1);
|
||||
rule.setChargeMode("CBM"); rule.setDefaultRule(true);
|
||||
when(repository.matchRules(eq(inbound), any())).thenReturn(List.of(rule));
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("CBM", "1.0001", "100.000000", "100.01", "0.00"));
|
||||
|
||||
FeeSnapshotResponse response = service.calculate(TENANT, OPERATOR, INBOUND, NOW, "calc-2");
|
||||
@@ -245,7 +245,7 @@ class FbaPricingServiceTest {
|
||||
InboundPricingEntity inbound = inbound("100.000", "1.0000");
|
||||
inbound.setProcessStatus("WAIT_SPLIT");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("KG", "167.000", "2.000000", "334.00", "0.00"));
|
||||
|
||||
FeeSnapshotResponse response = service.calculate(TENANT, OPERATOR, INBOUND, NOW, "wait-split");
|
||||
@@ -304,11 +304,11 @@ class FbaPricingServiceTest {
|
||||
InboundPricingEntity inbound = inbound("10", "1");
|
||||
inbound.setMeasureReviewStatus("REJECTED");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("CBM", "1.0000", "10", "10", "0"));
|
||||
|
||||
assertThat(service.calculate(TENANT, OPERATOR, INBOUND, NOW, "calc-3")).isNotNull();
|
||||
verify(priceBookEstimator).estimate(any());
|
||||
verify(priceBookEstimator).estimate(eq(TENANT), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -323,7 +323,7 @@ class FbaPricingServiceTest {
|
||||
when(repository.findLatestFeeForUpdate(TENANT, INBOUND)).thenReturn(sent);
|
||||
when(repository.nextFeeVersion(TENANT, INBOUND)).thenReturn(2);
|
||||
when(repository.supersedeCurrent(TENANT, 501L, "SENT")).thenReturn(1);
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenReturn(
|
||||
estimate("CBM", "1.0000", "10", "10", "0"));
|
||||
|
||||
FeeSnapshotResponse response = service.calculate(
|
||||
@@ -365,7 +365,7 @@ class FbaPricingServiceTest {
|
||||
readyRepository();
|
||||
InboundPricingEntity inbound = inbound("10", "1");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(priceBookEstimator.estimate(any())).thenThrow(
|
||||
when(priceBookEstimator.estimate(anyLong(), any())).thenThrow(
|
||||
new BizException(ResultCode.NOT_FOUND, "当前条件没有匹配价格"));
|
||||
|
||||
assertThatThrownBy(() -> service.calculate(TENANT, OPERATOR, INBOUND, NOW, "none"))
|
||||
|
||||
+10
-9
@@ -29,6 +29,7 @@ import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -44,7 +45,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldResolveNormalAndOverseasInboundCodePrefixesFromChannelCatalog() {
|
||||
when(repository.activeVersionId()).thenReturn(7L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(7L);
|
||||
when(repository.channelAvailable(7L, 10L, "MATSON_CLX_TRUCK")).thenReturn(true);
|
||||
when(repository.channels(7L, 10L)).thenReturn(List.of(
|
||||
channel(7L, "MATSON_CLX_TRUCK", PriceModel.FBA_WAREHOUSE_RATE)));
|
||||
@@ -59,7 +60,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldFallBackToNormalPrefixWhenChannelHasNoOverseasVariant() {
|
||||
when(repository.activeVersionId()).thenReturn(7L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(7L);
|
||||
when(repository.channelAvailable(7L, 10L, "OA_HOUSTON_TRUCK")).thenReturn(true);
|
||||
when(repository.channels(7L, 10L)).thenReturn(List.of(
|
||||
channel(7L, "OA_HOUSTON_TRUCK", PriceModel.FBA_WAREHOUSE_RATE)));
|
||||
@@ -72,7 +73,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldValidateLegacyChannelWithoutRequiringAnInboundCodePrefix() {
|
||||
when(repository.activeVersionId()).thenReturn(7L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(7L);
|
||||
when(repository.channelAvailable(7L, 10L, "LEGACY_CHANNEL")).thenReturn(true);
|
||||
var response = service.requireAvailableChannel(
|
||||
10001L, 10L, "LEGACY_CHANNEL", "FBA", false);
|
||||
@@ -84,7 +85,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyChannelsWithoutActiveVersion() {
|
||||
when(repository.activeVersionId()).thenReturn(null);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(null);
|
||||
|
||||
assertThat(service.channels(10001L, 10L)).isEmpty();
|
||||
verify(repository, never()).channels(1L, 10L);
|
||||
@@ -93,7 +94,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldExposeOnlyChannelsFromLatestActiveVersionAfterSwitch() {
|
||||
when(repository.activeVersionId()).thenReturn(1L, 2L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(1L, 2L);
|
||||
when(repository.channels(1L, 10L)).thenReturn(List.of(channel(1L, "OLD", PriceModel.FBA_WAREHOUSE_RATE)));
|
||||
when(repository.channels(2L, 10L)).thenReturn(List.of(channel(2L, "NEW", PriceModel.FBA_WAREHOUSE_RATE)));
|
||||
|
||||
@@ -105,7 +106,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldExposeDistinctPricingTaxModesForActiveOriginChannels() {
|
||||
when(repository.activeVersionId()).thenReturn(7L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(7L);
|
||||
when(repository.channels(7L, 10L)).thenReturn(List.of(
|
||||
channel(7L, "MATSON_CLX_TRUCK", PriceModel.FBA_WAREHOUSE_RATE),
|
||||
channel(7L, "OA_PARCEL", PriceModel.REGION_WEIGHT_TIER)));
|
||||
@@ -124,7 +125,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldKeepActiveChannelWhenItsTaxModeProjectionIsEmpty() {
|
||||
when(repository.activeVersionId()).thenReturn(7L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(7L);
|
||||
when(repository.channels(7L, 10L)).thenReturn(List.of(
|
||||
channel(7L, "MATSON_CLX_TRUCK", PriceModel.FBA_WAREHOUSE_RATE)));
|
||||
when(repository.channelTaxModes(7L, 10L)).thenReturn(List.of());
|
||||
@@ -136,7 +137,7 @@ class MerchantPriceBookServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldRejectForgedOrRemovedChannel() {
|
||||
when(repository.activeVersionId()).thenReturn(2L);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(2L);
|
||||
when(repository.channels(2L, 10L)).thenReturn(List.of(channel(2L, "ACTIVE_CHANNEL",
|
||||
PriceModel.FBA_WAREHOUSE_RATE)));
|
||||
|
||||
@@ -282,7 +283,7 @@ class MerchantPriceBookServiceTest {
|
||||
}
|
||||
|
||||
private void stubChannel(long versionId, String code, PriceModel model) {
|
||||
when(repository.activeVersionId()).thenReturn(versionId);
|
||||
when(repository.effectiveVersionId(anyLong())).thenReturn(versionId);
|
||||
when(repository.channels(versionId, 10L)).thenReturn(List.of(channel(versionId, code, model)));
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -18,6 +18,7 @@ import java.math.BigDecimal;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -48,7 +49,7 @@ class OrderChannelChangePricingServiceTest {
|
||||
InboundPricingEntity inbound = inbound("CALCULATED");
|
||||
when(repository.findInboundByOrder(10001L, 77L)).thenReturn(inbound);
|
||||
when(repository.findLatestFee(10001L, 88L)).thenReturn(fee(91L, "CALCULATED"));
|
||||
when(estimator.estimate(any())).thenReturn(estimate());
|
||||
when(estimator.estimate(anyLong(), any())).thenReturn(estimate());
|
||||
|
||||
var response = service.validate(request(4));
|
||||
|
||||
@@ -64,7 +65,7 @@ class OrderChannelChangePricingServiceTest {
|
||||
InboundFeeEntity fee = fee(91L, "CALCULATED");
|
||||
when(repository.findInboundByOrderForUpdate(10001L, 77L)).thenReturn(inbound);
|
||||
when(repository.findLatestFeeForUpdate(10001L, 88L)).thenReturn(fee);
|
||||
when(estimator.estimate(any())).thenReturn(estimate());
|
||||
when(estimator.estimate(anyLong(), any())).thenReturn(estimate());
|
||||
when(repository.supersedeCurrent(10001L, 91L, "CALCULATED")).thenReturn(1);
|
||||
when(repository.applyOrderChannelChange(inbound, "MATSON_NEW", "新渠道")).thenReturn(1);
|
||||
when(repository.completeIdempotency(any(), org.mockito.ArgumentMatchers.eq(4L), any())).thenReturn(1);
|
||||
@@ -88,7 +89,7 @@ class OrderChannelChangePricingServiceTest {
|
||||
token.setRequestHash(requestHash(request(4)));
|
||||
when(repository.findInboundByOrderForUpdate(10001L, 77L)).thenReturn(inbound);
|
||||
when(repository.findIdempotency(10001L, 0L, "ORDER_CHANNEL_CHANGE", "77:4")).thenReturn(token);
|
||||
when(estimator.estimate(any())).thenReturn(estimate());
|
||||
when(estimator.estimate(anyLong(), any())).thenReturn(estimate());
|
||||
|
||||
assertThat(service.apply(request(4)).orderVersion()).isEqualTo(4L);
|
||||
|
||||
|
||||
+17
-1
@@ -64,6 +64,22 @@ class PlatformPriceBookServiceTest {
|
||||
ReflectionTestUtils.setField(service, "internalToken", "internal-token");
|
||||
}
|
||||
|
||||
@Test
|
||||
void listsCustomerSpecificVersionsWithinRequestedScope() {
|
||||
PriceBookVersionEntity customerVersion = version(21L, PriceBookStatus.ACTIVE);
|
||||
customerVersion.setTenantId(10001L);
|
||||
when(repository.listVersions(10001L, PriceBookStatus.ACTIVE.name()))
|
||||
.thenReturn(List.of(customerVersion));
|
||||
|
||||
var result = service.list(10001L, PriceBookStatus.ACTIVE);
|
||||
|
||||
assertThat(result).singleElement().satisfies(summary -> {
|
||||
assertThat(summary.id()).isEqualTo(21L);
|
||||
assertThat(summary.customerId()).isEqualTo(10001L);
|
||||
});
|
||||
verify(repository).listVersions(10001L, PriceBookStatus.ACTIVE.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fileServiceFailureMarksAllocatedVersionFailed() {
|
||||
executeTransactionsImmediately();
|
||||
@@ -121,7 +137,7 @@ class PlatformPriceBookServiceTest {
|
||||
assertThat(Arrays.stream(PlatformPriceBookService.class.getDeclaredMethods())
|
||||
.filter(method -> java.lang.reflect.Modifier.isPublic(method.getModifiers()))
|
||||
.map(java.lang.reflect.Method::getName).toList())
|
||||
.containsExactlyInAnyOrder("list", "upload", "detail", "publish", "source");
|
||||
.containsOnly("list", "upload", "detail", "publish", "source");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user