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