This commit is contained in:
2026-08-13 11:57:33 +08:00
parent 5a48992d78
commit 16dc03a2e2
102 changed files with 3656 additions and 120 deletions
@@ -9,6 +9,7 @@ import com.baiyue.finance.dto.FinanceSettlementDtos.CreateReceivableSettlementRe
import com.baiyue.finance.dto.FinanceSettlementDtos.CreateSettlementPaymentRequest;
import com.baiyue.finance.dto.FinanceSettlementDtos.SettlementDetail;
import com.baiyue.finance.dto.FinanceSettlementDtos.SettlementSummary;
import com.baiyue.finance.dto.FinanceSettlementDtos.SettlementActionRequest;
import com.baiyue.finance.service.FinanceEventException;
import com.baiyue.finance.service.FinanceSettlementService;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -161,6 +162,55 @@ class FinanceSettlementBehaviorTest {
verify(jdbc, never()).update(startsWith("INSERT INTO fin_settlement_payment"), (Object[]) any());
}
@Test
void unpaidSettlementCanBeVoidedAndReopenedWithAuditAndVersionCheck() {
JdbcTemplate jdbc = org.mockito.Mockito.mock(JdbcTemplate.class, invocation ->
"update".equals(invocation.getMethod().getName()) ? 1 : RETURNS_DEFAULTS.answer(invocation));
Map<String, Object> issued = settlementRow("PAYABLE", "ISSUED", BigDecimal.ZERO,
new BigDecimal("100.00"), 2);
Map<String, Object> voided = settlementRow("PAYABLE", "VOIDED", BigDecimal.ZERO,
new BigDecimal("100.00"), 3);
when(jdbc.queryForList(startsWith("SELECT id,tenant_id,total_amount"), eq(88L)))
.thenReturn(List.of(issued));
when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L),
eq("SETTLEMENT_STATE_ACTION"), eq("void-key"))).thenReturn(List.of());
stubSettlementDetail(jdbc, voided);
FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class));
SettlementDetail result = service.actionSettlement(88L, 7L, "void-key",
new SettlementActionRequest("VOID", "业务取消", 2));
assertThat(result.summary().status()).isEqualTo("VOIDED");
verify(jdbc).update(startsWith("UPDATE fin_settlement SET status="), eq("VOIDED"), eq(7L),
eq(88L), eq(10L), eq(2));
verify(jdbc).update(startsWith("INSERT INTO fin_audit_log"), eq(10L), eq("FIN_SETTLEMENT"),
eq(88L), eq("VOID"), eq("业务取消"), any(), any(), eq(7L), any(), any(), any());
}
@Test
void settlementActionRejectsPaidInvalidStateAndStaleVersion() {
JdbcTemplate paidJdbc = org.mockito.Mockito.mock(JdbcTemplate.class);
when(paidJdbc.queryForList(startsWith("SELECT id,tenant_id,total_amount"), eq(88L)))
.thenReturn(List.of(settlementRow("PAYABLE", "PARTIALLY_PAID", new BigDecimal("40.00"),
new BigDecimal("60.00"), 2)));
when(paidJdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L),
eq("SETTLEMENT_STATE_ACTION"), anyString())).thenReturn(List.of());
FinanceSettlementService paidService = service(paidJdbc, org.mockito.Mockito.mock(FinanceFileClient.class));
assertCode(() -> paidService.actionSettlement(88L, 7L, "paid-key",
new SettlementActionRequest("VOID", "不能作废", 2)), "FIN_409");
JdbcTemplate staleJdbc = org.mockito.Mockito.mock(JdbcTemplate.class);
when(staleJdbc.queryForList(startsWith("SELECT id,tenant_id,total_amount"), eq(88L)))
.thenReturn(List.of(settlementRow("PAYABLE", "VOIDED", BigDecimal.ZERO,
new BigDecimal("100.00"), 3)));
when(staleJdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L),
eq("SETTLEMENT_STATE_ACTION"), eq("stale-action"))).thenReturn(List.of());
FinanceSettlementService staleService = service(staleJdbc, org.mockito.Mockito.mock(FinanceFileClient.class));
assertCode(() -> staleService.actionSettlement(88L, 7L, "stale-action",
new SettlementActionRequest("REOPEN", "恢复结算", 2)), "FIN_409");
verify(staleJdbc, never()).update(startsWith("UPDATE fin_settlement SET status="), (Object[]) any());
}
@Test
void partialPaymentUpdatesOutstandingAndReturnsTypedDetail() {
JdbcTemplate jdbc = org.mockito.Mockito.mock(JdbcTemplate.class, invocation ->