FINERACT-1678: Performance improvement of Apply Charge to Overdue Loans COB Business Step

This commit is contained in:
Adam Saghy
2022-08-26 14:43:32 +02:00
committed by Arnold Gálovics
parent 5bf9e1a288
commit 950f6489fe
23 changed files with 260 additions and 238 deletions
@@ -59,7 +59,6 @@ import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGL
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepository;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
@@ -105,7 +104,7 @@ public class AccountingProcessorHelper {
final Long loanId = (Long) accountingBridgeData.get("loanId");
final Long loanProductId = (Long) accountingBridgeData.get("loanProductId");
final Long officeId = (Long) accountingBridgeData.get("officeId");
final CurrencyData currencyData = (CurrencyData) accountingBridgeData.get("currency");
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
final List<LoanTransactionDTO> newLoanTransactions = new ArrayList<>();
boolean isAccountTransfer = (Boolean) accountingBridgeData.get("isAccountTransfer");
@@ -160,8 +159,8 @@ public class AccountingProcessorHelper {
}
return new LoanDTO(loanId, loanProductId, officeId, currencyData.code(), cashBasedAccountingEnabled,
upfrontAccrualBasedAccountingEnabled, periodicAccrualBasedAccountingEnabled, newLoanTransactions);
return new LoanDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, upfrontAccrualBasedAccountingEnabled,
periodicAccrualBasedAccountingEnabled, newLoanTransactions);
}
public SavingsDTO populateSavingsDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
@@ -169,7 +168,7 @@ public class AccountingProcessorHelper {
final Long loanId = (Long) accountingBridgeData.get("savingsId");
final Long loanProductId = (Long) accountingBridgeData.get("savingsProductId");
final Long officeId = (Long) accountingBridgeData.get("officeId");
final CurrencyData currencyData = (CurrencyData) accountingBridgeData.get("currency");
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
final List<SavingsTransactionDTO> newSavingsTransactions = new ArrayList<>();
boolean isAccountTransfer = (Boolean) accountingBridgeData.get("isAccountTransfer");
@@ -230,8 +229,8 @@ public class AccountingProcessorHelper {
}
return new SavingsDTO(loanId, loanProductId, officeId, currencyData.code(), cashBasedAccountingEnabled,
accrualBasedAccountingEnabled, newSavingsTransactions);
return new SavingsDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, accrualBasedAccountingEnabled,
newSavingsTransactions);
}
public SharesDTO populateSharesDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
@@ -239,7 +238,7 @@ public class AccountingProcessorHelper {
final Long shareAccountId = (Long) accountingBridgeData.get("shareAccountId");
final Long shareProductId = (Long) accountingBridgeData.get("shareProductId");
final Long officeId = (Long) accountingBridgeData.get("officeId");
final CurrencyData currencyData = (CurrencyData) accountingBridgeData.get("currency");
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
final List<SharesTransactionDTO> newTransactions = new ArrayList<>();
@SuppressWarnings("unchecked")
@@ -275,7 +274,7 @@ public class AccountingProcessorHelper {
}
return new SharesDTO(shareAccountId, shareProductId, officeId, currencyData.code(), cashBasedAccountingEnabled,
return new SharesDTO(shareAccountId, shareProductId, officeId, currencyCode, cashBasedAccountingEnabled,
accrualBasedAccountingEnabled, newTransactions);
}
@@ -19,11 +19,7 @@
package org.apache.fineract.cob.loan;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
@@ -34,24 +30,16 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor
public class ApplyChargeToOverdueLoansBusinessStep implements LoanCOBBusinessStep {
private final ConfigurationDomainService configurationDomainService;
private final LoanReadPlatformService loanReadPlatformService;
private final LoanWritePlatformService loanWritePlatformService;
@Override
public Loan execute(Loan input) {
final Long penaltyWaitPeriodValue = configurationDomainService.retrievePenaltyWaitPeriod();
final Boolean backdatePenalties = configurationDomainService.isBackdatePenaltiesEnabled();
final Collection<OverdueLoanScheduleData> overdueLoanScheduledInstallments = loanReadPlatformService
.retrieveAllLoansWithOverdueInstallments(penaltyWaitPeriodValue, backdatePenalties);
// TODO: this is very much not effective to get all overdue installments for each loan, a new method needs to be
// implemented for it
Map<Long, List<OverdueLoanScheduleData>> groupedOverdueData = overdueLoanScheduledInstallments.stream()
.collect(Collectors.groupingBy(OverdueLoanScheduleData::getLoanId));
for (Long loanId : groupedOverdueData.keySet()) {
loanWritePlatformService.applyOverdueChargesForLoan(input.getId(), groupedOverdueData.get(loanId));
}
return input;
public Loan execute(Loan loan) {
final Collection<OverdueLoanScheduleData> overdueLoanScheduleDataList = loanReadPlatformService
.retrieveAllOverdueInstallmentsForLoan(loan);
loanWritePlatformService.applyOverdueChargesForLoan(loan.getId(), overdueLoanScheduleDataList);
return loan;
}
@Override
@@ -71,7 +71,6 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
@@ -2394,8 +2393,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return actualChanges;
}
public Collection<Long> findExistingTransactionIds() {
final Collection<Long> ids = new ArrayList<>();
public List<Long> findExistingTransactionIds() {
final List<Long> ids = new ArrayList<>();
List<LoanTransaction> transactions = getLoanTransactions();
for (final LoanTransaction transaction : transactions) {
ids.add(transaction.getId());
@@ -2404,9 +2403,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return ids;
}
public Collection<Long> findExistingReversedTransactionIds() {
public List<Long> findExistingReversedTransactionIds() {
final Collection<Long> ids = new ArrayList<>();
final List<Long> ids = new ArrayList<>();
List<LoanTransaction> transactions = getLoanTransactions();
for (final LoanTransaction transaction : transactions) {
if (transaction.isReversed()) {
@@ -4411,14 +4410,14 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return this.proposedPrincipal;
}
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final List<Long> existingTransactionIds,
public Map<String, Object> deriveAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
final Map<String, Object> accountingBridgeData = new LinkedHashMap<>();
accountingBridgeData.put("loanId", getId());
accountingBridgeData.put("loanProductId", productId());
accountingBridgeData.put("officeId", getOfficeId());
accountingBridgeData.put("currency", currencyData);
accountingBridgeData.put("currencyCode", currencyCode);
accountingBridgeData.put("calculatedInterest", this.summary.getTotalInterestCharged());
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnLoanProduct());
accountingBridgeData.put("upfrontAccrualBasedAccountingEnabled", isUpfrontAccrualAccountingEnabledOnLoanProduct());
@@ -4429,9 +4428,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
for (final LoanTransaction transaction : this.loanTransactions) {
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId())
&& !existingReversedTransactionIds.contains(transaction.getId())) {
newLoanTransactions.add(transaction.toMapData(currencyData));
newLoanTransactions.add(transaction.toMapData(currencyCode));
} else if (!existingTransactionIds.contains(transaction.getId())) {
newLoanTransactions.add(transaction.toMapData(currencyData));
newLoanTransactions.add(transaction.toMapData(currencyCode));
}
}
@@ -111,7 +111,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private final HolidayRepository holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final NoteRepository noteRepository;
private final AccountTransferRepository accountTransferRepository;
@@ -417,10 +416,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer, boolean isLoanToLoanTransfer) {
final MonetaryCurrency currency = loanAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
final Map<String, Object> accountingBridgeData = loanAccount.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
accountingBridgeData.put("isLoanToLoanTransfer", isLoanToLoanTransfer);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@@ -535,14 +533,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
*/
@Override
public void recalculateAccruals(Loan loan) {
boolean isInterestCalcualtionHappened = loan.repaymentScheduleDetail().isInterestRecalculationEnabled();
recalculateAccruals(loan, isInterestCalcualtionHappened);
recalculateAccruals(loan, loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
}
@Override
public void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened) {
public void recalculateAccruals(Loan loan, boolean isInterestCalculationHappened) {
LocalDate accruedTill = loan.getAccruedTill();
if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !isInterestCalcualtionHappened || accruedTill == null || loan.isNpa()
if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !isInterestCalculationHappened || accruedTill == null || loan.isNpa()
|| !loan.status().isActive()) {
return;
}
@@ -552,7 +549,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
if (isOrganisationDateEnabled) {
organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
}
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
Collection<LoanScheduleAccrualData> loanScheduleAccrualList = new ArrayList<>();
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
Long loanId = loan.getId();
Long officeId = loan.getOfficeId();
@@ -571,15 +568,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
accruedTill = DateUtils.getBusinessLocalDate();
}
if (!isOrganisationDateEnabled || organisationStartDate.isBefore(installment.getDueDate())) {
generateLoanScheduleAccrualData(accruedTill, loanScheduleAccrualDatas, loanId, officeId, accrualStartDate,
generateLoanScheduleAccrualData(accruedTill, loanScheduleAccrualList, loanId, officeId, accrualStartDate,
repaymentFrequency, repayEvery, interestCalculatedFrom, loanProductId, currency, currencyData, loanCharges,
installment);
}
}
if (!loanScheduleAccrualDatas.isEmpty()) {
if (!loanScheduleAccrualList.isEmpty()) {
try {
this.loanAccrualPlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas);
this.loanAccrualPlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualList);
} catch (MultiException e) {
String globalisationMessageCode = "error.msg.accrual.exception";
throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e);
@@ -23,9 +23,11 @@ import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Getter
@Table(name = "m_loan_overdue_installment_charge")
public class LoanOverdueInstallmentCharge extends AbstractPersistableCustom {
@@ -54,9 +56,4 @@ public class LoanOverdueInstallmentCharge extends AbstractPersistableCustom {
public void updateLoanRepaymentScheduleInstallment(LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment) {
this.installment = loanRepaymentScheduleInstallment;
}
public LoanRepaymentScheduleInstallment getInstallment() {
return this.installment;
}
}
@@ -656,7 +656,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
transfer, null, outstandingLoanBalance, this.unrecognizedIncomePortion, this.manuallyAdjustedOrReversed);
}
public Map<String, Object> toMapData(final CurrencyData currencyData) {
public Map<String, Object> toMapData(final String currencyCode) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(this.typeOf);
@@ -666,7 +666,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", Boolean.valueOf(isReversed()));
thisTransactionData.put("date", getTransactionDate());
thisTransactionData.put("currency", currencyData);
thisTransactionData.put("currencyCode", currencyCode);
thisTransactionData.put("amount", this.amount);
thisTransactionData.put("netDisbursalAmount", this.loan.getNetDisbursalAmount());
thisTransactionData.put("principalPortion", this.principalPortion);
@@ -42,8 +42,6 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
@@ -99,7 +97,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private final PlatformSecurityContext platformSecurityContext;
private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
private final LoanTransactionRepository loanTransactionRepository;
@@ -125,7 +122,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
final PlatformSecurityContext platformSecurityContext,
final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
final LoanTransactionRepository loanTransactionRepository,
@@ -140,7 +136,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
this.platformSecurityContext = platformSecurityContext;
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
this.loanTransactionRepository = loanTransactionRepository;
@@ -538,10 +533,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private void postJournalEntries(Loan loan, List<Long> existingTransactionIds, List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@@ -33,8 +33,6 @@ import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlat
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
@@ -49,7 +47,6 @@ import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@@ -64,10 +61,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
private final JdbcTemplate jdbcTemplate;
private final DatabaseSpecificSQLGenerator sqlGenerator;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final AppUserRepositoryWrapper userRepository;
private final PlatformSecurityContext context;
private final LoanRepositoryWrapper loanRepositoryWrapper;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
@Override
@Transactional
@@ -289,7 +284,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
accountingBridgeData.put("loanId", loanScheduleAccrualData.getLoanId());
accountingBridgeData.put("loanProductId", loanScheduleAccrualData.getLoanProductId());
accountingBridgeData.put("officeId", loanScheduleAccrualData.getOfficeId());
accountingBridgeData.put("currency", loanScheduleAccrualData.getCurrencyData());
accountingBridgeData.put("currencyCode", loanScheduleAccrualData.getCurrencyData().getCode());
accountingBridgeData.put("cashBasedAccountingEnabled", false);
accountingBridgeData.put("upfrontAccrualBasedAccountingEnabled", false);
accountingBridgeData.put("periodicAccrualBasedAccountingEnabled", true);
@@ -489,10 +484,9 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
private void postJournalEntries(final Loan loan, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
}
@@ -20,9 +20,11 @@ package org.apache.fineract.portfolio.loanaccount.service;
import java.util.Collection;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
public interface LoanChargeReadPlatformService {
@@ -37,7 +39,7 @@ public interface LoanChargeReadPlatformService {
Collection<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId, boolean onlyPaymentPendingCharges);
Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(Long loanId, Long chargeId, Integer periodNumber);
Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(Loan loan, Charge charge, Integer periodNumber);
Collection<LoanChargeData> retrieveLoanChargesForAccural(Long loanId);
@@ -31,15 +31,17 @@ import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.service.ChargeDropdownReadPlatformService;
import org.apache.fineract.portfolio.charge.service.ChargeEnumerations;
import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -51,7 +53,6 @@ import org.springframework.stereotype.Service;
public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService;
@@ -243,16 +244,18 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
}
@Override
public Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(final Long loanId, final Long chargeId,
public Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(final Loan loan, final Charge charge,
final Integer periodNumber) {
String sql = "select oic.frequency_number from m_loan_overdue_installment_charge oic inner join m_loan_charge lc on lc.id=oic.loan_charge_id inner join m_loan_repayment_schedule rs on rs.id = oic.loan_schedule_id inner join m_loan loan on loan.id=rs.loan_id "
+ "where lc.is_active = true and loan.id = ? and rs.installment=?";
Object[] params = { loanId, periodNumber };
if (chargeId != null) {
sql += " and lc.charge_id = ? ";
params = new Object[] { loanId, periodNumber, chargeId };
List<Integer> frequencyNumbers = new ArrayList<>();
for (LoanCharge loanCharge : loan.getLoanCharges()) {
if (loanCharge.isOverdueInstallmentCharge() && charge.equals(loanCharge.getCharge()) && loanCharge.isActive()
&& periodNumber.equals(loanCharge.getOverdueInstallmentCharge().getInstallment().getInstallmentNumber())) {
frequencyNumbers.add(loanCharge.getOverdueInstallmentCharge().getFrequencyNumber());
}
}
return this.jdbcTemplate.queryForList(sql, Integer.class, params);
return frequencyNumbers;
}
@Override
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
@@ -93,6 +94,8 @@ public interface LoanReadPlatformService {
*/
Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallments(Long penaltyWaitPeriod, Boolean backdatePenalties);
Collection<OverdueLoanScheduleData> retrieveAllOverdueInstallmentsForLoan(Loan loan);
Integer retriveLoanCounter(Long groupId, Integer loanType, Long productId);
Integer retriveLoanCounter(Long clientId, Long productId);
@@ -31,7 +31,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
@@ -63,6 +65,7 @@ import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.client.data.ClientData;
@@ -151,7 +154,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final CalendarReadPlatformService calendarReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final PaginationHelper paginationHelper;
private final LoanMapper loaanLoanMapper;
private final LoanMapper loanMapper;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@@ -199,7 +202,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
this.configurationDomainService = configurationDomainService;
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
this.columnValidator = columnValidator;
this.loaanLoanMapper = new LoanMapper(sqlGenerator, delinquencyReadPlatformService);
this.loanMapper = new LoanMapper(sqlGenerator, delinquencyReadPlatformService);
this.sqlGenerator = sqlGenerator;
this.paginationHelper = paginationHelper;
this.delinquencyReadPlatformService = delinquencyReadPlatformService;
@@ -303,7 +306,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
sqlBuilder.append(this.loaanLoanMapper.loanSchema());
sqlBuilder.append(this.loanMapper.loanSchema());
// TODO - for time being this will data scope list of loans returned to
// only loans that have a client associated.
@@ -367,7 +370,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
final Object[] objectArray = extraCriterias.toArray();
final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.loaanLoanMapper);
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.loanMapper);
}
@Override
@@ -1040,7 +1043,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal amount = rs.getBigDecimal("amount");
final String dateFormat = "yyyy-MM-dd";
final String dueDate = rs.getString("dueDate");
final String locale = "en_GB";
final String locale = Locale.ENGLISH.toLanguageTag();
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
@@ -1558,6 +1561,46 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return this.jdbcTemplate.query(sqlBuilder.toString(), rm, penaltyWaitPeriod, penaltyWaitPeriod);
}
@Override
public Collection<OverdueLoanScheduleData> retrieveAllOverdueInstallmentsForLoan(final Loan loan) {
Collection<OverdueLoanScheduleData> list = new ArrayList<>();
if (!loan.isOpen()) {
return list;
}
final Long penaltyWaitPeriod = configurationDomainService.retrievePenaltyWaitPeriod();
final boolean backdatePenalties = configurationDomainService.isBackdatePenaltiesEnabled();
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
if (installment.isObligationsMet() || installment.isRecalculatedInterestComponent()) {
continue;
}
boolean isPenaltyDue = installment.isOverdueOn(DateUtils.getBusinessLocalDate().plusDays(penaltyWaitPeriod + 1));
boolean isDueToday = installment.getDueDate().equals(DateUtils.getBusinessLocalDate().plusDays(penaltyWaitPeriod));
if (isPenaltyDue) {
if (!backdatePenalties && !isDueToday) {
continue;
}
Optional<Charge> penaltyCharge = loan.getLoanProduct().getLoanProductCharges().stream()
.filter((e) -> ChargeTimeType.OVERDUE_INSTALLMENT.getValue().equals(e.getChargeTimeType()) && e.isLoanCharge())
.findFirst();
if (penaltyCharge.isEmpty()) {
continue;
}
list.add(new OverdueLoanScheduleData(loan.getId(), penaltyCharge.get().getId(),
DateUtils.DEFAULT_DATE_FORMATTER.format(installment.getDueDate()), penaltyCharge.get().getAmount(),
DateUtils.DEFAULT_DATE_FORMAT, Locale.ENGLISH.toLanguageTag(),
installment.getPrincipalOutstanding(loan.getCurrency()).getAmount(),
installment.getInterestOutstanding(loan.getCurrency()).getAmount(), installment.getInstallmentNumber()));
}
}
return list;
}
@SuppressWarnings("deprecation")
@Override
public Integer retriveLoanCounter(final Long groupId, final Integer loanType, Long productId) {
@@ -33,6 +33,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -57,8 +58,6 @@ import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChe
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.office.domain.Office;
@@ -120,6 +119,7 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarType;
import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException;
import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeUpdatedException;
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
@@ -235,7 +235,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanAssembler loanAssembler;
private final ChargeRepositoryWrapper chargeRepository;
private final LoanChargeRepository loanChargeRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final CalendarInstanceRepository calendarInstanceRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
@@ -828,7 +827,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = new ArrayList<>();
//
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final LocalDate recalculateFrom = null;
loan.setActualDisbursementDate(null);
@@ -852,7 +850,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
this.accountTransfersWritePlatformService.reverseAllTransactions(loanId, PortfolioAccountType.LOAN);
String noteText = null;
String noteText;
if (command.hasParameter("note")) {
noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
@@ -861,8 +859,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoDisbursalBusinessEvent(loan));
}
@@ -887,9 +885,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
glimRepository.findById(parentLoanId).orElseThrow();
JsonArray repayments = command.arrayOfParameterNamed("formDataArray");
JsonCommand childCommand = null;
JsonCommand childCommand;
CommandProcessingResult result = null;
JsonObject jsonObject = null;
JsonObject jsonObject;
Long[] childLoanId = new Long[repayments.size()];
for (int i = 0; i < repayments.size(); i++) {
@@ -982,7 +980,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
List<Long> transactionIds = new ArrayList<>();
boolean isAccountTransfer = false;
HolidayDetailDTO holidayDetailDTO = null;
Boolean isHolidayValidationDone = false;
boolean isHolidayValidationDone = false;
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
for (final SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) {
if (singleLoanRepaymentCommand != null) {
@@ -991,7 +989,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
singleLoanRepaymentCommand.getTransactionDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
boolean isHolidayEnabled = false;
boolean isHolidayEnabled;
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
allowTransactionsOnNonWorkingDay);
@@ -1033,7 +1031,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Loan loan = this.loanAssembler.assembleFrom(loanId);
if (loan.status().isClosed() && loan.getLoanSubStatus() != null
&& loan.getLoanSubStatus().equals(LoanSubStatus.FORECLOSED.getValue())) {
final String defaultUserMessage = "The loan cannot reopend as it is foreclosed.";
final String defaultUserMessage = "The loan cannot reopened as it is foreclosed.";
throw new LoanForeclosureException("loan.cannot.be.reopened.as.it.is.foreclosured", defaultUserMessage, loanId);
}
checkClientOrGroupActive(loan);
@@ -1121,7 +1119,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
changes.put("note", noteText);
Note note = null;
Note note;
/**
* If a new transaction is not created, associate note with the transaction to be adjusted
**/
@@ -1165,8 +1163,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult waiveInterestOnLoan(final Long loanId, final JsonCommand command) {
AppUser currentUser = getAppUserIfPresent();
this.loanEventApiJsonValidator.validateTransaction(command.json());
final Map<String, Object> changes = new LinkedHashMap<>();
@@ -1304,8 +1300,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult closeLoan(final Long loanId, final JsonCommand command) {
AppUser currentUser = getAppUserIfPresent();
this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json());
final Loan loan = this.loanAssembler.assembleFrom(loanId);
@@ -1373,7 +1367,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// disable all active standing instructions linked to the loan
this.loanAccountDomainService.disableStandingInstructionsLinkedToClosedLoan(loan);
CommandProcessingResult result = null;
CommandProcessingResult result;
if (possibleClosingTransaction != null) {
result = new CommandProcessingResultBuilder().withCommandId(command.commandId())
@@ -1485,7 +1479,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanCharge loanCharge = null;
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
if (disbursementDetail.actualDisbursementDate() == null) {
loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(), null, null, null,
@@ -1501,6 +1495,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
}
if (loanCharge == null) {
final String errorMessage = "Charge with identifier " + chargeDefinition.getId()
+ " cannot be applied: No valid loan disbursement available";
throw new ChargeCannotBeAppliedToException("loan", errorMessage, chargeDefinition.getId());
}
loan.addTrancheLoanCharge(chargeDefinition);
} else {
loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command);
@@ -1607,7 +1606,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private boolean addCharge(final Loan loan, final Charge chargeDefinition, final LoanCharge loanCharge) {
AppUser currentUser = getAppUserIfPresent();
if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
final String errorMessage = "Charge and Loan must have the same currency.";
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
@@ -1634,11 +1632,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
}
boolean isAppliedOnBackDate = false;
if (loanCharge.getDueLocalDate() == null || DateUtils.getBusinessLocalDate().isAfter(loanCharge.getDueLocalDate())) {
isAppliedOnBackDate = true;
}
return isAppliedOnBackDate;
return loanCharge.getDueLocalDate() == null || DateUtils.getBusinessLocalDate().isAfter(loanCharge.getDueLocalDate());
}
@Transactional
@@ -1694,7 +1688,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
final BigDecimal fullRefundAbleAmount = loanChargeValidateRefundAmount(loanCharge, installmentChargeEntry, transactionAmount);
JsonCommand repaymentJsonCommand = adaptLoanChargeRefundCommandForFutherRepaymentProcessing(command, fullRefundAbleAmount);
JsonCommand repaymentJsonCommand = adaptLoanChargeRefundCommandForFurtherRepaymentProcessing(command, fullRefundAbleAmount);
boolean isRecoveryRepayment = false;
String chargeRefundChargeType = "F";
@@ -1709,7 +1703,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Long loanChargeRefundTransactionId = result.resourceId();
LoanTransaction newChargeRefundTxn = null;
for (LoanTransaction chargeRefundTxn : loanCharge.getLoan().getLoanTransactions()) {
if (chargeRefundTxn.getId().equals(loanChargeRefundTransactionId)) {
if (loanChargeRefundTransactionId.equals(chargeRefundTxn.getId())) {
newChargeRefundTxn = chargeRefundTxn;
final BigDecimal appliedRefundAmount = newChargeRefundTxn.getAmount(loanCharge.getLoan().getCurrency()).getAmount()
.multiply(BigDecimal.valueOf(-1));
@@ -1726,7 +1720,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
private JsonCommand adaptLoanChargeRefundCommandForFutherRepaymentProcessing(JsonCommand command, BigDecimal fullRefundAbleAmount) {
private JsonCommand adaptLoanChargeRefundCommandForFurtherRepaymentProcessing(JsonCommand command, BigDecimal fullRefundAbleAmount) {
// creates JsonCommand for onward repayment processing
JsonObject jsonObject = (JsonObject) this.fromApiJsonHelper.parse(command.json());
@@ -1748,14 +1742,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
jsonObject.remove("installmentNumber");
jsonObject.remove("dueDate");
JsonCommand repaymentJsonCommand = JsonCommand.fromExistingCommand(command, jsonObject);
return repaymentJsonCommand;
return JsonCommand.fromExistingCommand(command, jsonObject);
}
private BigDecimal loanChargeValidateRefundAmount(LoanCharge loanCharge, LoanInstallmentCharge installmentChargeEntry,
BigDecimal transactionAmount) {
// if transactionAmount not provided return max refundable amount (amount paid minus previous refunds)
BigDecimal chargeAmountPaid = BigDecimal.ZERO;
BigDecimal chargeAmountPaid;
BigDecimal chargeAmountRefunded = BigDecimal.ZERO;
MonetaryCurrency loanCurrency = loanCharge.getLoan().getCurrency();
if (loanCharge.isInstalmentFee()) {
@@ -1780,7 +1773,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (chargeAmountRefunded.compareTo(chargeAmountPaid) > 0) {
final String errorMessage = "loan.charge.more.refunded.than.paid.unexpected.system.error";
final String details = "Paid: " + chargeAmountPaid.toString() + " Refunded: " + chargeAmountPaid.toString();
final String details = "Paid: " + chargeAmountPaid.toString() + " Refunded: " + chargeAmountPaid;
throw new LoanChargeRefundException(errorMessage, details);
}
@@ -1788,8 +1781,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (transactionAmount != null) { // refund amount was provided.
if (transactionAmount.compareTo(refundableAmount) > 0) {
final String errorMessage = "loan.charge.transaction.amount.is.more.than.is.refundable";
final String details = "transactionAmount: " + transactionAmount.toString() + " Refundable: "
+ refundableAmount.toString();
final String details = "transactionAmount: " + transactionAmount + " Refundable: " + refundableAmount;
throw new LoanChargeRefundException(errorMessage, details);
}
}
@@ -1900,7 +1892,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
businessEventNotifierService.notifyPreBusinessEvent(new LoanWaiveChargeUndoBusinessEvent(loanCharge));
if (loanCharge.isInstalmentFee()) {
LoanInstallmentCharge chargePerInstallment = null;
LoanInstallmentCharge chargePerInstallment;
// final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
if (installmentNumber != null) {
@@ -2004,8 +1996,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult waiveLoanCharge(final Long loanId, final Long loanChargeId, final JsonCommand command) {
AppUser currentUser = getAppUserIfPresent();
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
this.loanEventApiJsonValidator.validateInstallmentChargeTransaction(command.json());
@@ -2056,9 +2046,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Money accruedCharge = Money.zero(loan.getCurrency());
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService
Collection<LoanChargePaidByData> chargePaidByCollection = this.loanChargeReadPlatformService
.retriveLoanChargesPaidBy(loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) {
for (LoanChargePaidByData chargePaidByData : chargePaidByCollection) {
accruedCharge = accruedCharge.plus(chargePaidByData.getAmount());
}
}
@@ -2409,7 +2399,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanUpdateCommand.validate();
final LocalDate dateOfLoanOfficerunAssigned = command.localDateValueOfParameterNamed("unassignedDate");
final LocalDate dateOfLoanOfficerUnassigned = command.localDateValueOfParameterNamed("unassignedDate");
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
@@ -2419,7 +2409,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
businessEventNotifierService.notifyPreBusinessEvent(new LoanRemoveOfficerBusinessEvent(loan));
loan.removeLoanOfficer(dateOfLoanOfficerunAssigned);
loan.removeLoanOfficer(dateOfLoanOfficerUnassigned);
saveLoanWithDataIntegrityViolationChecks(loan);
businessEventNotifierService.notifyPostBusinessEvent(new LoanRemoveOfficerBusinessEvent(loan));
@@ -2438,10 +2428,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@@ -2449,22 +2438,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public void applyMeetingDateChanges(final Calendar calendar, final Collection<CalendarInstance> loanCalendarInstances) {
final Boolean reschedulebasedOnMeetingDates = null;
final Boolean rescheduleBasedOnMeetingDates = null;
final LocalDate presentMeetingDate = null;
final LocalDate newMeetingDate = null;
applyMeetingDateChanges(calendar, loanCalendarInstances, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate);
applyMeetingDateChanges(calendar, loanCalendarInstances, rescheduleBasedOnMeetingDates, presentMeetingDate, newMeetingDate);
}
@Transactional
@Override
public void applyMeetingDateChanges(final Calendar calendar, final Collection<CalendarInstance> loanCalendarInstances,
final Boolean reschedulebasedOnMeetingDates, final LocalDate presentMeetingDate, final LocalDate newMeetingDate) {
final Boolean rescheduleBasedOnMeetingDates, final LocalDate presentMeetingDate, final LocalDate newMeetingDate) {
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final AppUser currentUser = getAppUserIfPresent();
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
final Collection<Integer> loanStatuses = new ArrayList<>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
@@ -2477,7 +2465,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
final List<Loan> loans = this.loanRepositoryWrapper.findByIdsAndLoanStatusAndLoanType(loanIds, loanStatuses, loanTypes);
List<Holiday> holidays = null;
List<Holiday> holidays;
final LocalDate recalculateFrom = null;
// loop through each loan to reschedule the repayment dates
for (final Loan loan : loans) {
@@ -2489,7 +2477,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
Boolean isSkipRepaymentOnFirstMonth = false;
Integer numberOfDays = 0;
int numberOfDays = 0;
boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
if (isSkipRepaymentOnFirstMonthEnabled) {
isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
@@ -2505,9 +2493,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds,
existingReversedTransactionIds);
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
} else if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) {
} else if (rescheduleBasedOnMeetingDates != null && rescheduleBasedOnMeetingDates) {
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
holidays, workingDays, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate,
holidays, workingDays, rescheduleBasedOnMeetingDates, presentMeetingDate, newMeetingDate,
isSkipRepaymentOnFirstMonth, numberOfDays);
} else {
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
@@ -2603,7 +2591,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanToUpdate.updateClientLoanCounter(++currentLoanCounter);
}
if (loanToUpdate.loanProduct().getId().equals(loan.loanProduct().getId())) {
if (Objects.equals(loan.loanProduct().getId(), loanToUpdate.loanProduct().getId())) {
Integer loanProductLoanCounter = loanToUpdate.getLoanProductLoanCounter();
if (newLoanProductCounter > loanProductLoanCounter) {
newLoanProductCounter = loanProductLoanCounter;
@@ -2646,17 +2634,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private void updateLoanCycleCounter(final List<Loan> loansToUpdate, final Loan loan) {
final Integer currentLoancounter = loan.getCurrentLoanCounter();
final Integer currentLoanCounter = loan.getCurrentLoanCounter();
final Integer currentLoanProductCounter = loan.getLoanProductLoanCounter();
for (final Loan loanToUpdate : loansToUpdate) {
if (loan.loanProduct().isIncludeInBorrowerCycle()) {
Integer runningLoancounter = loanToUpdate.getCurrentLoanCounter();
if (runningLoancounter > currentLoancounter) {
loanToUpdate.updateClientLoanCounter(--runningLoancounter);
Integer runningLoanCounter = loanToUpdate.getCurrentLoanCounter();
if (runningLoanCounter > currentLoanCounter) {
loanToUpdate.updateClientLoanCounter(--runningLoanCounter);
}
}
if (loan.loanProduct().getId().equals(loanToUpdate.loanProduct().getId())) {
if (Objects.equals(loan.loanProduct().getId(), loanToUpdate.loanProduct().getId())) {
Integer runningLoanProductCounter = loanToUpdate.getLoanProductLoanCounter();
if (runningLoanProductCounter > currentLoanProductCounter) {
loanToUpdate.updateLoanProductLoanCounter(--runningLoanProductCounter);
@@ -2683,21 +2671,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
@Transactional
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) {
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDataList) {
Loan loan = null;
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
Loan loan = this.loanAssembler.assembleFrom(loanId);
final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();
boolean runInterestRecalculation = false;
LocalDate recalculateFrom = DateUtils.getBusinessLocalDate();
LocalDate lastChargeDate = null;
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) {
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDataList) {
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper, null, null,
null, null, null, loanId, null, null, null, null, null, null);
LoanOverdueDTO overdueDTO = applyChargeToOverdueLoanInstallment(loanId, overdueInstallment.getChargeId(),
overdueInstallment.getPeriodNumber(), command, loan, existingTransactionIds, existingReversedTransactionIds);
LoanOverdueDTO overdueDTO = applyChargeToOverdueLoanInstallment(loan, overdueInstallment.getChargeId(),
overdueInstallment.getPeriodNumber(), command);
loan = overdueDTO.getLoan();
runInterestRecalculation = runInterestRecalculation || overdueDTO.isRunInterestRecalculation();
if (recalculateFrom.isAfter(overdueDTO.getRecalculateFrom())) {
@@ -2735,7 +2723,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
}
saveLoanWithDataIntegrityViolationChecks(loan);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
@@ -2745,7 +2732,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanAccountDomainService.recalculateAccruals(loan);
}
businessEventNotifierService.notifyPostBusinessEvent(new LoanApplyOverdueChargeBusinessEvent(loan));
}
}
@@ -2772,14 +2758,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
public LoanOverdueDTO applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final Integer periodNumber,
final JsonCommand command, Loan loan, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
public LoanOverdueDTO applyChargeToOverdueLoanInstallment(final Loan loan, final Long loanChargeId, final Integer periodNumber,
final JsonCommand command) {
boolean runInterestRecalculation = false;
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(loanChargeId);
Collection<Integer> frequencyNumbers = loanChargeReadPlatformService.retrieveOverdueInstallmentChargeFrequencyNumber(loanId,
chargeDefinition.getId(), periodNumber);
Collection<Integer> frequencyNumbers = loanChargeReadPlatformService.retrieveOverdueInstallmentChargeFrequencyNumber(loan,
chargeDefinition, periodNumber);
Integer feeFrequency = chargeDefinition.feeFrequency();
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
@@ -2787,21 +2772,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod();
final Long penaltyPostingWaitPeriodValue = this.configurationDomainService.retrieveGraceOnPenaltyPostingPeriod();
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
Long diff = penaltyWaitPeriodValue + 1 - penaltyPostingWaitPeriodValue;
long diff = penaltyWaitPeriodValue + 1 - penaltyPostingWaitPeriodValue;
if (diff < 1) {
diff = 1L;
}
LocalDate startDate = dueDate.plusDays(penaltyWaitPeriodValue.intValue() + 1);
Integer frequencyNunber = 1;
int frequencyNumber = 1;
if (feeFrequency == null) {
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff));
scheduleDates.put(frequencyNumber++, startDate.minusDays(diff));
} else {
while (!startDate.isAfter(DateUtils.getBusinessLocalDate())) {
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff));
LocalDate scheduleDate = scheduledDateGenerator.getRepaymentPeriodDate(PeriodFrequencyType.fromInt(feeFrequency),
chargeDefinition.feeInterval(), startDate);
scheduleDates.put(frequencyNumber++, startDate.minusDays(diff));
startDate = scheduleDate;
startDate = scheduledDateGenerator.getRepaymentPeriodDate(PeriodFrequencyType.fromInt(feeFrequency),
chargeDefinition.feeInterval(), startDate);
}
}
@@ -2812,12 +2796,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanRepaymentScheduleInstallment installment = null;
LocalDate lastChargeAppliedDate = dueDate;
if (!scheduleDates.isEmpty()) {
if (loan == null) {
loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
}
installment = loan.fetchRepaymentScheduleInstallment(periodNumber);
lastChargeAppliedDate = installment.getDueDate();
}
@@ -2852,7 +2830,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult undoWriteOff(Long loanId) {
final AppUser currentUser = getAppUserIfPresent();
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
@@ -2972,7 +2949,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
if (loan.getDisbursementDetails().size() > loan.loanProduct().maxTrancheCount()) {
final String errorMessage = "Number of tranche shouldn't be greter than " + loan.loanProduct().maxTrancheCount();
final String errorMessage = "Number of tranche shouldn't be greater than " + loan.loanProduct().maxTrancheCount();
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
loan.loanProduct().maxTrancheCount(), loan.getDisbursementDetails().size());
}
@@ -2983,16 +2960,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private CommandProcessingResult processLoanDisbursementDetail(Loan loan, Long loanId, JsonCommand command,
LoanDisbursementDetails loanDisbursementDetails) {
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();
final Map<String, Object> changes = new LinkedHashMap<>();
LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
ChangedTransactionDetail changedTransactionDetail = null;
AppUser currentUser = getAppUserIfPresent();
if (command.entityId() != null) {
@@ -3302,7 +3276,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoLastDisbursalBusinessEvent(loan));
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
@@ -3312,7 +3285,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
existingReversedTransactionIds, loan);
if (!changes.isEmpty()) {
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
String noteText = null;
String noteText;
if (command.hasParameter("note")) {
noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
@@ -3321,8 +3294,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
existingReversedTransactionIds, isAccountTransfer);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoLastDisbursalBusinessEvent(loan));
}
@@ -3354,7 +3327,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
for (LoanDisbursementDetails loanDisbursementDetails : loan.getDisbursementDetails()) {
if (!loanDisbursementDetails.expectedDisbursementDateAsLocalDate().isAfter(transactionDate)
&& loanDisbursementDetails.actualDisbursementDate() == null) {
final String defaultUserMessage = "The loan with undisbrsed tranche before foreclosure cannot be foreclosed.";
final String defaultUserMessage = "The loan with undisbursed tranche before foreclosure cannot be foreclosed.";
throw new LoanForeclosureException("loan.with.undisbursed.tranche.before.foreclosure.cannot.be.foreclosured",
defaultUserMessage, transactionDate);
}
@@ -40,9 +40,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
@@ -574,11 +572,9 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final boolean backdatedTxnsAllowedTill = false;
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(savingsAccount.getCurrency().getCode(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
@@ -82,7 +82,6 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
@@ -1878,13 +1877,13 @@ public class SavingsAccount extends AbstractPersistableCustom {
}
}
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final Set<Long> existingTransactionIds,
public Map<String, Object> deriveAccountingBridgeData(final String currencyCode, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds, boolean isAccountTransfer, final boolean backdatedTxnsAllowedTill) {
final Map<String, Object> accountingBridgeData = new LinkedHashMap<>();
accountingBridgeData.put("savingsId", getId());
accountingBridgeData.put("savingsProductId", productId());
accountingBridgeData.put("currency", currencyData);
accountingBridgeData.put("currencyCode", currencyCode);
accountingBridgeData.put("officeId", officeId());
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnSavingsProduct());
accountingBridgeData.put("accrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnSavingsProduct());
@@ -1903,9 +1902,9 @@ public class SavingsAccount extends AbstractPersistableCustom {
// Adding new transactions to the array
for (final SavingsAccountTransaction transaction : trans) {
if (transaction.isReversed() && !existingReversedTransactionIds.contains(transaction.getId())) {
newSavingsTransactions.add(transaction.toMapData(currencyData));
newSavingsTransactions.add(transaction.toMapData(currencyCode));
} else if (!existingTransactionIds.contains(transaction.getId())) {
newSavingsTransactions.add(transaction.toMapData(currencyData));
newSavingsTransactions.add(transaction.toMapData(currencyCode));
}
}
@@ -32,9 +32,7 @@ import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlat
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.businessevent.domain.savings.transaction.SavingsDepositBusinessEvent;
@@ -273,10 +271,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds, boolean isAccountTransfer, final boolean backdatedTxnsAllowedTill) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(savingsAccount.getCurrency().getCode(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
@@ -39,7 +39,6 @@ import javax.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.office.domain.Office;
@@ -544,7 +543,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
return transactionAmount.isNotEqualTo(amountToCheck);
}
public Map<String, Object> toMapData(final CurrencyData currencyData) {
public Map<String, Object> toMapData(final String currencyCode) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations.transactionType(this.typeOf);
@@ -552,9 +551,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
thisTransactionData.put("id", getId());
thisTransactionData.put("officeId", this.office.getId());
thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", Boolean.valueOf(isReversed()));
thisTransactionData.put("reversed", isReversed());
thisTransactionData.put("date", getTransactionLocalDate());
thisTransactionData.put("currency", currencyData);
thisTransactionData.put("currencyCode", currencyCode);
thisTransactionData.put("amount", this.amount);
thisTransactionData.put("overdraftAmount", this.overdraftAmount);
@@ -52,9 +52,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavaila
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.organisation.office.domain.Office;
@@ -1369,10 +1367,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(savingsAccount.getCurrency().getCode(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, false);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
@@ -64,9 +64,7 @@ import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.organisation.office.domain.Office;
@@ -1544,10 +1542,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds, final boolean backdatedTxnsAllowedTill) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
boolean isAccountTransfer = false;
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(savingsAccount.getCurrency().getCode(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
@@ -38,8 +38,6 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
import org.apache.fineract.portfolio.businessevent.domain.share.ShareAccountApproveBusinessEvent;
import org.apache.fineract.portfolio.businessevent.domain.share.ShareAccountCreateBusinessEvent;
@@ -130,16 +128,13 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
private Map<String, Object> populateJournalEntries(final ShareAccount account, final Set<ShareAccountTransaction> transactions) {
final Map<String, Object> accountingBridgeData = new HashMap<>();
Boolean cashBasedAccounting = account.getShareProduct().getAccountingType().intValue() == 2 ? Boolean.TRUE : Boolean.FALSE;
Boolean cashBasedAccounting = account.getShareProduct().getAccountingType() == 2 ? Boolean.TRUE : Boolean.FALSE;
accountingBridgeData.put("cashBasedAccountingEnabled", cashBasedAccounting);
accountingBridgeData.put("accrualBasedAccountingEnabled", Boolean.FALSE);
accountingBridgeData.put("shareAccountId", account.getId());
accountingBridgeData.put("shareProductId", account.getShareProduct().getId());
accountingBridgeData.put("officeId", account.getOfficeId());
MonetaryCurrency currency = account.getCurrency();
final CurrencyData currencyData = new CurrencyData(currency.getCode(), "", currency.getDigitsAfterDecimal(),
currency.getCurrencyInMultiplesOf(), "", "");
accountingBridgeData.put("currency", currencyData);
accountingBridgeData.put("currencyCode", account.getCurrency().getCode());
final List<Map<String, Object>> newTransactionsMap = new ArrayList<>();
accountingBridgeData.put("newTransactions", newTransactionsMap);
@@ -4167,7 +4167,7 @@ public class ClientLoanIntegrationTest {
todaysDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
Integer overdueFeeChargeId = ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
ChargesHelper.getLoanOverdueFeeJSONWithCalculattionTypePercentage("10"));
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("10"));
Assertions.assertNotNull(overdueFeeChargeId);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
@@ -230,7 +230,8 @@ public class SchedulerJobsTestResults {
final Integer loanProductID = createLoanProduct(null);
Assertions.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), savingsId.toString());
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), savingsId.toString(),
"10 January 2013");
Assertions.assertNotNull(loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -281,7 +282,7 @@ public class SchedulerJobsTestResults {
final Integer loanProductID = createLoanProduct(null);
Assertions.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2013");
Assertions.assertNotNull(loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -378,7 +379,7 @@ public class SchedulerJobsTestResults {
}
@Test
public void testUpdateAccountingRunningBalancesJobOutcome() throws InterruptedException {
public void testUpdateAccountingRunningBalancesJobOutcome() {
this.savingsAccountHelper = new SavingsAccountHelper(requestSpec, responseSpec);
final Account assetAccount = this.accountHelper.createAssetAccount();
@@ -421,7 +422,7 @@ public class SchedulerJobsTestResults {
}
@Test
public void testUpdateLoanArrearsAgingJobOutcome() throws InterruptedException {
public void testUpdateLoanArrearsAgingJobOutcome() {
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
@@ -430,7 +431,7 @@ public class SchedulerJobsTestResults {
final Integer loanProductID = createLoanProduct(null);
Assertions.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2013");
Assertions.assertNotNull(loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -560,18 +561,18 @@ public class SchedulerJobsTestResults {
final Integer loanProductID = createLoanProduct(overdueFeeChargeId.toString());
Assertions.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2020");
Assertions.assertNotNull(loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanStatusHashMap = this.loanTransactionHelper.approveLoan(AccountTransferTest.LOAN_APPROVAL_DATE, loanID);
loanStatusHashMap = this.loanTransactionHelper.approveLoan("01 March 2020", loanID);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
String loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(AccountTransferTest.LOAN_APPROVAL_DATE_PLUS_ONE,
loanID, JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020", loanID,
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
String JobName = "Apply penalty to overdue loans";
@@ -592,6 +593,59 @@ public class SchedulerJobsTestResults {
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
}
@Test
public void testLoanCOBJobOutcome() {
this.savingsAccountHelper = new SavingsAccountHelper(requestSpec, responseSpec);
this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
Assertions.assertNotNull(clientID);
Integer overdueFeeChargeId = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
Assertions.assertNotNull(overdueFeeChargeId);
final Integer loanProductID = createLoanProduct(overdueFeeChargeId.toString());
Assertions.assertNotNull(loanProductID);
List<Integer> loanIDs = new ArrayList<>();
HashMap loanStatusHashMap;
for (int i = 0; i < 10; i++) {
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2020");
Assertions.assertNotNull(loanID);
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanStatusHashMap = this.loanTransactionHelper.approveLoan("01 March 2020", loanID);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
String loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020", loanID,
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
loanIDs.add(loanID);
}
String jobName = "Loan COB";
this.schedulerJobHelper.executeAndAwaitJob(jobName);
for (Integer loanId : loanIDs) {
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
loanId);
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(2).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(3).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(4).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
}
}
@Test
public void testAvoidUnncessaryPenaltyWhenAmountZeroForOverdueLoansJobOutcome() throws InterruptedException {
this.savingsAccountHelper = new SavingsAccountHelper(requestSpec, responseSpec);
@@ -601,13 +655,13 @@ public class SchedulerJobsTestResults {
Assertions.assertNotNull(clientID);
Integer overdueFeeChargeId = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanOverdueFeeJSONWithCalculattionTypePercentage("0.000001"));
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("0.000001"));
Assertions.assertNotNull(overdueFeeChargeId);
final Integer loanProductID = createLoanProduct(overdueFeeChargeId.toString());
Assertions.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2013");
Assertions.assertNotNull(loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -659,7 +713,7 @@ public class SchedulerJobsTestResults {
final Integer loanProductID = createLoanProduct(null);
Assertions.assertNotNull(loanProductID);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null);
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2013");
Assertions.assertNotNull(loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -794,7 +848,7 @@ public class SchedulerJobsTestResults {
return collateral;
}
private Integer applyForLoanApplication(final String clientID, final String loanProductID, final String savingsID) {
private Integer applyForLoanApplication(final String clientID, final String loanProductID, final String savingsID, final String date) {
List<HashMap> collaterals = new ArrayList<>();
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
@@ -808,7 +862,7 @@ public class SchedulerJobsTestResults {
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("4").withRepaymentEveryAfter("1")
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withAmortizationTypeAsEqualInstallments()
.withInterestTypeAsDecliningBalance().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
.withExpectedDisbursementDate("10 January 2013").withSubmittedOnDate("10 January 2013").withCollaterals(collaterals)
.withExpectedDisbursementDate(date).withSubmittedOnDate(date).withCollaterals(collaterals)
.build(clientID, loanProductID, savingsID);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
@@ -302,7 +302,7 @@ public final class ChargesHelper {
return chargesCreateJson;
}
public static String getLoanOverdueFeeJSONWithCalculattionTypePercentage(String penaltyPercentageAmount) {
public static String getLoanOverdueFeeJSONWithCalculationTypePercentage(String penaltyPercentageAmount) {
final HashMap<String, Object> map = populateDefaultsForLoan();
map.put("penalty", ChargesHelper.PENALTY);
map.put("amount", penaltyPercentageAmount);