修改提交
This commit is contained in:
@@ -294,9 +294,9 @@ public class FbaPricingRepository {
|
|||||||
.eq(InboundPricingEntity::getTenantId, tenantId)
|
.eq(InboundPricingEntity::getTenantId, tenantId)
|
||||||
.eq(InboundPricingEntity::getId, inboundId)
|
.eq(InboundPricingEntity::getId, inboundId)
|
||||||
.eq(InboundPricingEntity::getProcessStatus, processStatus)
|
.eq(InboundPricingEntity::getProcessStatus, processStatus)
|
||||||
.in(InboundPricingEntity::getFeeStatus, List.of("UNPRICED", "CALCULATED"))
|
.in(InboundPricingEntity::getFeeStatus, List.of("UNPRICED", "CALCULATED", "SENT"))
|
||||||
.set(InboundPricingEntity::getFeeStatus, "CALCULATED");
|
.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");
|
update.set(InboundPricingEntity::getProcessStatus, "FEE_CONFIRMING");
|
||||||
}
|
}
|
||||||
if ("KG".equals(chargeMode)) update.set(InboundPricingEntity::getChargeableWeightKg, chargeable);
|
if ("KG".equals(chargeMode)) update.set(InboundPricingEntity::getChargeableWeightKg, chargeable);
|
||||||
@@ -317,7 +317,7 @@ public class FbaPricingRepository {
|
|||||||
return inbounds.update(null, new LambdaUpdateWrapper<InboundPricingEntity>()
|
return inbounds.update(null, new LambdaUpdateWrapper<InboundPricingEntity>()
|
||||||
.eq(InboundPricingEntity::getTenantId, tenantId).eq(InboundPricingEntity::getId, inboundId)
|
.eq(InboundPricingEntity::getTenantId, tenantId).eq(InboundPricingEntity::getId, inboundId)
|
||||||
.in(InboundPricingEntity::getProcessStatus,
|
.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")
|
.eq(InboundPricingEntity::getFeeStatus, "CALCULATED")
|
||||||
.set(InboundPricingEntity::getFeeStatus, "CONFIRMED"));
|
.set(InboundPricingEntity::getFeeStatus, "CONFIRMED"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,16 @@ public class FbaPricingService {
|
|||||||
if ("COMPLETED".equals(token.getStatus())) return response(tenantId, token.getResultResourceId(), null);
|
if ("COMPLETED".equals(token.getStatus())) return response(tenantId, token.getResultResourceId(), null);
|
||||||
|
|
||||||
InboundPricingEntity inbound = requireEligibleInbound(tenantId, inboundId);
|
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);
|
var measurement = measurementRepository.findLatestMeasurement(tenantId, inboundId);
|
||||||
if ("OUTER".equalsIgnoreCase(volumeSource) && measurement == null) {
|
if ("OUTER".equalsIgnoreCase(volumeSource) && measurement == null) {
|
||||||
throw conflict("外箱规测量批次不存在,不能生成对客费用");
|
throw conflict("外箱规测量批次不存在,不能生成对客费用");
|
||||||
@@ -114,7 +124,13 @@ public class FbaPricingService {
|
|||||||
weight, volume, converted, chargeable, subtotal, additional, total, now);
|
weight, volume, converted, chargeable, subtotal, additional, total, now);
|
||||||
fee.setMeasurementId(measurement == null ? null : measurement.getId());
|
fee.setMeasurementId(measurement == null ? null : measurement.getId());
|
||||||
fee.setMeasurementSource("OUTER".equalsIgnoreCase(volumeSource) ? "OUTER" : volumeSource);
|
fee.setMeasurementSource("OUTER".equalsIgnoreCase(volumeSource) ? "OUTER" : volumeSource);
|
||||||
|
if (published != null) {
|
||||||
|
if (repository.supersedeCurrent(tenantId, published.getId(), "SENT") != 1) {
|
||||||
|
throw conflict("费用已被客户确认或其他操作员修改,请刷新后重试");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
repository.supersedeCalculated(tenantId, inboundId);
|
repository.supersedeCalculated(tenantId, inboundId);
|
||||||
|
}
|
||||||
repository.insertFee(fee);
|
repository.insertFee(fee);
|
||||||
repository.insertItem(baseItem(tenantId, fee, rate, chargeable, subtotal, now));
|
repository.insertItem(baseItem(tenantId, fee, rate, chargeable, subtotal, now));
|
||||||
if (additional.signum() > 0) repository.insertItem(additionalItem(tenantId, fee, additional, 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);
|
feeDetails.materializeCalculatedFreight(tenantId, inboundId, fee.getId(), operatorId, now);
|
||||||
complete(token, fee.getId(), now);
|
complete(token, fee.getId(), now);
|
||||||
String nextStatus = isCompletedInbound(inbound.getProcessStatus())
|
String nextStatus = preserveProcessStatusDuringPricing(inbound.getProcessStatus())
|
||||||
? inbound.getProcessStatus() : "FEE_CONFIRMING";
|
? inbound.getProcessStatus() : "FEE_CONFIRMING";
|
||||||
return response(inbound, fee, rate.getUnitPrice(), nextStatus);
|
return response(inbound, fee, rate.getUnitPrice(), nextStatus);
|
||||||
}
|
}
|
||||||
@@ -163,7 +179,7 @@ public class FbaPricingService {
|
|||||||
String hash = hash("SEND", inboundId, current.getId().toString());
|
String hash = hash("SEND", inboundId, current.getId().toString());
|
||||||
InboundPricingIdempotencyEntity token = beginIdempotency(
|
InboundPricingIdempotencyEntity token = beginIdempotency(
|
||||||
tenantId, operatorId, inboundId, "FEE_SEND", idempotencyKey, hash);
|
tenantId, operatorId, inboundId, "FEE_SEND", idempotencyKey, hash);
|
||||||
String nextProcessStatus = isCompletedInbound(inbound.getProcessStatus())
|
String nextProcessStatus = preserveProcessStatusDuringPricing(inbound.getProcessStatus())
|
||||||
? inbound.getProcessStatus() : "WAIT_PALLET";
|
? inbound.getProcessStatus() : "WAIT_PALLET";
|
||||||
if ("COMPLETED".equals(token.getStatus())) {
|
if ("COMPLETED".equals(token.getStatus())) {
|
||||||
feeDetails.publishFreight(tenantId, inboundId, token.getResultResourceId(),
|
feeDetails.publishFreight(tenantId, inboundId, token.getResultResourceId(),
|
||||||
@@ -311,7 +327,7 @@ public class FbaPricingService {
|
|||||||
private InboundPricingEntity requireEligibleInbound(long tenantId, long inboundId) {
|
private InboundPricingEntity requireEligibleInbound(long tenantId, long inboundId) {
|
||||||
InboundPricingEntity inbound = requireApprovedInbound(tenantId, inboundId);
|
InboundPricingEntity inbound = requireApprovedInbound(tenantId, inboundId);
|
||||||
if (!feeEditableBeforeLoading(inbound.getProcessStatus())
|
if (!feeEditableBeforeLoading(inbound.getProcessStatus())
|
||||||
|| !List.of("UNPRICED", "CALCULATED").contains(inbound.getFeeStatus())) {
|
|| !List.of("UNPRICED", "CALCULATED", "SENT").contains(inbound.getFeeStatus())) {
|
||||||
throw conflict("当前入库单状态不允许费用试算");
|
throw conflict("当前入库单状态不允许费用试算");
|
||||||
}
|
}
|
||||||
return inbound;
|
return inbound;
|
||||||
@@ -336,9 +352,13 @@ public class FbaPricingService {
|
|||||||
return "WAIT_SPLIT".equals(processStatus);
|
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) {
|
private static boolean feeEditableBeforeLoading(String processStatus) {
|
||||||
return List.of("WAIT_MEASURE", "MEASURING", "MEASURE_PENDING", "MEASURE_REJECTED",
|
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) {
|
private static PricingDestinationType destinationType(InboundPricingEntity inbound) {
|
||||||
|
|||||||
+1
-1
@@ -198,7 +198,7 @@ class FbaPricingRepositoryRuleListTest {
|
|||||||
throw new AssertionError(exception);
|
throw new AssertionError(exception);
|
||||||
}
|
}
|
||||||
assertThat(source)
|
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) {
|
private Collection<Object> queryValues(Long tenantId, String ruleCode) {
|
||||||
|
|||||||
@@ -242,6 +242,54 @@ class FbaPricingServiceTest {
|
|||||||
verify(priceBookEstimator).estimate(any());
|
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
|
@Test
|
||||||
@DisplayName("当前生效价格表无匹配价格时明确拒绝")
|
@DisplayName("当前生效价格表无匹配价格时明确拒绝")
|
||||||
void rejectsMissingActivePriceBookRate() {
|
void rejectsMissingActivePriceBookRate() {
|
||||||
|
|||||||
Reference in New Issue
Block a user