Skip to content

Commit

Permalink
Enhancements for Savings jobs
Browse files Browse the repository at this point in the history
  • Loading branch information
Jose Alberto Hernandez authored and alberto-art3ch committed Mar 30, 2024
1 parent d51cd09 commit ded89ce
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -547,4 +547,8 @@ public Integer getFinancialYearBeginningMonth() {
return this.financialYearBeginningMonth;
}

public List<CompoundingPeriod> getCompoundingPeriods() {
return compoundingPeriods;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public void modifyApplication(final JsonCommand command, final Map<String, Objec
}

@Override
protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) {
public BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) {
boolean isPreMatureClosure = false;
return getEffectiveInterestRateAsFraction(mc, interestPostingUpToDate, isPreMatureClosure);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public void updateDepositAmount(final BigDecimal depositAmount) {
}

@Override
protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) {
public BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate interestPostingUpToDate) {
boolean isPreMatureClosure = false;
return getEffectiveInterestRateAsFraction(mc, interestPostingUpToDate, isPreMatureClosure);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
package org.apache.fineract.portfolio.savings.jobs.addaccrualtransactionforsavings;

import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccrualWritePlatformService;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
Expand All @@ -40,9 +39,7 @@ public class AddAccrualTransactionForSavingsConfig {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
@Autowired
private SavingsAccountWritePlatformService savingsAccountWritePlatformService;
private SavingsAccrualWritePlatformService savingsAccrualWritePlatformService;

@Bean
protected Step addAccrualTransactionForSavingsStep() {
Expand All @@ -58,6 +55,6 @@ public Job addAccrualTransactionForSavingsJob() {

@Bean
public AddAccrualTransactionForSavingsTasklet addAccrualTransactionForSavingsTasklet() {
return new AddAccrualTransactionForSavingsTasklet(savingsAccountChargeReadPlatformService, savingsAccountWritePlatformService);
return new AddAccrualTransactionForSavingsTasklet(savingsAccrualWritePlatformService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,33 @@
*/
package org.apache.fineract.portfolio.savings.jobs.addaccrualtransactionforsavings;

import java.util.Collection;
import java.util.List;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.portfolio.savings.data.SavingsAccountAnnualFeeData;
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.apache.fineract.infrastructure.core.exception.MultiException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import org.apache.fineract.portfolio.savings.service.SavingsAccrualWritePlatformService;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

@Slf4j
@RequiredArgsConstructor
public class AddAccrualTransactionForSavingsTasklet implements Tasklet {

private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
private final SavingsAccrualWritePlatformService savingsAccrualWritePlatformService;

@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
final Collection<SavingsAccountAnnualFeeData> annualFeeData = savingsAccountChargeReadPlatformService
.retrieveChargesWithAnnualFeeDue();

for (final SavingsAccountAnnualFeeData savingsAccountReference : annualFeeData) {
try {
savingsAccountWritePlatformService.applyAnnualFee(savingsAccountReference.getId(), savingsAccountReference.getAccountId());
} catch (final PlatformApiDataValidationException e) {
final List<ApiParameterError> errors = e.getErrors();
for (final ApiParameterError error : errors) {
log.error("Add Accrual Transaction failed for account: {} with message {}", savingsAccountReference.getAccountNo(),
error);
}
} catch (final Exception ex) {
log.error("Add Accrual Transaction failed for account: {}", savingsAccountReference.getAccountNo(), ex);
}
try {
addPeriodicAccruals(DateUtils.getBusinessLocalDate());
} catch (MultiException e) {
throw new JobExecutionException(e);
}

log.debug("{}: Records affected by addAccrualTransactionForSavings: {}", ThreadLocalContextUtil.getTenant().getName(),
annualFeeData.size());
return RepeatStatus.FINISHED;
}

private void addPeriodicAccruals(final LocalDate tilldate) throws MultiException {
savingsAccrualWritePlatformService.addAccrualEntries(tilldate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingRuleType;
Expand Down Expand Up @@ -77,7 +78,9 @@
import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccrualData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountChargesPaidByData;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
Expand All @@ -93,6 +96,7 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.util.CollectionUtils;

@Slf4j
Expand All @@ -107,6 +111,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final DatabaseSpecificSQLGenerator sqlGenerator;
private final ChargeReadPlatformService chargeReadPlatformService;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

// mappers
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
Expand All @@ -132,7 +137,8 @@ public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext conte
final ChargeReadPlatformService chargeReadPlatformService,
final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator,
final SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper,
DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper) {
DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper,
final NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.context = context;
this.jdbcTemplate = jdbcTemplate;
this.clientReadPlatformService = clientReadPlatformService;
Expand All @@ -152,6 +158,7 @@ public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext conte
this.paginationHelper = paginationHelper;
this.savingAccountMapperForInterestPosting = new SavingAccountMapperForInterestPosting();
this.savingAccountAssembler = savingAccountAssembler;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}

@Override
Expand Down Expand Up @@ -1742,4 +1749,107 @@ public List<Long> getAccountsIdsByStatusPaged(Integer status, int pageSize, Long
public Long retrieveAccountIdByExternalId(final ExternalId externalId) {
return savingsAccountRepositoryWrapper.findIdByExternalId(externalId);
}

@Override
public Collection<SavingsAccrualData> retrievePeriodicAccrualData(LocalDate tillDate, SavingsAccount savings) {
final SavingAccrualMapper mapper = new SavingAccrualMapper();
final StringBuilder sqlBuilder = new StringBuilder(400);
Map<String, Object> paramMap = new HashMap<>(3);
sqlBuilder.append(" select " + mapper.schema() + " where ");

sqlBuilder.append(" savings.status_enum = :active ");
sqlBuilder.append(" and (savings.nominal_annual_interest_rate is not null and savings.nominal_annual_interest_rate > 0) ");
sqlBuilder.append(" and msp.accounting_type = :type ");
sqlBuilder.append(" and (savings.closedon_date <= :tillDate or savings.closedon_date is null) ");
sqlBuilder.append(" and (savings.accrued_till_date <= :tillDate or savings.accrued_till_date is null) ");
if (savings != null) {
sqlBuilder.append(" and savings.id = " + savings.getId());
}
sqlBuilder.append(" order by savings.id ");
paramMap.put("active", SavingsAccountStatusType.ACTIVE.getValue());
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
paramMap.put("tillDate", tillDate);
try {
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
} catch (EmptyResultDataAccessException e) {
return new ArrayList<>();
}
}

private static final class SavingAccrualMapper implements RowMapper<SavingsAccrualData> {

private final String schemaSql;

SavingAccrualMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append(
" savings.id as savingsId, savings.status_enum as status, (CASE WHEN savings.client_id is null THEN mg.office_id ELSE mc.office_id END) as officeId, ");
sqlBuilder.append(
" savings.accrued_till_date as accruedTill, savings.product_id as productId, savings.deposit_type_enum as depositType, ");
sqlBuilder.append(" savings.account_no as accountNo, savings.nominal_annual_interest_rate as nominalAnnualIterestRate, ");
sqlBuilder.append(" savings.interest_compounding_period_enum as interestCompoundingPeriodType, ");
sqlBuilder.append(" savings.interest_posting_period_enum as interestPostingPeriodType, ");
sqlBuilder.append(" savings.interest_calculation_type_enum as interestCalculationType, ");
sqlBuilder.append(" savings.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
sqlBuilder.append(" savings.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
sqlBuilder.append(" savings.interest_posted_till_date as postedTill, tg.id as taxGroupId, ");
sqlBuilder.append(
" savings.currency_code as currencyCode, savings.currency_digits as currencyDigits, savings.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append(
" curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode ");
sqlBuilder.append(" from m_savings_account savings ");
sqlBuilder.append(" left join m_savings_product msp on msp.id = savings.product_id ");
sqlBuilder.append(" left join m_client mc on mc.id = savings.client_id ");
sqlBuilder.append(" left join m_group mg on mg.id = savings.group_id ");
sqlBuilder.append(" left join m_currency curr on curr.code = savings.currency_code ");
sqlBuilder.append(" left join m_tax_group tg on tg.id = savings.tax_group_id ");

this.schemaSql = sqlBuilder.toString();
}

public String schema() {
return this.schemaSql;
}

@Override
public SavingsAccrualData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {

final Long savingsId = rs.getLong("savingsId");
final String accountNo = rs.getString("accountNo");
final Long productId = rs.getLong("productId");
final Long officeId = rs.getLong("officeId");
final LocalDate accruedTill = JdbcSupport.getLocalDate(rs, "accruedTill");
final LocalDate postedTill = JdbcSupport.getLocalDate(rs, "postedTill");
final Integer depositTypeId = rs.getInt("depositType");
final EnumOptionData depositType = SavingsEnumerations.depositType(depositTypeId);

final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
currencyNameCode);

final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate");

final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations.compoundingInterestPeriodType(
SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestCompoundingPeriodType")));

final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(
SavingsPostingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));

final EnumOptionData interestCalculationType = SavingsEnumerations
.interestCalculationType(SavingsInterestCalculationType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));

final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations.interestCalculationDaysInYearType(
SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType")));

return new SavingsAccrualData(savingsId, accountNo, depositType, null, productId, officeId, accruedTill, postedTill, currency,
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, BigDecimal.ZERO);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@

import java.time.LocalDate;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.exception.MultiException;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountCharge;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;

public interface SavingsAccrualWritePlatformService {

void addAccrualAccounting(Long savingsId);
void addAccrualEntries(LocalDate tillDate) throws MultiException;

boolean isChargeToBeRecognizedAsAccrual(Collection<Long> chargeIds, SavingsAccountCharge savingsAccountCharge);

Expand Down
Loading

0 comments on commit ded89ce

Please sign in to comment.