Merge remote-tracking branch 'origin/test' into dev
This commit is contained in:
@@ -294,9 +294,9 @@ public class FbaPricingRepository {
|
||||
.eq(InboundPricingEntity::getTenantId, tenantId)
|
||||
.eq(InboundPricingEntity::getId, inboundId)
|
||||
.eq(InboundPricingEntity::getProcessStatus, processStatus)
|
||||
.in(InboundPricingEntity::getFeeStatus, List.of("UNPRICED", "CALCULATED"))
|
||||
.in(InboundPricingEntity::getFeeStatus, List.of("UNPRICED", "CALCULATED", "SENT"))
|
||||
.set(InboundPricingEntity::getFeeStatus, "CALCULATED");
|
||||
if (!"WAIT_SPLIT".equals(processStatus)) {
|
||||
if (!List.of("WAIT_PALLET", "WAIT_SPLIT", "LOADING").contains(processStatus)) {
|
||||
update.set(InboundPricingEntity::getProcessStatus, "FEE_CONFIRMING");
|
||||
}
|
||||
if ("KG".equals(chargeMode)) update.set(InboundPricingEntity::getChargeableWeightKg, chargeable);
|
||||
@@ -317,7 +317,7 @@ public class FbaPricingRepository {
|
||||
return inbounds.update(null, new LambdaUpdateWrapper<InboundPricingEntity>()
|
||||
.eq(InboundPricingEntity::getTenantId, tenantId).eq(InboundPricingEntity::getId, inboundId)
|
||||
.in(InboundPricingEntity::getProcessStatus,
|
||||
List.of("FEE_CONFIRMING", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED"))
|
||||
List.of("FEE_CONFIRMING", "WAIT_PALLET", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED"))
|
||||
.eq(InboundPricingEntity::getFeeStatus, "CALCULATED")
|
||||
.set(InboundPricingEntity::getFeeStatus, "CONFIRMED"));
|
||||
}
|
||||
|
||||
@@ -80,6 +80,16 @@ public class FbaPricingService {
|
||||
if ("COMPLETED".equals(token.getStatus())) return response(tenantId, token.getResultResourceId(), null);
|
||||
|
||||
InboundPricingEntity inbound = requireEligibleInbound(tenantId, inboundId);
|
||||
InboundFeeEntity published = null;
|
||||
if ("SENT".equals(inbound.getFeeStatus())) {
|
||||
published = repository.findLatestFeeForUpdate(tenantId, inboundId);
|
||||
if (published == null || !"SENT".equals(published.getFeeStatus())) {
|
||||
throw conflict("费用状态已变化,请刷新后重试");
|
||||
}
|
||||
if (published.getCustomerConfirmedAt() != null) {
|
||||
throw conflict("客户已确认该费用,不允许再修改或重新发送");
|
||||
}
|
||||
}
|
||||
var measurement = measurementRepository.findLatestMeasurement(tenantId, inboundId);
|
||||
if ("OUTER".equalsIgnoreCase(volumeSource) && measurement == null) {
|
||||
throw conflict("外箱规测量批次不存在,不能生成对客费用");
|
||||
@@ -114,7 +124,13 @@ public class FbaPricingService {
|
||||
weight, volume, converted, chargeable, subtotal, additional, total, now);
|
||||
fee.setMeasurementId(measurement == null ? null : measurement.getId());
|
||||
fee.setMeasurementSource("OUTER".equalsIgnoreCase(volumeSource) ? "OUTER" : volumeSource);
|
||||
repository.supersedeCalculated(tenantId, inboundId);
|
||||
if (published != null) {
|
||||
if (repository.supersedeCurrent(tenantId, published.getId(), "SENT") != 1) {
|
||||
throw conflict("费用已被客户确认或其他操作员修改,请刷新后重试");
|
||||
}
|
||||
} else {
|
||||
repository.supersedeCalculated(tenantId, inboundId);
|
||||
}
|
||||
repository.insertFee(fee);
|
||||
repository.insertItem(baseItem(tenantId, fee, rate, chargeable, subtotal, now));
|
||||
if (additional.signum() > 0) repository.insertItem(additionalItem(tenantId, fee, additional, now));
|
||||
@@ -124,7 +140,7 @@ public class FbaPricingService {
|
||||
}
|
||||
feeDetails.materializeCalculatedFreight(tenantId, inboundId, fee.getId(), operatorId, now);
|
||||
complete(token, fee.getId(), now);
|
||||
String nextStatus = isCompletedInbound(inbound.getProcessStatus())
|
||||
String nextStatus = preserveProcessStatusDuringPricing(inbound.getProcessStatus())
|
||||
? inbound.getProcessStatus() : "FEE_CONFIRMING";
|
||||
return response(inbound, fee, rate.getUnitPrice(), nextStatus);
|
||||
}
|
||||
@@ -163,7 +179,7 @@ public class FbaPricingService {
|
||||
String hash = hash("SEND", inboundId, current.getId().toString());
|
||||
InboundPricingIdempotencyEntity token = beginIdempotency(
|
||||
tenantId, operatorId, inboundId, "FEE_SEND", idempotencyKey, hash);
|
||||
String nextProcessStatus = isCompletedInbound(inbound.getProcessStatus())
|
||||
String nextProcessStatus = preserveProcessStatusDuringPricing(inbound.getProcessStatus())
|
||||
? inbound.getProcessStatus() : "WAIT_PALLET";
|
||||
if ("COMPLETED".equals(token.getStatus())) {
|
||||
feeDetails.publishFreight(tenantId, inboundId, token.getResultResourceId(),
|
||||
@@ -311,7 +327,7 @@ public class FbaPricingService {
|
||||
private InboundPricingEntity requireEligibleInbound(long tenantId, long inboundId) {
|
||||
InboundPricingEntity inbound = requireApprovedInbound(tenantId, inboundId);
|
||||
if (!feeEditableBeforeLoading(inbound.getProcessStatus())
|
||||
|| !List.of("UNPRICED", "CALCULATED").contains(inbound.getFeeStatus())) {
|
||||
|| !List.of("UNPRICED", "CALCULATED", "SENT").contains(inbound.getFeeStatus())) {
|
||||
throw conflict("当前入库单状态不允许费用试算");
|
||||
}
|
||||
return inbound;
|
||||
@@ -336,9 +352,13 @@ public class FbaPricingService {
|
||||
return "WAIT_SPLIT".equals(processStatus);
|
||||
}
|
||||
|
||||
private static boolean preserveProcessStatusDuringPricing(String processStatus) {
|
||||
return List.of("WAIT_PALLET", "WAIT_SPLIT", "LOADING").contains(processStatus);
|
||||
}
|
||||
|
||||
private static boolean feeEditableBeforeLoading(String processStatus) {
|
||||
return List.of("WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED",
|
||||
"WAIT_PRICING", "FEE_CONFIRMING", "WAIT_SPLIT").contains(processStatus);
|
||||
"WAIT_PRICING", "FEE_CONFIRMING", "WAIT_PALLET", "WAIT_SPLIT", "LOADING").contains(processStatus);
|
||||
}
|
||||
|
||||
private static PricingDestinationType destinationType(InboundPricingEntity inbound) {
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ class FbaPricingRepositoryRuleListTest {
|
||||
throw new AssertionError(exception);
|
||||
}
|
||||
assertThat(source)
|
||||
.contains("List.of(\"FEE_CONFIRMING\", \"WAIT_SPLIT\", \"LOADING\", \"LOADED\", \"DELIVERED\")");
|
||||
.contains("List.of(\"FEE_CONFIRMING\", \"WAIT_PALLET\", \"WAIT_SPLIT\", \"LOADING\", \"LOADED\", \"DELIVERED\")");
|
||||
}
|
||||
|
||||
private Collection<Object> queryValues(Long tenantId, String ruleCode) {
|
||||
|
||||
@@ -242,6 +242,54 @@ class FbaPricingServiceTest {
|
||||
verify(priceBookEstimator).estimate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("客户未确认时待装柜订单可重新计算并替换已发送费用")
|
||||
void recalculatesSentFeeBeforeCustomerConfirmation() {
|
||||
readyRepository();
|
||||
InboundPricingEntity inbound = inbound("10", "1");
|
||||
inbound.setProcessStatus("WAIT_PALLET");
|
||||
inbound.setFeeStatus("SENT");
|
||||
InboundFeeEntity sent = savedFee("SENT");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
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(
|
||||
estimate("CBM", "1.0000", "10", "10", "0"));
|
||||
|
||||
FeeSnapshotResponse response = service.calculate(
|
||||
TENANT, OPERATOR, INBOUND, NOW, "resend-before-confirm");
|
||||
|
||||
assertThat(response.feeVersion()).isEqualTo(2);
|
||||
assertThat(response.feeStatus()).isEqualTo("CALCULATED");
|
||||
assertThat(response.nextProcessStatus()).isEqualTo("WAIT_PALLET");
|
||||
verify(repository).supersedeCurrent(TENANT, 501L, "SENT");
|
||||
verify(repository).markInboundCalculated(TENANT, INBOUND, "WAIT_PALLET", "CBM",
|
||||
new BigDecimal("1.0000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("客户已确认时拒绝重新计算已发送费用")
|
||||
void rejectsRecalculationAfterCustomerConfirmation() {
|
||||
readyRepository();
|
||||
InboundPricingEntity inbound = inbound("10", "1");
|
||||
inbound.setProcessStatus("WAIT_PALLET");
|
||||
inbound.setFeeStatus("SENT");
|
||||
InboundFeeEntity sent = savedFee("SENT");
|
||||
sent.setCustomerConfirmedAt(NOW);
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(repository.findLatestFeeForUpdate(TENANT, INBOUND)).thenReturn(sent);
|
||||
|
||||
assertThatThrownBy(() -> service.calculate(
|
||||
TENANT, OPERATOR, INBOUND, NOW, "resend-after-confirm"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("客户已确认");
|
||||
|
||||
verify(priceBookEstimator, never()).estimate(any());
|
||||
verify(repository, never()).supersedeCurrent(anyLong(), anyLong(), any());
|
||||
verify(repository, never()).insertFee(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("当前生效价格表无匹配价格时明确拒绝")
|
||||
void rejectsMissingActivePriceBookRate() {
|
||||
|
||||
Reference in New Issue
Block a user