FINEREACT-2404: Improved extensibility and cleanup

This commit is contained in:
cucusenok
2025-11-07 10:19:40 +01:00
committed by Adam Saghy
parent d269cb983f
commit 9320dd1bd7
12 changed files with 72 additions and 45 deletions
@@ -45,10 +45,14 @@ import org.springframework.stereotype.Component;
*/
@Component
@RequiredArgsConstructor
public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<JournalEntryCommand> {
public class JournalEntryCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<JournalEntryCommand> {
private final FromJsonHelper fromApiJsonHelper;
protected Set<String> getSupportedParameters() {
return JournalEntryJsonInputParams.getAllValues();
}
@Override
public JournalEntryCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) {
@@ -56,7 +60,7 @@ public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFr
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = JournalEntryJsonInputParams.getAllValues();
final Set<String> supportedParameters = this.getSupportedParameters();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = this.fromApiJsonHelper.parse(json);
@@ -32,4 +32,8 @@ public class MDCWrapper {
public void remove(String key) {
MDC.remove(key);
}
public String get(String key) {
return MDC.get(key);
}
}
@@ -86,7 +86,7 @@ public class SendAsynchronousEventsTasklet implements Tasklet {
return RepeatStatus.FINISHED;
}
private boolean isDownstreamChannelEnabled() {
protected boolean isDownstreamChannelEnabled() {
return fineractProperties.getEvents().getExternal().getProducer().getJms().isEnabled()
|| fineractProperties.getEvents().getExternal().getProducer().getKafka().isEnabled();
}
@@ -53,7 +53,7 @@ import org.springframework.util.CollectionUtils;
*/
@Getter
@JsonLocalDateArrayFormat
public final class SavingsAccountTransactionData implements Serializable {
public class SavingsAccountTransactionData implements Serializable {
private Long id;
private final SavingsAccountTransactionEnumData transactionType;
@@ -110,7 +110,7 @@ public final class SavingsAccountTransactionData implements Serializable {
private Long accountCredit;
private Long accountDebit;
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
protected SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate transactionDate,
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
@@ -68,7 +68,7 @@ public class EventStore {
return receivedEvents;
}
void receive(byte[] message) throws Exception {
public void receive(byte[] message) throws Exception {
MessageV1 msgObject = MessageV1.fromByteBuffer(ByteBuffer.wrap(message));
String type = msgObject.getType();
String idempotencyKey = msgObject.getIdempotencyKey();
@@ -20,11 +20,11 @@ package org.apache.fineract.portfolio.loanaccount.domain;
import java.time.LocalDate;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
public interface LoanAccrualActivityRepository extends Repository<Loan, Long> {
public interface LoanAccrualActivityRepository extends JpaRepository<Loan, Long> {
@Query("select loan.id from Loan loan left join LoanTransaction lt on lt.loan = loan and lt.typeOf = :loanType and lt.reversed = false and lt.dateOf = :currentDate inner join LoanRepaymentScheduleInstallment rs on rs.loan = loan and rs.isDownPayment = false and rs.additional = false and rs.dueDate = :currentDate where loan.loanRepaymentScheduleDetail.enableAccrualActivityPosting = true and loan.loanStatus = :loanStatus and lt.id is null ")
Set<Long> fetchLoanIdsForAccrualActivityPosting(@Param("currentDate") LocalDate currentDate,
@@ -79,11 +79,11 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
private final PaginationHelper paginationHelper;
private final DatabaseSpecificSQLGenerator sqlGenerator;
private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
protected static class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
private final JournalEntryAssociationParametersData associationParametersData;
GLJournalEntryMapper(final JournalEntryAssociationParametersData associationParametersData) {
protected GLJournalEntryMapper(final JournalEntryAssociationParametersData associationParametersData) {
this.associationParametersData = Objects.requireNonNullElseGet(associationParametersData,
JournalEntryAssociationParametersData::new);
}
@@ -240,8 +240,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
final Boolean onlyManualEntries, final LocalDate fromDate, final LocalDate toDate, final LocalDate submittedOnDateFrom,
final LocalDate submittedOnDateTo, final String transactionId, final Integer entityType,
final JournalEntryAssociationParametersData associationParametersData) {
GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
GLJournalEntryMapper rm = getGlJournalEntryMapper(associationParametersData);
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select ").append(sqlGenerator.calcFoundRows()).append(" ");
sqlBuilder.append(rm.schema());
@@ -383,12 +382,16 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, rm);
}
protected GLJournalEntryMapper getGlJournalEntryMapper(JournalEntryAssociationParametersData associationParametersData) {
return new GLJournalEntryMapper(associationParametersData);
}
@Override
public JournalEntryData retrieveGLJournalEntryById(final long glJournalEntryId,
JournalEntryAssociationParametersData associationParametersData) {
try {
final GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
final GLJournalEntryMapper rm = getGlJournalEntryMapper(associationParametersData);
// Programmatic query, disable sonar issue
final String sql = "select " + rm.schema() + " where journalEntry.id = ?";
@@ -526,7 +529,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
public Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType) {
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(true, true);
try {
final GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
final GLJournalEntryMapper rm = getGlJournalEntryMapper(associationParametersData);
final String sql = "select " + rm.schema()
+ " where journalEntry.transaction_id = ? and journalEntry.entity_id = ? and journalEntry.entity_type_enum = ?";
Object[] data = { transactionId, entityId, entityType };
@@ -98,7 +98,7 @@ import org.springframework.stereotype.Component;
@Component("loanTransactionValidator")
@AllArgsConstructor
public final class LoanTransactionValidatorImpl implements LoanTransactionValidator {
public class LoanTransactionValidatorImpl implements LoanTransactionValidator {
private final FromJsonHelper fromApiJsonHelper;
private final LoanApplicationValidator fromApiJsonDeserializer;
@@ -245,7 +245,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
});
}
private void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
protected void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
final JsonElement jsonElement = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.disbursement");
@@ -291,7 +291,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
}
}
private void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance,
protected void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance,
Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) {
if (null != calendarInstance) {
final Calendar calendar = calendarInstance.getCalendar();
@@ -334,9 +334,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
throw new InvalidJsonException();
}
final Set<String> transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME));
final Set<String> transactionParameters = getTransactionParametersForEdit();
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
@@ -363,6 +361,12 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
protected HashSet<String> getTransactionParametersForEdit() {
return new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note", "locale", "dateFormat",
"paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME));
}
@Override
public void validateChargebackTransaction(final String json) {
@@ -405,8 +409,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
throw new InvalidJsonException();
}
final Set<String> disbursementParameters = new HashSet<>(
Arrays.asList("transactionDate", "note", "locale", "dateFormat", "writeoffReasonId", "externalId"));
final Set<String> disbursementParameters = getNoAmountTransactionParameters();
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
@@ -427,6 +430,10 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
protected Set<String> getNoAmountTransactionParameters() {
return new HashSet<>(Arrays.asList("transactionDate", "note", "locale", "dateFormat", "writeoffReasonId", "externalId"));
}
@Override
public void validateChargeOffTransaction(final String json) {
if (StringUtils.isBlank(json)) {
@@ -659,7 +666,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
}
}
private void validateLoanHasNoLaterChargeRefundTransactionToReverseOrCreateATransaction(Loan loan, LocalDate transactionDate,
protected void validateLoanHasNoLaterChargeRefundTransactionToReverseOrCreateATransaction(Loan loan, LocalDate transactionDate,
String reversedOrCreated) {
for (LoanTransaction txn : loan.getLoanTransactions()) {
if (txn.isChargeRefund() && DateUtils.isBefore(transactionDate, txn.getTransactionDate())) {
@@ -671,7 +678,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
}
}
private void validateLoanDisbursementIsBeforeTransactionDate(final Loan loan, final LocalDate transactionDate) {
protected void validateLoanDisbursementIsBeforeTransactionDate(final Loan loan, final LocalDate transactionDate) {
if (DateUtils.isBefore(transactionDate, loan.getDisbursementDate())) {
final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
+ loan.getDisbursementDate().toString();
@@ -680,14 +687,14 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
}
}
private void validateTransactionShouldNotBeInTheFuture(final LocalDate transactionDate) {
protected void validateTransactionShouldNotBeInTheFuture(final LocalDate transactionDate) {
if (DateUtils.isDateInTheFuture(transactionDate)) {
final String errorMessage = "The transaction date cannot be in the future.";
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, transactionDate);
}
}
private void validateLoanHasCurrency(final Loan loan) {
protected void validateLoanHasCurrency(final Loan loan) {
MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency defaultApplicationCurrency = this.applicationCurrencyRepository.findOneByCode(currency.getCode());
if (defaultApplicationCurrency == null) {
@@ -695,7 +702,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
}
}
private void validateClientOfficeJoiningDateIsBeforeTransactionDate(Loan loan, LocalDate transactionDate) {
protected void validateClientOfficeJoiningDateIsBeforeTransactionDate(Loan loan, LocalDate transactionDate) {
if (loan.getClient() != null && loan.getClient().getOfficeJoiningDate() != null) {
final LocalDate clientOfficeJoiningDate = loan.getClient().getOfficeJoiningDate();
if (DateUtils.isBefore(transactionDate, clientOfficeJoiningDate)) {
@@ -759,7 +766,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
}
}
private void validateTransactionAmountNotExceedThresholdForMultiDisburseLoan(Loan loan) {
protected void validateTransactionAmountNotExceedThresholdForMultiDisburseLoan(Loan loan) {
if (loan.getLoanProduct().isMultiDisburseLoan()) {
BigDecimal totalDisbursed = loan.getDisbursedAmount();
BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
@@ -830,7 +837,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
validateTransactionAmountNotExceedThresholdForMultiDisburseLoan(loan);
}
private void validateRepaymentTypeTransactionNotBeforeAChargeRefund(final Loan loan, final LoanTransactionType loanTransactionType,
protected void validateRepaymentTypeTransactionNotBeforeAChargeRefund(final Loan loan, final LoanTransactionType loanTransactionType,
final LocalDate transactionDate) {
if (loanTransactionType.isRepaymentType() && !loanTransactionType.isChargeRefund()) {
for (LoanTransaction txn : loan.getLoanTransactions()) {
@@ -931,7 +938,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
return totalCollateral;
}
private static @NonNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
protected @NonNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
Set<String> disbursementParameters;
if (isAccountTransfer) {
@@ -952,9 +959,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
throw new InvalidJsonException();
}
final Set<String> transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
"loanId", "numberOfRepayments", "interestRefundCalculation"));
final Set<String> transactionParameters = getRepaymentParameters();
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
@@ -976,6 +981,12 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
protected Set<String> getRepaymentParameters() {
return new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note", "locale", "dateFormat",
"paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", "loanId",
"numberOfRepayments", "interestRefundCalculation"));
}
@Override
public void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
// Validate all string payment detail fields for max length
@@ -188,7 +188,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
private final ConfigurationDomainService configurationDomainService;
private final AccountDetailsReadPlatformService accountDetailsReadPlatformService;
private final ColumnValidator columnValidator;
private final DatabaseSpecificSQLGenerator sqlGenerator;
protected final DatabaseSpecificSQLGenerator sqlGenerator;
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
private final LoanTransactionRepository loanTransactionRepository;
private final LoanChargePaidByReadService loanChargePaidByReadService;
@@ -300,7 +300,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
try {
this.context.authenticatedUser();
final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator);
final LoanTransactionsMapper rm = createLoanTransactionsMapper();
// retrieve all loan transactions that are not invalid and have not
// been 'contra'ed by another transaction
@@ -332,6 +332,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
}
}
protected LoanTransactionsMapper createLoanTransactionsMapper() {
return new LoanTransactionsMapper(sqlGenerator);
}
@Override
public Page<LoanAccountData> retrieveAll(final SearchParameters searchParameters) {
@@ -704,7 +708,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
public LoanTransactionData retrieveLoanTransaction(final Long loanId, final Long transactionId) {
this.context.authenticatedUser();
try {
final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator);
final LoanTransactionsMapper rm = createLoanTransactionsMapper();
final String sql = "select " + rm.loanPaymentsSchema() + " where l.id = ? and tr.id = ? ";
LoanTransactionData loanTransactionData = this.jdbcTemplate.queryForObject(sql, rm, loanId, transactionId); // NOSONAR
loanTransactionData.setTransactionRelations(
@@ -1702,11 +1706,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
}
private static final class LoanTransactionsMapper implements RowMapper<LoanTransactionData> {
protected static class LoanTransactionsMapper implements RowMapper<LoanTransactionData> {
private final DatabaseSpecificSQLGenerator sqlGenerator;
LoanTransactionsMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
protected LoanTransactionsMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
this.sqlGenerator = sqlGenerator;
}
@@ -1680,7 +1680,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.getTopupLoanDetails().setTopupAmount(amount);
}
private void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
protected Long disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount,
final PaymentDetail paymentDetail) {
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
@@ -1699,7 +1700,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
PortfolioAccountType.SAVINGS, loan.getId(), portfolioAccountData.getId(), "Loan Disbursement", locale, fmt, paymentDetail,
LoanTransactionType.DISBURSEMENT.getValue(), null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null,
txnExternalId, loan, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
return this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
}
@Transactional
@@ -91,7 +91,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
// mappers
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
private final SavingsAccountTransactionsMapper transactionsMapper;
protected SavingsAccountTransactionsMapper transactionsMapper;
private final SavingsAccountTransactionsForBatchMapper savingsAccountTransactionsForBatchMapper;
private final SavingAccountMapper savingAccountMapper;
private final SavingAccountMapperForInterestPosting savingAccountMapperForInterestPosting;
@@ -1088,7 +1088,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
* return this.jdbcTemplate.query(sql, this.annualFeeMapper, new Object[] {}); }
*/
public static final class SavingsAccountTransactionsMapper implements RowMapper<SavingsAccountTransactionData> {
public static class SavingsAccountTransactionsMapper implements RowMapper<SavingsAccountTransactionData> {
private static final String SELECT = buildSelect();
private static final String FROM = buildFrom();
@@ -1096,7 +1096,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
public SavingsAccountTransactionsMapper() {}
private static String buildSelect() {
protected static String buildSelect() {
return "tr.id as transactionId, tr.transaction_type_enum as transactionType, "
+ "tr.transaction_date as transactionDate, tr.amount as transactionAmount, "
+ "tr.release_id_of_hold_amount as releaseTransactionId, tr.reason_for_block as reasonForBlock, "
@@ -1116,7 +1116,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
+ "curr.display_symbol as currencyDisplaySymbol, pt.value as paymentTypeName, " + "tr.is_manual as postInterestAsOn ";
}
private static String buildFrom() {
protected static String buildFrom() {
return " FROM m_savings_account_transaction tr join m_savings_account sa on tr.savings_account_id = sa.id "
+ "join m_currency curr on curr.code = sa.currency_code "
+ "left join m_account_transfer_transaction fromtran on fromtran.from_savings_transaction_id = tr.id "
@@ -70,6 +70,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
private final DataTableValidator dataTableValidator;
private final JdbcTemplate jdbcTemplate;
private final SearchUtil searchUtil;
protected SavingsAccountReadPlatformServiceImpl.SavingsAccountTransactionsMapper tm = new SavingsAccountReadPlatformServiceImpl.SavingsAccountTransactionsMapper();
@Override
public Page<SavingsAccountTransactionData> searchTransactions(@NonNull Long savingsId,
@@ -111,7 +112,6 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
ArrayList<Object> params = new ArrayList<>();
searchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, null, null, null, false, sqlGenerator);
SavingsAccountReadPlatformServiceImpl.SavingsAccountTransactionsMapper tm = new SavingsAccountReadPlatformServiceImpl.SavingsAccountTransactionsMapper();
Object[] args = params.toArray();
String countQuery = "SELECT COUNT(*) " + tm.from() + where;