优化
This commit is contained in:
+3
-3
@@ -27,7 +27,7 @@ public class FinancePhase1OperationController {
|
||||
public FinancePhase1OperationController(FinancePhase1OperationService service) { this.service = service; }
|
||||
|
||||
@GetMapping("/accounts")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-account:list','platform:finance:account:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-account:list')")
|
||||
public Object listDerivedCustomerAccounts(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "20") int pageSize,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@@ -36,7 +36,7 @@ public class FinancePhase1OperationController {
|
||||
}
|
||||
|
||||
@GetMapping("/accounts/{customerId}/ledger")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-account:list','platform:finance:account:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-account:list')")
|
||||
public Object listDerivedCustomerAccountLedger(@PathVariable long customerId,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "20") int pageSize,
|
||||
@@ -47,7 +47,7 @@ public class FinancePhase1OperationController {
|
||||
}
|
||||
|
||||
@GetMapping("/fee-sources/{feeSourceId}")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-arap:list','platform:finance:fee-source:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-arap:list')")
|
||||
public ApiResponse<?> getFinanceFeeSource(@PathVariable long feeSourceId) {
|
||||
return FinanceResponses.success(service.getFinanceFeeSource(feeSourceId));
|
||||
}
|
||||
|
||||
+4
-7
@@ -41,7 +41,7 @@ public class FinanceSettlementController {
|
||||
}
|
||||
|
||||
@GetMapping("/receivable-settlements")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:settlement-ar:list','platform:finance:settlement-ar:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:settlement-ar:list')")
|
||||
@Operation(summary = "查询正式应收结算单")
|
||||
public SettlementListEnvelope receivableSettlements(
|
||||
@Parameter(description = "可选客户筛选;省略时查询全部客户")
|
||||
@@ -50,7 +50,7 @@ public class FinanceSettlementController {
|
||||
}
|
||||
|
||||
@GetMapping("/payable-settlements")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:settlement-ap:list','platform:finance:settlement-ap:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:settlement-ap:list')")
|
||||
@Operation(summary = "查询正式应付结算单")
|
||||
public SettlementListEnvelope payableSettlements(
|
||||
@Parameter(description = "可选客户筛选;省略时查询全部客户")
|
||||
@@ -77,17 +77,14 @@ public class FinanceSettlementController {
|
||||
}
|
||||
|
||||
@GetMapping("/settlements/{settlementId}")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:settlement-ar:list','platform:finance:settlement-ar:view','platform:finance:settlement-ap:list','platform:finance:settlement-ap:view')")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:settlement-ar:list','platform:finance:settlement-ap:list')")
|
||||
public SettlementDetailEnvelope getSettlement(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable long settlementId) {
|
||||
SettlementDetail detail = service.getSettlement(settlementId);
|
||||
String direction = detail.summary().direction();
|
||||
String listPermission = "RECEIVABLE".equals(direction)
|
||||
? "platform:finance:settlement-ar:list" : "platform:finance:settlement-ap:list";
|
||||
String legacyViewPermission = "RECEIVABLE".equals(direction)
|
||||
? "platform:finance:settlement-ar:view" : "platform:finance:settlement-ap:view";
|
||||
if (user == null || (!user.permissions().contains(listPermission)
|
||||
&& !user.permissions().contains(legacyViewPermission))) {
|
||||
if (user == null || !user.permissions().contains(listPermission)) {
|
||||
throw new AccessDeniedException("无权查看该方向结算单");
|
||||
}
|
||||
return success(detail);
|
||||
|
||||
@@ -45,14 +45,21 @@ public class PlatformFinanceController {
|
||||
}
|
||||
|
||||
@GetMapping("/customer-accounts/{customerId}")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-account:list','platform:finance:account:view','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-account:list')")
|
||||
@Operation(summary = "客户人工应收账户及流水")
|
||||
public ApiResponse<?> customerAccount(@PathVariable long customerId) {
|
||||
return FinanceResponses.success(queryService.customerAccount(customerId));
|
||||
}
|
||||
|
||||
@GetMapping("/customer-accounts/{customerId}/summary")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-account:list')")
|
||||
@Operation(summary = "客户正式应收应付汇总")
|
||||
public ApiResponse<?> customerAccountSummary(@PathVariable long customerId) {
|
||||
return FinanceResponses.success(queryService.customerAccountSummary(customerId));
|
||||
}
|
||||
|
||||
@GetMapping("/fee-pool")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-arap:list','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-arap:list')")
|
||||
@Operation(summary = "客户应收应付费用池")
|
||||
public ApiResponse<?> feePool(
|
||||
@Parameter(description = "可选客户筛选;省略时查询全部客户")
|
||||
@@ -62,7 +69,7 @@ public class PlatformFinanceController {
|
||||
}
|
||||
|
||||
@GetMapping("/statements")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-statement:list','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-statement:list')")
|
||||
@Operation(summary = "客户对账单")
|
||||
public ApiResponse<?> statements(
|
||||
@Parameter(description = "可选客户筛选;省略时查询全部客户")
|
||||
@@ -87,7 +94,7 @@ public class PlatformFinanceController {
|
||||
public ApiResponse<?> terms(@PathVariable long tenantId) { return FinanceResponses.success(termsView(tenantId, service.customerTerms(tenantId))); }
|
||||
|
||||
@GetMapping("/bills")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-statement:list','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-statement:list')")
|
||||
@Operation(summary = "查询平台账单")
|
||||
public ApiResponse<?> bills(@RequestParam long customerId) { return FinanceResponses.success(queryService.statements(customerId, false)); }
|
||||
|
||||
@@ -115,11 +122,11 @@ public class PlatformFinanceController {
|
||||
}
|
||||
|
||||
@GetMapping("/bills/{billId}")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-statement:list','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-statement:list')")
|
||||
public ApiResponse<?> bill(@PathVariable long billId) { return FinanceResponses.success(queryService.platformStatement(billId)); }
|
||||
|
||||
@GetMapping("/bills/{billId}/revisions")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-statement:list','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-statement:list')")
|
||||
public ApiResponse<?> revisions(@PathVariable long billId, @RequestParam long customerId) {
|
||||
return FinanceResponses.success(queryService.billRevisions(customerId, billId));
|
||||
}
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public class ReconciliationStatementController {
|
||||
}
|
||||
|
||||
@GetMapping("/reconciliation-statements/{statementId}")
|
||||
@PreAuthorize("hasAnyAuthority('platform:finance:customer-statement:list','platform:finance:bill:view')")
|
||||
@PreAuthorize("hasAuthority('platform:finance:customer-statement:list')")
|
||||
public ApiResponse<ReconciliationDetail> getReconciliationStatement(@PathVariable long statementId) {
|
||||
return FinanceResponses.success(service.detailForPlatform(statementId));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.baiyue.finance.dto;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/** Stable read projections used by the Finance pages. */
|
||||
@@ -18,6 +19,17 @@ public final class FinanceReadModels {
|
||||
BigDecimal outstandingAmount,
|
||||
List<AccountEntry> entries) {}
|
||||
|
||||
public record CustomerAccountSummary(
|
||||
long customerId,
|
||||
String currency,
|
||||
BigDecimal totalReceivable,
|
||||
BigDecimal receivedAmount,
|
||||
BigDecimal outstandingReceivable,
|
||||
BigDecimal pendingBilling,
|
||||
BigDecimal outstandingPayable,
|
||||
Instant asOf,
|
||||
LocalDateTime lastChangedAt) {}
|
||||
|
||||
public record AccountEntry(
|
||||
String entryType,
|
||||
long resourceId,
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.baiyue.finance.dto.FinanceReadModels.ClientReceipt;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.ClientReceiptAllocation;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.BillRevision;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.CustomerAccount;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.CustomerAccountSummary;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.CostAllocation;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.CostDetail;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.FeePoolItem;
|
||||
@@ -26,6 +27,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -58,6 +60,28 @@ public class FinanceQueryService {
|
||||
return new CustomerAccount(customerId, CNY, "MANUAL_RECEIVABLE_LEDGER", total, received, outstanding, List.copyOf(entries));
|
||||
}
|
||||
|
||||
public CustomerAccountSummary customerAccountSummary(long customerId) {
|
||||
requireCustomer(customerId);
|
||||
String sql = "SELECT " +
|
||||
"COALESCE(SUM(CASE WHEN s.direction='RECEIVABLE' AND s.status<>'VOIDED' THEN s.total_amount ELSE 0 END),0) total_receivable," +
|
||||
"COALESCE(SUM(CASE WHEN s.direction='RECEIVABLE' AND s.status<>'VOIDED' THEN s.paid_amount ELSE 0 END),0) received_amount," +
|
||||
"COALESCE(SUM(CASE WHEN s.direction='RECEIVABLE' AND s.status<>'VOIDED' THEN s.outstanding_amount ELSE 0 END),0) outstanding_receivable," +
|
||||
"COALESCE(SUM(CASE WHEN s.direction='PAYABLE' AND s.status<>'VOIDED' THEN s.outstanding_amount ELSE 0 END),0) outstanding_payable," +
|
||||
"(SELECT COALESCE(SUM(f.amount),0) FROM fin_fee_source f WHERE f.customer_id=? AND f.direction='RECEIVABLE' " +
|
||||
"AND f.currency_code='CNY' AND f.status='AVAILABLE' " +
|
||||
"AND JSON_UNQUOTE(JSON_EXTRACT(f.source_snapshot,'$.reviewStatus'))='APPROVED' " +
|
||||
"AND JSON_UNQUOTE(JSON_EXTRACT(f.source_snapshot,'$.settlementIncluded'))='true' " +
|
||||
"AND NOT EXISTS (SELECT 1 FROM fin_bill_line bl JOIN fin_bill b ON b.id=bl.bill_id AND b.tenant_id=bl.tenant_id " +
|
||||
"WHERE bl.tenant_id=f.customer_id AND bl.source_id=f.id AND b.status<>'VOIDED')) pending_billing," +
|
||||
"GREATEST(COALESCE(MAX(CASE WHEN s.status<>'VOIDED' THEN s.updated_at END),'1970-01-01')," +
|
||||
"COALESCE((SELECT MAX(f.updated_at) FROM fin_fee_source f WHERE f.customer_id=?),'1970-01-01')) last_changed_at " +
|
||||
"FROM fin_settlement s WHERE s.tenant_id=?";
|
||||
return jdbc.queryForObject(sql, (rs, rowNum) -> new CustomerAccountSummary(customerId, CNY,
|
||||
money(rs, "total_receivable"), money(rs, "received_amount"), money(rs, "outstanding_receivable"),
|
||||
money(rs, "pending_billing"), money(rs, "outstanding_payable"), Instant.now(), time(rs, "last_changed_at")),
|
||||
customerId, customerId, customerId);
|
||||
}
|
||||
|
||||
public List<FeePoolItem> feePool(Long customerId, String direction) {
|
||||
validateOptionalCustomer(customerId);
|
||||
String normalized = direction == null || direction.isBlank() ? "ALL" : direction.trim().toUpperCase(java.util.Locale.ROOT);
|
||||
@@ -200,6 +224,11 @@ public class FinanceQueryService {
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
private static BigDecimal money(ResultSet rs, String column) throws SQLException {
|
||||
BigDecimal value = rs.getBigDecimal(column);
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
private static Long nullableLong(ResultSet rs, String column) throws SQLException {
|
||||
long value = rs.getLong(column);
|
||||
return rs.wasNull() ? null : value;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.baiyue.finance;
|
||||
|
||||
import com.baiyue.finance.client.FinanceFileClient;
|
||||
import com.baiyue.finance.client.FirstlegCostSnapshotClient;
|
||||
import com.baiyue.finance.controller.PlatformFinanceController;
|
||||
import com.baiyue.finance.dto.FinanceReadModels.CustomerAccountSummary;
|
||||
import com.baiyue.finance.service.FinanceQueryService;
|
||||
import com.baiyue.finance.service.FinanceService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class CustomerAccountSummaryBehaviorTest {
|
||||
|
||||
@Test
|
||||
void aggregatesFormalSettlementsInOneCustomerScopedQuery() throws Exception {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
ResultSet rs = summaryRow("300.00", "140.00", "160.00", "25.00", "80.00");
|
||||
when(jdbc.queryForObject(anyString(), any(RowMapper.class), any(), any(), any())).thenAnswer(invocation ->
|
||||
((RowMapper<?>) invocation.getArgument(1)).mapRow(rs, 0));
|
||||
FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper());
|
||||
|
||||
CustomerAccountSummary result = service.customerAccountSummary(9L);
|
||||
|
||||
assertThat(result.customerId()).isEqualTo(9L);
|
||||
assertThat(result.currency()).isEqualTo("CNY");
|
||||
assertThat(result.totalReceivable()).isEqualByComparingTo("300.00");
|
||||
assertThat(result.receivedAmount()).isEqualByComparingTo("140.00");
|
||||
assertThat(result.outstandingReceivable()).isEqualByComparingTo("160.00");
|
||||
assertThat(result.pendingBilling()).isEqualByComparingTo("25.00");
|
||||
assertThat(result.outstandingPayable()).isEqualByComparingTo("80.00");
|
||||
|
||||
var sql = org.mockito.ArgumentCaptor.forClass(String.class);
|
||||
verify(jdbc).queryForObject(sql.capture(), any(RowMapper.class),
|
||||
org.mockito.ArgumentMatchers.eq(9L), org.mockito.ArgumentMatchers.eq(9L), org.mockito.ArgumentMatchers.eq(9L));
|
||||
assertThat(sql.getValue())
|
||||
.contains("FROM fin_settlement s WHERE s.tenant_id=?")
|
||||
.contains("s.direction='RECEIVABLE' AND s.status<>'VOIDED'")
|
||||
.contains("s.direction='PAYABLE' AND s.status<>'VOIDED'")
|
||||
.contains("f.customer_id=?", "f.status='AVAILABLE'", "'$.reviewStatus'", "'$.settlementIncluded'")
|
||||
.contains("b.status<>'VOIDED'")
|
||||
.doesNotContain("SUM(received_amount)", "FROM fin_receipt r");
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyCustomerReturnsZeroAmounts() throws Exception {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
ResultSet rs = summaryRow("0", "0", "0", "0", "0");
|
||||
when(jdbc.queryForObject(anyString(), any(RowMapper.class), any(), any(), any())).thenAnswer(invocation ->
|
||||
((RowMapper<?>) invocation.getArgument(1)).mapRow(rs, 0));
|
||||
|
||||
CustomerAccountSummary result = new FinanceQueryService(jdbc, new ObjectMapper()).customerAccountSummary(77L);
|
||||
|
||||
assertThat(result.totalReceivable()).isZero();
|
||||
assertThat(result.receivedAmount()).isZero();
|
||||
assertThat(result.outstandingReceivable()).isZero();
|
||||
assertThat(result.pendingBilling()).isZero();
|
||||
assertThat(result.outstandingPayable()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void controllerUsesListPermissionAndDelegatesSummary() throws Exception {
|
||||
FinanceQueryService query = mock(FinanceQueryService.class);
|
||||
PlatformFinanceController controller = new PlatformFinanceController(mock(FinanceService.class),
|
||||
mock(FinanceFileClient.class), mock(FirstlegCostSnapshotClient.class), query);
|
||||
|
||||
controller.customerAccountSummary(11L);
|
||||
|
||||
verify(query).customerAccountSummary(11L);
|
||||
PreAuthorize permission = PlatformFinanceController.class
|
||||
.getMethod("customerAccountSummary", long.class).getAnnotation(PreAuthorize.class);
|
||||
assertThat(permission.value()).isEqualTo("hasAuthority('platform:finance:customer-account:list')");
|
||||
}
|
||||
|
||||
private static ResultSet summaryRow(String total, String paid, String outstanding,
|
||||
String pending, String payable) throws Exception {
|
||||
ResultSet rs = mock(ResultSet.class);
|
||||
when(rs.getBigDecimal("total_receivable")).thenReturn(new BigDecimal(total));
|
||||
when(rs.getBigDecimal("received_amount")).thenReturn(new BigDecimal(paid));
|
||||
when(rs.getBigDecimal("outstanding_receivable")).thenReturn(new BigDecimal(outstanding));
|
||||
when(rs.getBigDecimal("pending_billing")).thenReturn(new BigDecimal(pending));
|
||||
when(rs.getBigDecimal("outstanding_payable")).thenReturn(new BigDecimal(payable));
|
||||
when(rs.getTimestamp("last_changed_at")).thenReturn(Timestamp.valueOf(LocalDateTime.of(2026, 8, 13, 12, 0)));
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,14 @@ class FinancePhase1PermissionContractTest {
|
||||
@Test
|
||||
void freezesG1AndG2ControllerPermissionsAndHttpActions() throws Exception {
|
||||
assertEndpoint("listDerivedCustomerAccounts", GetMapping.class,
|
||||
"platform:finance:customer-account:list", "platform:finance:account:view",
|
||||
"platform:finance:customer-account:list", null,
|
||||
int.class, int.class, String.class, String.class);
|
||||
assertEndpoint("listDerivedCustomerAccountLedger", GetMapping.class,
|
||||
"platform:finance:customer-account:list", "platform:finance:account:view",
|
||||
"platform:finance:customer-account:list", null,
|
||||
long.class, int.class, int.class,
|
||||
String.class, java.time.LocalDate.class, java.time.LocalDate.class);
|
||||
assertEndpoint("getFinanceFeeSource", GetMapping.class,
|
||||
"platform:finance:customer-arap:list", "platform:finance:fee-source:view", long.class);
|
||||
"platform:finance:customer-arap:list", null, long.class);
|
||||
assertEndpoint("reviewFinanceFeeSource", PostMapping.class,
|
||||
"platform:finance:fee-source:review", null,
|
||||
com.baiyue.common.security.AuthenticatedUser.class, long.class, String.class,
|
||||
|
||||
@@ -30,8 +30,8 @@ class FinancePlatformListContractTest {
|
||||
Map<String, String> expected = Map.of(
|
||||
"feePool", "platform:finance:customer-arap:list",
|
||||
"statements", "platform:finance:customer-statement:list",
|
||||
"receivableSettlements", "platform:finance:settlement-ar:view",
|
||||
"payableSettlements", "platform:finance:settlement-ap:view");
|
||||
"receivableSettlements", "platform:finance:settlement-ar:list",
|
||||
"payableSettlements", "platform:finance:settlement-ap:list");
|
||||
for (var entry : expected.entrySet()) {
|
||||
assertThat(find(entry.getKey()).getAnnotation(PreAuthorize.class).value()).contains(entry.getValue());
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class FinanceSettlementBehaviorTest {
|
||||
FinanceSettlementController controller = new FinanceSettlementController(controllerService);
|
||||
SettlementDetail payable = detail("PAYABLE", "ISSUED", BigDecimal.ZERO, new BigDecimal("100.00"), 0);
|
||||
when(controllerService.getSettlement(88L)).thenReturn(payable);
|
||||
AuthenticatedUser apViewer = user(Set.of("platform:finance:settlement-ap:view"));
|
||||
AuthenticatedUser apViewer = user(Set.of("platform:finance:settlement-ap:list"));
|
||||
|
||||
var response = controller.getSettlement(apViewer, 88L);
|
||||
|
||||
@@ -62,7 +62,7 @@ class FinanceSettlementBehaviorTest {
|
||||
assertThat(response.data()).isSameAs(payable);
|
||||
assertThat(response.requestId()).isNotBlank();
|
||||
assertThatThrownBy(() -> controller.getSettlement(
|
||||
user(Set.of("platform:finance:settlement-ar:view")), 88L)).isInstanceOf(AccessDeniedException.class);
|
||||
user(Set.of("platform:finance:settlement-ar:list")), 88L)).isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user