提交
This commit is contained in:
@@ -148,7 +148,10 @@ public class BusinessFeeSourceEventService {
|
||||
positive(value.walletSettlement().ledgerId(), "walletSettlement.ledgerId");
|
||||
if (blank(value.walletSettlement().commandEventId())) throw error("FIN-EVT-012", "缺少原资金命令引用");
|
||||
walletIdentity(value);
|
||||
} else if ("OVERSEAS_PROCUREMENT_FEE".equals(value.sourceType()) && "RECEIVABLE".equals(value.direction())) {
|
||||
} else if ("OVERSEAS_PROCUREMENT_FEE".equals(value.sourceType()) && "RECEIVABLE".equals(value.direction())
|
||||
// 新采购主单复用人工收费,只有显式账单结算可入池;旧采购和余额费用仍必须引用实扣。
|
||||
&& !("LASTLEG".equals(value.sourceSystem()) && "OVERSEAS_PROCUREMENT".equals(value.businessType())
|
||||
&& "BILL".equals(value.settlementMode()))) {
|
||||
throw error("FIN-EVT-012", "采购应收必须引用成功实扣资金,不能进入待收费池");
|
||||
}
|
||||
if ("OVERSEAS_CONTAINER_FEE".equals(value.sourceType())) {
|
||||
|
||||
@@ -328,6 +328,53 @@ class BusinessFeeSourceEventServiceTest {
|
||||
noFinancialWrites();
|
||||
}
|
||||
|
||||
/** 新采购主单只有明确选择账单时才可作为未收应收入池。 */
|
||||
@Test
|
||||
void manualProcurementExplicitBillCanEnterReceivablePool() {
|
||||
EventEnvelope event = manualProcurementEvent("BILL");
|
||||
when(jdbc.queryForObject(startsWith("SELECT id FROM fin_fee_source"), eq(Long.class),
|
||||
eq("LASTLEG"), eq("OVERSEAS_PROCUREMENT_FEE"), eq(701L), eq(1))).thenReturn(901L);
|
||||
assertThat(service.consume(event).eventId()).isEqualTo(event.eventId());
|
||||
verify(jdbc).update(startsWith("INSERT INTO fin_fee_source"), any(Object[].class));
|
||||
verify(jdbc, never()).update(startsWith("UPDATE fin_fee_source SET wallet_hold_id="), any(Object[].class));
|
||||
}
|
||||
|
||||
/** 采购主单的余额或默认模式不能因兼容账单而绕过实扣校验。 */
|
||||
@ParameterizedTest
|
||||
@org.junit.jupiter.params.provider.NullSource
|
||||
@ValueSource(strings = {"WALLET"})
|
||||
void manualProcurementWithoutConsumptionCannotEnterPool(String mode) {
|
||||
assertThatThrownBy(() -> service.consume(manualProcurementEvent(mode))).hasMessageContaining("必须引用成功实扣");
|
||||
noFinancialWrites();
|
||||
}
|
||||
|
||||
/** 保留旧采购子费用的扣款约束,即使载荷声明账单结算也不能入池。 */
|
||||
@Test
|
||||
void legacyProcurementCannotBypassConsumptionWithBillMode() {
|
||||
EventEnvelope base = procurementEvent(false);
|
||||
ObjectNode payload = (ObjectNode) base.payload().deepCopy();
|
||||
payload.put("settlementMode", "BILL");
|
||||
assertThatThrownBy(() -> service.consume(resignProcurementEvent(base, payload))).hasMessageContaining("必须引用成功实扣");
|
||||
noFinancialWrites();
|
||||
}
|
||||
|
||||
private EventEnvelope manualProcurementEvent(String mode) {
|
||||
EventEnvelope base = procurementEvent(false);
|
||||
ObjectNode payload = (ObjectNode) base.payload().deepCopy();
|
||||
payload.put("businessType", "OVERSEAS_PROCUREMENT");
|
||||
if (mode == null) payload.putNull("settlementMode"); else payload.put("settlementMode", mode);
|
||||
return resignProcurementEvent(base, payload);
|
||||
}
|
||||
|
||||
private EventEnvelope resignProcurementEvent(EventEnvelope base, ObjectNode payload) {
|
||||
payload.putNull("action"); payload.putNull("sourceHash");
|
||||
payload.put("sourceHash", EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload)));
|
||||
payload.put("action", "PUBLISHED");
|
||||
return new EventEnvelope(base.eventId(), base.eventType(), base.eventVersion(), base.occurredAt(), base.tenantId(),
|
||||
base.aggregateType(), base.aggregateId(), base.aggregateVersion(), null, base.traceId(), base.correlationId(), null,
|
||||
payload, EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settledProcurementUsesVerifiedActualConsumptionAndNeverOriginalEstimate() {
|
||||
EventEnvelope event = procurementEvent(true);
|
||||
|
||||
Reference in New Issue
Block a user