fix: restore customer visit uploads and guard fee publication

This commit is contained in:
2026-11-05 16:30:27 +08:00
parent 97c80574df
commit 497544e9fd
11 changed files with 369 additions and 45 deletions
@@ -165,6 +165,7 @@ class FbaPricingServiceTest {
}
@Test
/** 试算生成费用但保留原履约进度。 */
@DisplayName("包税KG按生效价格表计费且不依赖客户规则或承运商")
void calculatesTaxIncludedKgWithoutCustomerRuleOrCarrier() {
readyRepository();
@@ -186,7 +187,7 @@ class FbaPricingServiceTest {
assertThat(response.conversionFactor()).isEqualByComparingTo("167");
assertThat(response.roundingScale()).isEqualTo(6);
assertThat(response.roundingMode()).isEqualTo("HALF_UP");
assertThat(response.nextProcessStatus()).isEqualTo("FEE_CONFIRMING");
assertThat(response.nextProcessStatus()).isEqualTo("WAIT_PRICING");
ArgumentCaptor<InboundFeeEntity> fee = ArgumentCaptor.forClass(InboundFeeEntity.class);
verify(repository).insertFee(fee.capture());
assertThat(fee.getValue().getTaxMode()).isEqualTo("BUY_DECLARATION");
@@ -630,7 +631,7 @@ class FbaPricingServiceTest {
/** 待组托发送费用保留主状态,重复请求不重复生成费用事件。 */
@ParameterizedTest
@CsvSource({"FEE_CONFIRMING,WAIT_PALLET", "WAIT_PALLETIZE,WAIT_PALLETIZE"})
@CsvSource({"FEE_CONFIRMING,FEE_CONFIRMING", "WAIT_PALLETIZE,WAIT_PALLETIZE"})
@DisplayName("重复发送只返回首次结果且不会重复写Outbox")
void sendIsIdempotentAndDoesNotDuplicateOutbox(String processStatus, String expectedStatus) {
InboundPricingEntity inbound = inbound("10", "1");
@@ -687,6 +688,39 @@ class FbaPricingServiceTest {
eq(TENANT), eq(INBOUND), eq(501L), eq(1), eq(NOW));
}
/** 测量未审核前拒绝单独确认,不留下阻断审核后初算的已确认费用。 */
@ParameterizedTest
@ValueSource(strings = {"NOT_SUBMITTED", "PENDING", "REJECTED"})
void rejectsConfirmingBeforeMeasurementApproval(String reviewStatus) {
InboundPricingEntity inbound = inbound("570.500", "3.6894");
inbound.setProcessStatus("MEASURE_PENDING");
inbound.setMeasureReviewStatus(reviewStatus);
inbound.setFeeStatus("CALCULATED");
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
assertThatThrownBy(() -> service.confirm(TENANT, OPERATOR, INBOUND, "confirm-pending"))
.isInstanceOf(BizException.class).hasMessageContaining("先完成测量审核");
verify(repository, never()).confirmFee(anyLong(), anyLong(), anyLong(), any());
verify(repository, never()).markInboundConfirmed(anyLong(), anyLong());
verify(repository, never()).insertIdempotency(any());
}
/** 测量未审核前拒绝发布,不能产生已发送快照或财务事件。 */
@ParameterizedTest
@ValueSource(strings = {"NOT_SUBMITTED", "PENDING", "REJECTED"})
void rejectsSendingBeforeMeasurementApproval(String reviewStatus) {
InboundPricingEntity inbound = inbound("570.500", "3.6894");
inbound.setProcessStatus("MEASURE_PENDING");
inbound.setMeasureReviewStatus(reviewStatus);
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
assertThatThrownBy(() -> service.send(TENANT, OPERATOR, INBOUND, "send-pending"))
.isInstanceOf(BizException.class).hasMessageContaining("先完成测量审核");
verify(repository, never()).sendFee(anyLong(), anyLong(), anyLong(), any());
verify(repository, never()).insertIdempotency(any());
verify(financeOutbox, never()).publishInbound(anyString(), anyLong(), anyLong(), anyLong(), anyInt(), any());
}
@Test
@DisplayName("同一幂等键用于不同试算时点会冲突")
void rejectsReusedIdempotencyKeyWithDifferentRequest() {