Merge pull request #1106 from pramodn02/develop

MIFOSX-1589 : accounting changes for waive interest and waive charge
This commit is contained in:
Vishwas Babu A J
2014-10-15 07:48:41 -07:00
23 changed files with 844 additions and 113 deletions
@@ -2444,11 +2444,11 @@ public class ClientLoanIntegrationTest {
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID); loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
validateCharge(flatInstallmentFee, loanCharges, "50", "100.00", "50.0", "50.0"); validateCharge(flatInstallmentFee, loanCharges, "50", "100.00", "50.0", "50.0");
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(Float.valueOf("50.0"), /*this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(Float.valueOf("50.0"),
JournalEntry.TransactionType.CREDIT)); JournalEntry.TransactionType.CREDIT));
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011", this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
new JournalEntry(Float.valueOf("50.0"), JournalEntry.TransactionType.DEBIT)); new JournalEntry(Float.valueOf("50.0"), JournalEntry.TransactionType.DEBIT));
*/
final String jobName = "Add Accrual Transactions"; final String jobName = "Add Accrual Transactions";
try { try {
this.schedulerJobHelper.excuteJob(jobName); this.schedulerJobHelper.excuteJob(jobName);
@@ -2645,10 +2645,10 @@ public class ClientLoanIntegrationTest {
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID); loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
validateCharge(percentageInstallmentFee, loanCharges, "1", "61.19", "29.11", "29.70"); validateCharge(percentageInstallmentFee, loanCharges, "1", "61.19", "29.11", "29.70");
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(Float.valueOf("29.7"), /*this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(Float.valueOf("29.7"),
JournalEntry.TransactionType.CREDIT)); JournalEntry.TransactionType.CREDIT));
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011", this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
new JournalEntry(Float.valueOf("29.7"), JournalEntry.TransactionType.DEBIT)); new JournalEntry(Float.valueOf("29.7"), JournalEntry.TransactionType.DEBIT));*/
final String jobName = "Add Accrual Transactions"; final String jobName = "Add Accrual Transactions";
try { try {
@@ -2850,10 +2850,10 @@ public class ClientLoanIntegrationTest {
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID); loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
validateCharge(amountPlusInterestPercentageInstallmentFee, loanCharges, "1", "63.02", "31.51", "31.51"); validateCharge(amountPlusInterestPercentageInstallmentFee, loanCharges, "1", "63.02", "31.51", "31.51");
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry( /*this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(
Float.valueOf("31.51"), JournalEntry.TransactionType.CREDIT)); Float.valueOf("31.51"), JournalEntry.TransactionType.CREDIT));
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011", this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.DEBIT)); new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.DEBIT));*/
final String jobName = "Add Accrual Transactions"; final String jobName = "Add Accrual Transactions";
try { try {
@@ -216,19 +216,21 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
* Single DEBIT transaction for write-offs or Repayments (and their * Single DEBIT transaction for write-offs or Repayments (and their
* reversals) * reversals)
***/ ***/
if (writeOff) { if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, if (writeOff) {
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal);
} else {
if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal); transactionDate, totalDebitAmount, isReversal);
} else { } else {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, if (loanTransactionDTO.isAccountTransfer()) {
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
transactionDate, totalDebitAmount, isReversal); FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal);
} else {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal);
}
} }
} }
} }
@@ -67,6 +67,8 @@ public class LoanChargeData {
private BigDecimal amountAccrued; private BigDecimal amountAccrued;
private BigDecimal amountUnrecognized;
public static LoanChargeData template(final Collection<ChargeData> chargeOptions) { public static LoanChargeData template(final Collection<ChargeData> chargeOptions) {
return new LoanChargeData(null, null, null, null, null, null, null, null, chargeOptions, false, null, false, false, null, null, return new LoanChargeData(null, null, null, null, null, null, null, null, chargeOptions, false, null, false, false, null, null,
null, null, null); null, null, null);
@@ -126,6 +128,7 @@ public class LoanChargeData {
this.loanId = loanId; this.loanId = loanId;
this.installmentChargeData = installmentChargeData; this.installmentChargeData = installmentChargeData;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null;
} }
private LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount, private LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
@@ -169,6 +172,7 @@ public class LoanChargeData {
this.maxCap = maxCap; this.maxCap = maxCap;
this.installmentChargeData = installmentChargeData; this.installmentChargeData = installmentChargeData;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null;
} }
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, EnumOptionData chargeTimeType, public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, EnumOptionData chargeTimeType,
@@ -199,6 +203,7 @@ public class LoanChargeData {
this.maxCap = null; this.maxCap = null;
this.installmentChargeData = installmentChargeData; this.installmentChargeData = installmentChargeData;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null;
} }
public LoanChargeData(final Long id, final Long chargeId, final LocalDate dueAsOfDate, EnumOptionData chargeTimeType, public LoanChargeData(final Long id, final Long chargeId, final LocalDate dueAsOfDate, EnumOptionData chargeTimeType,
@@ -229,6 +234,37 @@ public class LoanChargeData {
this.maxCap = null; this.maxCap = null;
this.installmentChargeData = null; this.installmentChargeData = null;
this.amountAccrued = amountAccrued; this.amountAccrued = amountAccrued;
this.amountUnrecognized = null;
}
public LoanChargeData(final BigDecimal amountUnrecognized, final LoanChargeData chargeData) {
this.id = chargeData.id;
this.chargeId = chargeData.chargeId;
this.name = null;
this.currency = null;
this.amount = chargeData.amount;
this.amountPaid = null;
this.amountWaived = chargeData.amountWaived;
this.amountWrittenOff = null;
this.amountOutstanding = null;
this.chargeTimeType = chargeData.chargeTimeType;
this.dueDate = chargeData.dueDate;
this.chargeCalculationType = null;
this.percentage = null;
this.amountPercentageAppliedTo = null;
this.penalty = chargeData.penalty;
this.chargePaymentMode = null;
this.paid = false;
this.waived = false;
this.amountOrPercentage = null;
this.chargeOptions = null;
this.chargePayable = false;
this.loanId = null;
this.minCap = null;
this.maxCap = null;
this.installmentChargeData = null;
this.amountAccrued = chargeData.amountAccrued;
this.amountUnrecognized = amountUnrecognized;
} }
public LoanChargeData(LoanChargeData chargeData, Collection<LoanInstallmentChargeData> installmentChargeData) { public LoanChargeData(LoanChargeData chargeData, Collection<LoanInstallmentChargeData> installmentChargeData) {
@@ -258,6 +294,7 @@ public class LoanChargeData {
this.loanId = chargeData.loanId; this.loanId = chargeData.loanId;
this.installmentChargeData = installmentChargeData; this.installmentChargeData = installmentChargeData;
this.amountAccrued = chargeData.amountAccrued; this.amountAccrued = chargeData.amountAccrued;
this.amountUnrecognized = chargeData.amountUnrecognized;
} }
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOrPercentage) { public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOrPercentage) {
@@ -287,6 +324,7 @@ public class LoanChargeData {
this.maxCap = null; this.maxCap = null;
this.installmentChargeData = null; this.installmentChargeData = null;
this.amountAccrued = null; this.amountAccrued = null;
this.amountUnrecognized = null;
} }
public boolean isChargePayable() { public boolean isChargePayable() {
@@ -349,8 +387,11 @@ public class LoanChargeData {
return this.chargeId; return this.chargeId;
} }
public BigDecimal getAmountWaived() { public BigDecimal getAmountWaived() {
return this.amountWaived; return this.amountWaived;
} }
public BigDecimal getAmountUnrecognized() {
return this.amountUnrecognized;
}
} }
@@ -0,0 +1,47 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.data;
import java.math.BigDecimal;
public class LoanChargePaidByData {
private final Long id;
private final BigDecimal amount;
private final Integer installmentNumber;
private final Long chargeId;
private final Long transactionId;
public LoanChargePaidByData(final Long id, final BigDecimal amount, final Integer installmentNumber, final Long chargeId,
final Long transactionId) {
this.id = id;
this.amount = amount;
this.installmentNumber = installmentNumber;
this.chargeId = chargeId;
this.transactionId = transactionId;
}
public Long getId() {
return this.id;
}
public BigDecimal getAmount() {
return this.amount;
}
public Integer getInstallmentNumber() {
return this.installmentNumber;
}
public Long getChargeId() {
return this.chargeId;
}
public Long getTransactionId() {
return this.transactionId;
}
}
@@ -19,6 +19,9 @@ public class LoanInstallmentChargeData {
private final boolean paid; private final boolean paid;
private final boolean waived; private final boolean waived;
private BigDecimal amountAccrued;
private BigDecimal amountUnrecognized;
public LoanInstallmentChargeData(final Integer installmentNumber, final LocalDate dueDate, final BigDecimal amount, public LoanInstallmentChargeData(final Integer installmentNumber, final LocalDate dueDate, final BigDecimal amount,
final BigDecimal amountOutstanding, BigDecimal amountWaived, final boolean paid, final boolean waived) { final BigDecimal amountOutstanding, BigDecimal amountWaived, final boolean paid, final boolean waived) {
this.installmentNumber = installmentNumber; this.installmentNumber = installmentNumber;
@@ -30,6 +33,31 @@ public class LoanInstallmentChargeData {
this.amountWaived = amountWaived; this.amountWaived = amountWaived;
} }
public LoanInstallmentChargeData(final Integer installmentNumber, final LocalDate dueDate, final BigDecimal amount,
final BigDecimal amountOutstanding, final BigDecimal amountWaived, final boolean paid, final boolean waived,
final BigDecimal amountAccrued) {
this.installmentNumber = installmentNumber;
this.dueDate = dueDate;
this.amount = amount;
this.amountOutstanding = amountOutstanding;
this.paid = paid;
this.waived = waived;
this.amountWaived = amountWaived;
this.amountAccrued = amountAccrued;
}
public LoanInstallmentChargeData(final LoanInstallmentChargeData installmentChargeData, final BigDecimal amountUnrecognized) {
this.installmentNumber = installmentChargeData.installmentNumber;
this.dueDate = installmentChargeData.dueDate;
this.amount = installmentChargeData.amount;
this.amountOutstanding = installmentChargeData.amountOutstanding;
this.paid = installmentChargeData.paid;
this.waived = installmentChargeData.waived;
this.amountWaived = installmentChargeData.amountWaived;
this.amountAccrued = installmentChargeData.amountAccrued;
this.amountUnrecognized = amountUnrecognized;
}
public Integer getInstallmentNumber() { public Integer getInstallmentNumber() {
return this.installmentNumber; return this.installmentNumber;
} }
@@ -54,4 +82,12 @@ public class LoanInstallmentChargeData {
return this.amountWaived; return this.amountWaived;
} }
public BigDecimal getAmountAccrued() {
return this.amountAccrued;
}
public BigDecimal getAmountUnrecognized() {
return this.amountUnrecognized;
}
} }
@@ -28,6 +28,7 @@ public class LoanScheduleAccrualData {
private final BigDecimal interestIncome; private final BigDecimal interestIncome;
private final BigDecimal feeIncome; private final BigDecimal feeIncome;
private final BigDecimal penaltyIncome; private final BigDecimal penaltyIncome;
private final BigDecimal waivedInterestIncome;
private final BigDecimal accruedInterestIncome; private final BigDecimal accruedInterestIncome;
private final BigDecimal accruedFeeIncome; private final BigDecimal accruedFeeIncome;
private final BigDecimal accruedPenaltyIncome; private final BigDecimal accruedPenaltyIncome;
@@ -37,12 +38,14 @@ public class LoanScheduleAccrualData {
private Map<LoanChargeData, BigDecimal> applicableCharges; private Map<LoanChargeData, BigDecimal> applicableCharges;
private BigDecimal dueDateFeeIncome; private BigDecimal dueDateFeeIncome;
private BigDecimal dueDatePenaltyIncome; private BigDecimal dueDatePenaltyIncome;
private BigDecimal accruableIncome;
public LoanScheduleAccrualData(final Long loanId, final Long officeId, final Integer installmentNumber, final LocalDate accruedTill, public LoanScheduleAccrualData(final Long loanId, final Long officeId, final Integer installmentNumber, final LocalDate accruedTill,
final PeriodFrequencyType repaymentFrequency, final Integer repayEvery, final LocalDate dueDate, final LocalDate fromDate, final PeriodFrequencyType repaymentFrequency, final Integer repayEvery, final LocalDate dueDate, final LocalDate fromDate,
final Long repaymentScheduleId, final Long loanProductId, final BigDecimal interestIncome, final BigDecimal feeIncome, final Long repaymentScheduleId, final Long loanProductId, final BigDecimal interestIncome, final BigDecimal feeIncome,
final BigDecimal penaltyIncome, final BigDecimal accruedInterestIncome, final BigDecimal accruedFeeIncome, final BigDecimal penaltyIncome, final BigDecimal accruedInterestIncome, final BigDecimal accruedFeeIncome,
final BigDecimal accruedPenaltyIncome, final CurrencyData currencyData, final LocalDate interestCalculatedFrom) { final BigDecimal accruedPenaltyIncome, final CurrencyData currencyData, final LocalDate interestCalculatedFrom,
final BigDecimal waivedInterestIncome) {
this.loanId = loanId; this.loanId = loanId;
this.installmentNumber = installmentNumber; this.installmentNumber = installmentNumber;
this.officeId = officeId; this.officeId = officeId;
@@ -61,6 +64,7 @@ public class LoanScheduleAccrualData {
this.repaymentFrequency = repaymentFrequency; this.repaymentFrequency = repaymentFrequency;
this.repayEvery = repayEvery; this.repayEvery = repayEvery;
this.interestCalculatedFrom = interestCalculatedFrom; this.interestCalculatedFrom = interestCalculatedFrom;
this.waivedInterestIncome = waivedInterestIncome;
} }
public Long getLoanId() { public Long getLoanId() {
@@ -158,4 +162,20 @@ public class LoanScheduleAccrualData {
this.dueDatePenaltyIncome = dueDatePenaltyIncome; this.dueDatePenaltyIncome = dueDatePenaltyIncome;
} }
public BigDecimal getWaivedInterestIncome() {
return this.waivedInterestIncome;
}
public BigDecimal getAccruableIncome () {
return this.accruableIncome;
}
public void updateAccruableIncome (BigDecimal accruableIncome ) {
this.accruableIncome = accruableIncome ;
}
} }
@@ -36,6 +36,7 @@ public class LoanTransactionData {
private final BigDecimal feeChargesPortion; private final BigDecimal feeChargesPortion;
private final BigDecimal penaltyChargesPortion; private final BigDecimal penaltyChargesPortion;
private final BigDecimal overpaymentPortion; private final BigDecimal overpaymentPortion;
private final BigDecimal unrecognizedIncomePortion;
private final String externalId; private final String externalId;
private final AccountTransferData transfer; private final AccountTransferData transfer;
private final BigDecimal fixedEmiAmount; private final BigDecimal fixedEmiAmount;
@@ -52,45 +53,47 @@ public class LoanTransactionData {
loanTransactionData.type, loanTransactionData.paymentDetailData, loanTransactionData.currency, loanTransactionData.date, loanTransactionData.type, loanTransactionData.paymentDetailData, loanTransactionData.currency, loanTransactionData.date,
loanTransactionData.amount, loanTransactionData.principalPortion, loanTransactionData.interestPortion, loanTransactionData.amount, loanTransactionData.principalPortion, loanTransactionData.interestPortion,
loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, loanTransactionData.overpaymentPortion, loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, loanTransactionData.overpaymentPortion,
paymentTypeOptions, loanTransactionData.externalId, loanTransactionData.transfer, loanTransactionData.fixedEmiAmount, loanTransactionData.unrecognizedIncomePortion, paymentTypeOptions, loanTransactionData.externalId,
loanTransactionData.outstandingLoanBalance); loanTransactionData.transfer, loanTransactionData.fixedEmiAmount, loanTransactionData.outstandingLoanBalance);
} }
public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final String externalId, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final String externalId,
final AccountTransferData transfer, BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance) { final AccountTransferData transfer, BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance,
final BigDecimal unrecognizedIncomePortion) {
this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overpaymentPortion, null, externalId, transfer, fixedEmiAmount, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, transfer,
outstandingLoanBalance); fixedEmiAmount, outstandingLoanBalance);
} }
public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, BigDecimal unrecognizedIncomePortion,
final Collection<CodeValueData> paymentTypeOptions, final String externalId, final AccountTransferData transfer, final Collection<CodeValueData> paymentTypeOptions, final String externalId, final AccountTransferData transfer,
final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance) { final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance) {
this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overpaymentPortion, paymentTypeOptions, externalId, transfer, fixedEmiAmount, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, paymentTypeOptions, externalId,
outstandingLoanBalance, null); transfer, fixedEmiAmount, outstandingLoanBalance, null);
} }
public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final String externalId, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final BigDecimal unrecognizedIncomePortion,
final AccountTransferData transfer, BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, LocalDate submittedOnDate) { final String externalId, final AccountTransferData transfer, BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance,
LocalDate submittedOnDate) {
this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overpaymentPortion, null, externalId, transfer, fixedEmiAmount, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, transfer,
outstandingLoanBalance, submittedOnDate); fixedEmiAmount, outstandingLoanBalance, submittedOnDate);
} }
public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final BigDecimal unrecognizedIncomePortion,
final Collection<CodeValueData> paymentTypeOptions, final String externalId, final AccountTransferData transfer, final Collection<CodeValueData> paymentTypeOptions, final String externalId, final AccountTransferData transfer,
final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, final LocalDate submittedOnDate) { final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, final LocalDate submittedOnDate) {
this.id = id; this.id = id;
@@ -105,6 +108,7 @@ public class LoanTransactionData {
this.interestPortion = interestPortion; this.interestPortion = interestPortion;
this.feeChargesPortion = feeChargesPortion; this.feeChargesPortion = feeChargesPortion;
this.penaltyChargesPortion = penaltyChargesPortion; this.penaltyChargesPortion = penaltyChargesPortion;
this.unrecognizedIncomePortion = unrecognizedIncomePortion;
this.paymentTypeOptions = paymentTypeOptions; this.paymentTypeOptions = paymentTypeOptions;
this.externalId = externalId; this.externalId = externalId;
this.transfer = transfer; this.transfer = transfer;
@@ -114,6 +118,13 @@ public class LoanTransactionData {
this.submittedOnDate = submittedOnDate; this.submittedOnDate = submittedOnDate;
} }
public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount,
BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion,
BigDecimal overPaymentPortion, BigDecimal unrecognizedIncomePortion, BigDecimal outstandingLoanBalance) {
this(id, null, null, transactionType, null, null, date, totalAmount, principalPortion, interestPortion, feeChargesPortion,
penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null);
}
public LocalDate dateOf() { public LocalDate dateOf() {
return this.date; return this.date;
} }
@@ -121,4 +132,19 @@ public class LoanTransactionData {
public boolean isNotDisbursement() { public boolean isNotDisbursement() {
return Integer.valueOf(1).equals(this.type.id()); return Integer.valueOf(1).equals(this.type.id());
} }
public BigDecimal getAmount() {
return this.amount;
}
public BigDecimal getUnrecognizedIncomePortion() {
return this.unrecognizedIncomePortion;
}
public BigDecimal getInterestPortion() {
return this.interestPortion;
}
} }
@@ -592,8 +592,9 @@ public class Loan extends AbstractPersistable<Long> {
final LoanTransaction applyLoanChargeTransaction = LoanTransaction.accrueLoanCharge(this, getOffice(), chargeAmount, final LoanTransaction applyLoanChargeTransaction = LoanTransaction.accrueLoanCharge(this, getOffice(), chargeAmount,
transactionDate, feeCharges, penaltyCharges); transactionDate, feeCharges, penaltyCharges);
Integer installmentNumber = null;
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(applyLoanChargeTransaction, loanCharge, loanCharge.getAmount( final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(applyLoanChargeTransaction, loanCharge, loanCharge.getAmount(
getCurrency()).getAmount()); getCurrency()).getAmount(), installmentNumber);
applyLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy); applyLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
this.loanTransactions.add(applyLoanChargeTransaction); this.loanTransactions.add(applyLoanChargeTransaction);
return applyLoanChargeTransaction; return applyLoanChargeTransaction;
@@ -603,7 +604,7 @@ public class Loan extends AbstractPersistable<Long> {
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) { final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) {
chargesPayment.updateLoan(this); chargesPayment.updateLoan(this);
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency()) final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency())
.getAmount()); .getAmount(), installmentNumber);
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
this.loanTransactions.add(chargesPayment); this.loanTransactions.add(chargesPayment);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT,
@@ -839,7 +840,8 @@ public class Loan extends AbstractPersistable<Long> {
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final Integer loanInstallmentNumber, final List<Holiday> holidays, final WorkingDays workingDays, final Integer loanInstallmentNumber, final List<Holiday> holidays, final WorkingDays workingDays,
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency, final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) { final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation,
final Money accruedCharge) {
validateLoanIsNotClosed(loanCharge); validateLoanIsNotClosed(loanCharge);
@@ -847,10 +849,22 @@ public class Loan extends AbstractPersistable<Long> {
changes.put("amount", amountWaived.getAmount()); changes.put("amount", amountWaived.getAmount());
Money feeChargesWaived = amountWaived; Money unrecognizedIncome = amountWaived.zero();
Money chargeComponent = amountWaived;
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
Money receivableCharge = accruedCharge.minus(loanCharge.getAmountPaid(getCurrency()));
if (receivableCharge.isLessThanZero()) {
receivableCharge = amountWaived.zero();
}
if (amountWaived.isGreaterThan(receivableCharge)) {
chargeComponent = receivableCharge;
unrecognizedIncome = amountWaived.minus(receivableCharge);
}
}
Money feeChargesWaived = chargeComponent;
Money penaltyChargesWaived = Money.zero(loanCurrency()); Money penaltyChargesWaived = Money.zero(loanCurrency());
if (loanCharge.isPenaltyCharge()) { if (loanCharge.isPenaltyCharge()) {
penaltyChargesWaived = amountWaived; penaltyChargesWaived = chargeComponent;
feeChargesWaived = Money.zero(loanCurrency()); feeChargesWaived = Money.zero(loanCurrency());
} }
@@ -865,7 +879,10 @@ public class Loan extends AbstractPersistable<Long> {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, getOffice(), amountWaived, final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, getOffice(), amountWaived,
transactionDate, feeChargesWaived, penaltyChargesWaived); transactionDate, feeChargesWaived, penaltyChargesWaived, unrecognizedIncome);
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge, waiveLoanChargeTransaction
.getAmount(getCurrency()).getAmount(), loanInstallmentNumber);
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
this.loanTransactions.add(waiveLoanChargeTransaction); this.loanTransactions.add(waiveLoanChargeTransaction);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled() if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
@@ -2126,13 +2143,15 @@ public class Loan extends AbstractPersistable<Long> {
Money disbursentMoney = Money.zero(getCurrency()); Money disbursentMoney = Money.zero(getCurrency());
final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, null, disbursedOn, final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, null, disbursedOn,
null); null);
final Integer installmentNumber = null;
for (final LoanCharge charge : charges()) { for (final LoanCharge charge : charges()) {
if (charge.isDueAtDisbursement()) { if (charge.isDueAtDisbursement()) {
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero() if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()
&& !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) { && !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
charge.markAsFullyPaid(); charge.markAsFullyPaid();
// Add "Loan Charge Paid By" details to this transaction // Add "Loan Charge Paid By" details to this transaction
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount()); final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount(),
installmentNumber);
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
disbursentMoney = disbursentMoney.plus(charge.amount()); disbursentMoney = disbursentMoney.plus(charge.amount());
} }
@@ -2190,7 +2209,7 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
@SuppressWarnings("null") @SuppressWarnings("null")
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount()); final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount(), null);
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
final Money zero = Money.zero(getCurrency()); final Money zero = Money.zero(getCurrency());
chargesPayment.updateComponents(zero, zero, charge.getAmount(getCurrency()), zero); chargesPayment.updateComponents(zero, zero, charge.getAmount(getCurrency()), zero);
@@ -2741,7 +2760,8 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
return LoanTransaction.waiver(getOffice(), this, possibleInterestToWaive, transactionDate); return LoanTransaction.waiver(getOffice(), this, possibleInterestToWaive, transactionDate, possibleInterestToWaive,
possibleInterestToWaive.zero());
} }
public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail, public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail,
@@ -3540,7 +3560,7 @@ public class Loan extends AbstractPersistable<Long> {
|| isAccountingDisabledOnLoanProduct(); || isAccountingDisabledOnLoanProduct();
} }
private Boolean isPeriodicAccrualAccountingEnabledOnLoanProduct() { public Boolean isPeriodicAccrualAccountingEnabledOnLoanProduct() {
return this.loanProduct.isPeriodicAccrualAccountingEnabled(); return this.loanProduct.isPeriodicAccrualAccountingEnabled();
} }
@@ -3587,6 +3607,28 @@ public class Loan extends AbstractPersistable<Long> {
return accountingBridgeData; return accountingBridgeData;
} }
public Money getReceivableInterest(final LocalDate tillDate) {
Money receivableInterest = Money.zero(getCurrency());
for (final LoanTransaction transaction : this.loanTransactions) {
if (transaction.isNotReversed() && !transaction.isRepaymentAtDisbursement() && !transaction.isDisbursement()
&& !transaction.getTransactionDate().isAfter(tillDate)) {
if (transaction.isAccrual()) {
receivableInterest = receivableInterest.plus(transaction.getInterestPortion(getCurrency()));
} else if (transaction.isRepayment() || transaction.isInterestWaiver()) {
receivableInterest = receivableInterest.minus(transaction.getInterestPortion(getCurrency()));
}
}
if (receivableInterest.isLessThanZero()) {
receivableInterest = receivableInterest.zero();
}
/* if (transaction.getTransactionDate().isAfter(tillDate) && transaction.isAccrual()) {
final String errorMessage = "The date on which a loan is interest waived cannot be in after accrual transactions.";
throw new InvalidLoanStateTransitionException("waive", "cannot.be.after.accrual.date", errorMessage, tillDate);
}*/
}
return receivableInterest;
}
public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanSummaryWrapper loanSummaryWrapper, public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory) { final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory) {
this.loanLifecycleStateMachine = loanLifecycleStateMachine; this.loanLifecycleStateMachine = loanLifecycleStateMachine;
@@ -463,7 +463,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(), accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(),
installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment
.getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(), .getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(),
accruedInterestIncome, accruedFeeIncome, accruedPenaltyIncome, currencyData, interestCalculatedFrom); accruedInterestIncome, accruedFeeIncome, accruedPenaltyIncome, currencyData, interestCalculatedFrom, installment
.getInterestWaived(currency).getAmount());
loanScheduleAccrualDatas.add(accrualData); loanScheduleAccrualDatas.add(accrualData);
} }
@@ -653,7 +653,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
return Money.of(currency, this.amount); return Money.of(currency, this.amount);
} }
private Money getAmountPaid(final MonetaryCurrency currency) { public Money getAmountPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.amountPaid); return Money.of(currency, this.amountPaid);
} }
@@ -30,14 +30,19 @@ public class LoanChargePaidBy extends AbstractPersistable<Long> {
@Column(name = "amount", scale = 6, precision = 19, nullable = false) @Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount; private BigDecimal amount;
@Column(name = "installment_number", nullable = true)
private Integer installmentNumber;
protected LoanChargePaidBy() { protected LoanChargePaidBy() {
} }
public LoanChargePaidBy(final LoanTransaction loanTransaction, final LoanCharge loanCharge, final BigDecimal amount) { public LoanChargePaidBy(final LoanTransaction loanTransaction, final LoanCharge loanCharge, final BigDecimal amount,
Integer installmentNumber) {
this.loanTransaction = loanTransaction; this.loanTransaction = loanTransaction;
this.loanCharge = loanCharge; this.loanCharge = loanCharge;
this.amount = amount; this.amount = amount;
this.installmentNumber = installmentNumber;
} }
public LoanTransaction getLoanTransaction() { public LoanTransaction getLoanTransaction() {
@@ -63,4 +68,9 @@ public class LoanChargePaidBy extends AbstractPersistable<Long> {
public void setAmount(final BigDecimal amount) { public void setAmount(final BigDecimal amount) {
this.amount = amount; this.amount = amount;
} }
public Integer getInstallmentNumber() {
return this.installmentNumber;
}
} }
@@ -63,6 +63,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true) @Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestWrittenOff; private BigDecimal interestWrittenOff;
@Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestAccrued;
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true) @Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesCharged; private BigDecimal feeChargesCharged;
@@ -90,6 +90,9 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
@Column(name = "overpayment_portion_derived", scale = 6, precision = 19, nullable = true) @Column(name = "overpayment_portion_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal overPaymentPortion; private BigDecimal overPaymentPortion;
@Column(name = "unrecognized_income_portion", scale = 6, precision = 19, nullable = true)
private BigDecimal unrecognizedIncomePortion;
@Column(name = "is_reversed", nullable = false) @Column(name = "is_reversed", nullable = false)
private boolean reversed; private boolean reversed;
@@ -138,8 +141,12 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
paymentDate, externalId); paymentDate, externalId);
} }
public static LoanTransaction waiver(final Office office, final Loan loan, final Money waived, final LocalDate waiveDate) { public static LoanTransaction waiver(final Office office, final Loan loan, final Money amount, final LocalDate waiveDate,
return new LoanTransaction(loan, office, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate, null); final Money waived, final Money unrecognizedPortion) {
LoanTransaction loanTransaction = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_INTEREST, amount.getAmount(),
waiveDate, null);
loanTransaction.updateInterestComponent(waived, unrecognizedPortion);
return loanTransaction;
} }
public static LoanTransaction accrueInterest(final Office office, final Loan loan, final Money amount, public static LoanTransaction accrueInterest(final Office office, final Loan loan, final Money amount,
@@ -231,10 +238,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
} }
public static LoanTransaction waiveLoanCharge(final Loan loan, final Office office, final Money waived, final LocalDate waiveDate, public static LoanTransaction waiveLoanCharge(final Loan loan, final Office office, final Money waived, final LocalDate waiveDate,
final Money feeChargesWaived, final Money penaltyChargesWaived) { final Money feeChargesWaived, final Money penaltyChargesWaived, final Money unrecognizedCharge) {
final LoanTransaction waiver = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate, final LoanTransaction waiver = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate,
null); null);
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived); waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived,unrecognizedCharge);
return waiver; return waiver;
} }
@@ -303,6 +310,23 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges).getAmount()); this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges).getAmount());
} }
private void updateChargesComponents(final Money feeCharges, final Money penaltyCharges, final Money unrecognizedCharges) {
final MonetaryCurrency currency = feeCharges.getCurrency();
this.feeChargesPortion = defaultToNullIfZero(getFeeChargesPortion(currency).plus(feeCharges).getAmount());
this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges).getAmount());
this.unrecognizedIncomePortion = defaultToNullIfZero(getUnrecognizedIncomePortion(currency).plus(unrecognizedCharges).getAmount());
}
private void updateInterestComponent(final Money interest, final Money unrecognizedInterest) {
final MonetaryCurrency currency = interest.getCurrency();
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).plus(interest).getAmount());
this.unrecognizedIncomePortion = defaultToNullIfZero(getUnrecognizedIncomePortion(currency).plus(unrecognizedInterest).getAmount());
}
public void adjustInterestComponent(final MonetaryCurrency currency) {
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).minus(getUnrecognizedIncomePortion(currency)).getAmount());
}
public void updateComponentsAndTotal(final Money principal, final Money interest, final Money feeCharges, final Money penaltyCharges) { public void updateComponentsAndTotal(final Money principal, final Money interest, final Money feeCharges, final Money penaltyCharges) {
updateComponents(principal, interest, feeCharges, penaltyCharges); updateComponents(principal, interest, feeCharges, penaltyCharges);
@@ -328,6 +352,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
return Money.of(currency, this.interestPortion); return Money.of(currency, this.interestPortion);
} }
public Money getUnrecognizedIncomePortion(final MonetaryCurrency currency) {
return Money.of(currency, this.unrecognizedIncomePortion);
}
public Money getFeeChargesPortion(final MonetaryCurrency currency) { public Money getFeeChargesPortion(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesPortion); return Money.of(currency, this.feeChargesPortion);
} }
@@ -475,7 +503,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
} }
return new LoanTransactionData(getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData, return new LoanTransactionData(getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData,
currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion, currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion,
this.penaltyChargesPortion, this.overPaymentPortion, this.externalId, transfer, null, outstandingLoanBalance); this.penaltyChargesPortion, this.overPaymentPortion, this.externalId, transfer, null, outstandingLoanBalance, unrecognizedIncomePortion);
} }
public Map<String, Object> toMapData(final CurrencyData currencyData) { public Map<String, Object> toMapData(final CurrencyData currencyData) {
@@ -129,8 +129,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
// pass through for new transactions // pass through for new transactions
if (loanTransaction.getId() == null) { if (loanTransaction.getId() == null) {
loanTransaction.resetDerivedComponents();
handleTransaction(loanTransaction, currency, installments, charges); handleTransaction(loanTransaction, currency, installments, charges);
loanTransaction.adjustInterestComponent(currency);
} else { } else {
/** /**
* For existing transactions, check if the re-payment * For existing transactions, check if the re-payment
@@ -141,9 +141,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
// Reset derived component of new loan transaction and // Reset derived component of new loan transaction and
// re-process transaction // re-process transaction
newLoanTransaction.resetDerivedComponents();
handleTransaction(newLoanTransaction, currency, installments, charges); handleTransaction(newLoanTransaction, currency, installments, charges);
newLoanTransaction.adjustInterestComponent(currency);
/** /**
* Check if the transaction amounts have changed. If so, * Check if the transaction amounts have changed. If so,
* reverse the original transaction and update * reverse the original transaction and update
@@ -200,6 +199,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final Money chargeAmountToProcess, final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final Money chargeAmountToProcess,
final boolean isFeeCharge) { final boolean isFeeCharge) {
// to. // to.
if(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()){
loanTransaction.resetDerivedComponents();
}
Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess); Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess);
final Set<LoanCharge> loanFees = extractFeeCharges(charges); final Set<LoanCharge> loanFees = extractFeeCharges(charges);
@@ -311,7 +313,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
} }
} else { } else {
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(loanTransaction, unpaidCharge, final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(loanTransaction, unpaidCharge,
amountPaidTowardsCharge.getAmount()); amountPaidTowardsCharge.getAmount(), installmentNumber);
chargesPaidBies.add(loanChargePaidBy); chargesPaidBies.add(loanChargePaidBy);
} }
amountRemaining = amountRemaining.minus(amountPaidTowardsCharge); amountRemaining = amountRemaining.minus(amountPaidTowardsCharge);
@@ -97,6 +97,7 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
} else if (loanTransaction.isInterestWaiver()) { } else if (loanTransaction.isInterestWaiver()) {
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining); interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else if (loanTransaction.isChargePayment()) { } else if (loanTransaction.isChargePayment()) {
if (loanTransaction.isPenaltyPayment()) { if (loanTransaction.isPenaltyPayment()) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining); penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
@@ -81,6 +81,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
if (loanTransaction.isInterestWaiver()) { if (loanTransaction.isInterestWaiver()) {
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining); interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else if (loanTransaction.isChargePayment()) { } else if (loanTransaction.isChargePayment()) {
if (loanTransaction.isPenaltyPayment()) { if (loanTransaction.isPenaltyPayment()) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining); penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
@@ -149,6 +150,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
} else if (loanTransaction.isInterestWaiver()) { } else if (loanTransaction.isInterestWaiver()) {
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining); interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
} else if (loanTransaction.isChargePayment()) { } else if (loanTransaction.isChargePayment()) {
if (loanTransaction.isPenaltyPayment()) { if (loanTransaction.isPenaltyPayment()) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining); penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
@@ -11,6 +11,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -29,8 +30,10 @@ import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData; import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -69,6 +72,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
Set<Long> loansIds = new HashSet<>(); Set<Long> loansIds = new HashSet<>();
Map<Long, Collection<LoanChargeData>> loanChargeMap = new HashMap<>(); Map<Long, Collection<LoanChargeData>> loanChargeMap = new HashMap<>();
Map<Long, Collection<LoanTransactionData>> loanWaiverTansactions = new HashMap<>();
Map<Long, Collection<LoanSchedulePeriodData>> loanWaiverSchedules = new HashMap<>();
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) {
try { try {
if (!loansIds.contains(accrualData.getLoanId())) { if (!loansIds.contains(accrualData.getLoanId())) {
@@ -77,8 +82,15 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
.retrieveLoanChargesForAccural(accrualData.getLoanId()); .retrieveLoanChargesForAccural(accrualData.getLoanId());
loanChargeMap.put(accrualData.getLoanId(), chargeData); loanChargeMap.put(accrualData.getLoanId(), chargeData);
} }
if (accrualData.getWaivedInterestIncome() != null && !loanWaiverTansactions.containsKey(accrualData.getLoanId())) {
loanWaiverSchedules.put(accrualData.getLoanId(),
this.loanReadPlatformService.fetchWaiverInterestRepaymentData(accrualData.getLoanId()));
loanWaiverTansactions.put(accrualData.getLoanId(),
this.loanReadPlatformService.retrieveWaiverLoanTransactions(accrualData.getLoanId()));
}
updateCharges(loanChargeMap.get(accrualData.getLoanId()), accrualData, accrualData.getFromDateAsLocaldate(), updateCharges(loanChargeMap.get(accrualData.getLoanId()), accrualData, accrualData.getFromDateAsLocaldate(),
accrualData.getDueDateAsLocaldate()); accrualData.getDueDateAsLocaldate());
updateInterestIncome(accrualData, loanWaiverTansactions, loanWaiverSchedules, accrualData.getDueDateAsLocaldate());
addAccrualAccounting(accrualData); addAccrualAccounting(accrualData);
} }
} catch (Exception e) { } catch (Exception e) {
@@ -115,9 +127,18 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
LocalDate accruredTill = null; LocalDate accruredTill = null;
Long lastLoanId = null; Long lastLoanId = null;
Map<Long, Collection<LoanChargeData>> loanChargeMap = new HashMap<>(); Map<Long, Collection<LoanChargeData>> loanChargeMap = new HashMap<>();
Map<Long, Collection<LoanTransactionData>> loanWaiverTansactions = new HashMap<>();
Map<Long, Collection<LoanSchedulePeriodData>> loanWaiverSchedules = new HashMap<>();
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) {
try { try {
if (!loansIds.contains(accrualData.getLoanId())) { if (!loansIds.contains(accrualData.getLoanId())) {
if (accrualData.getWaivedInterestIncome() != null && !loanWaiverTansactions.containsKey(accrualData.getLoanId())) {
loanWaiverSchedules.put(accrualData.getLoanId(),
this.loanReadPlatformService.fetchWaiverInterestRepaymentData(accrualData.getLoanId()));
loanWaiverTansactions.put(accrualData.getLoanId(),
this.loanReadPlatformService.retrieveWaiverLoanTransactions(accrualData.getLoanId()));
}
if (!loanChargeMap.containsKey(accrualData.getLoanId())) { if (!loanChargeMap.containsKey(accrualData.getLoanId())) {
Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService Collection<LoanChargeData> chargeData = this.loanChargeReadPlatformService
.retrieveLoanChargesForAccural(accrualData.getLoanId()); .retrieveLoanChargesForAccural(accrualData.getLoanId());
@@ -130,11 +151,13 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
if (accruredTill == null || accruredTill.isBefore(tilldate)) { if (accruredTill == null || accruredTill.isBefore(tilldate)) {
updateCharges(loanChargeMap.get(accrualData.getLoanId()), accrualData, accrualData.getFromDateAsLocaldate(), updateCharges(loanChargeMap.get(accrualData.getLoanId()), accrualData, accrualData.getFromDateAsLocaldate(),
tilldate); tilldate);
updateInterestIncome(accrualData, loanWaiverTansactions, loanWaiverSchedules, tilldate);
addAccrualTillSpecificDate(tilldate, accrualData, accruredTill); addAccrualTillSpecificDate(tilldate, accrualData, accruredTill);
} }
} else { } else {
updateCharges(loanChargeMap.get(accrualData.getLoanId()), accrualData, accrualData.getFromDateAsLocaldate(), updateCharges(loanChargeMap.get(accrualData.getLoanId()), accrualData, accrualData.getFromDateAsLocaldate(),
accrualData.getDueDateAsLocaldate()); accrualData.getDueDateAsLocaldate());
updateInterestIncome(accrualData, loanWaiverTansactions, loanWaiverSchedules, tilldate);
addAccrualAccounting(accrualData); addAccrualAccounting(accrualData);
accruredTill = accrualData.getDueDateAsLocaldate(); accruredTill = accrualData.getDueDateAsLocaldate();
} }
@@ -178,13 +201,13 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
} }
} }
int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays(); int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays();
double interestPerDay = accrualData.getInterestIncome().doubleValue() / totalNumberOfDays; double interestPerDay = accrualData.getAccruableIncome().doubleValue() / totalNumberOfDays;
BigDecimal amount = BigDecimal.ZERO; BigDecimal amount = BigDecimal.ZERO;
BigDecimal interestportion = null; BigDecimal interestportion = null;
BigDecimal feeportion = accrualData.getDueDateFeeIncome(); BigDecimal feeportion = accrualData.getDueDateFeeIncome();
BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome(); BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome();
if (daysToBeAccrued >= totalNumberOfDays) { if (daysToBeAccrued >= totalNumberOfDays) {
interestportion = accrualData.getInterestIncome(); interestportion = accrualData.getAccruableIncome();
} else { } else {
double iterest = interestPerDay * daysToBeAccrued; double iterest = interestPerDay * daysToBeAccrued;
interestportion = BigDecimal.valueOf(iterest); interestportion = BigDecimal.valueOf(iterest);
@@ -241,8 +264,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
BigDecimal amount = BigDecimal.ZERO; BigDecimal amount = BigDecimal.ZERO;
BigDecimal interestportion = null; BigDecimal interestportion = null;
BigDecimal totalAccInterest = null; BigDecimal totalAccInterest = null;
if (scheduleAccrualData.getInterestIncome() != null) { if (scheduleAccrualData.getAccruableIncome() != null) {
interestportion = scheduleAccrualData.getInterestIncome(); interestportion = scheduleAccrualData.getAccruableIncome();
totalAccInterest = interestportion; totalAccInterest = interestportion;
if (scheduleAccrualData.getAccruedInterestIncome() != null) { if (scheduleAccrualData.getAccruedInterestIncome() != null) {
interestportion = interestportion.subtract(scheduleAccrualData.getAccruedInterestIncome()); interestportion = interestportion.subtract(scheduleAccrualData.getAccruedInterestIncome());
@@ -255,8 +278,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
BigDecimal feeportion = null; BigDecimal feeportion = null;
BigDecimal totalAccFee = null; BigDecimal totalAccFee = null;
if (scheduleAccrualData.getFeeIncome() != null) { if (scheduleAccrualData.getDueDateFeeIncome() != null) {
feeportion = scheduleAccrualData.getFeeIncome(); feeportion = scheduleAccrualData.getDueDateFeeIncome();
totalAccFee = feeportion; totalAccFee = feeportion;
if (scheduleAccrualData.getAccruedFeeIncome() != null) { if (scheduleAccrualData.getAccruedFeeIncome() != null) {
feeportion = feeportion.subtract(scheduleAccrualData.getAccruedFeeIncome()); feeportion = feeportion.subtract(scheduleAccrualData.getAccruedFeeIncome());
@@ -269,8 +292,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
BigDecimal penaltyportion = null; BigDecimal penaltyportion = null;
BigDecimal totalAccPenalty = null; BigDecimal totalAccPenalty = null;
if (scheduleAccrualData.getPenaltyIncome() != null) { if (scheduleAccrualData.getDueDatePenaltyIncome() != null) {
penaltyportion = scheduleAccrualData.getPenaltyIncome(); penaltyportion = scheduleAccrualData.getDueDatePenaltyIncome();
totalAccPenalty = penaltyportion; totalAccPenalty = penaltyportion;
if (scheduleAccrualData.getAccruedPenaltyIncome() != null) { if (scheduleAccrualData.getAccruedPenaltyIncome() != null) {
penaltyportion = penaltyportion.subtract(scheduleAccrualData.getAccruedPenaltyIncome()); penaltyportion = penaltyportion.subtract(scheduleAccrualData.getAccruedPenaltyIncome());
@@ -280,8 +303,10 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
penaltyportion = null; penaltyportion = null;
} }
} }
addAccrualAccounting(scheduleAccrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion, if (amount.compareTo(BigDecimal.ZERO) == 1) {
totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate()); addAccrualAccounting(scheduleAccrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion,
totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate());
}
} }
private void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData, BigDecimal amount, BigDecimal interestportion, private void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData, BigDecimal amount, BigDecimal interestportion,
@@ -297,10 +322,11 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
final Long transactonId = this.jdbcTemplate.queryForLong("SELECT LAST_INSERT_ID()"); final Long transactonId = this.jdbcTemplate.queryForLong("SELECT LAST_INSERT_ID()");
Map<LoanChargeData, BigDecimal> applicableCharges = scheduleAccrualData.getApplicableCharges(); Map<LoanChargeData, BigDecimal> applicableCharges = scheduleAccrualData.getApplicableCharges();
String chargespaidSql = "INSERT INTO m_loan_charge_paid_by (loan_transaction_id, loan_charge_id, amount) VALUES (?,?,?)"; String chargespaidSql = "INSERT INTO m_loan_charge_paid_by (loan_transaction_id, loan_charge_id, amount,installment_number) VALUES (?,?,?,?)";
for (Map.Entry<LoanChargeData, BigDecimal> entry : applicableCharges.entrySet()) { for (Map.Entry<LoanChargeData, BigDecimal> entry : applicableCharges.entrySet()) {
LoanChargeData chargeData = entry.getKey(); LoanChargeData chargeData = entry.getKey();
this.jdbcTemplate.update(chargespaidSql, transactonId, chargeData.getId(), entry.getValue()); this.jdbcTemplate.update(chargespaidSql, transactonId, chargeData.getId(), entry.getValue(),
scheduleAccrualData.getInstallmentNumber());
} }
Map<String, Object> transactionMap = toMapData(transactonId, amount, interestportion, feeportion, penaltyportion, Map<String, Object> transactionMap = toMapData(transactonId, amount, interestportion, feeportion, penaltyportion,
@@ -391,19 +417,17 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
BigDecimal chargeAmount = BigDecimal.ZERO; BigDecimal chargeAmount = BigDecimal.ZERO;
if (loanCharge.getDueDate() == null) { if (loanCharge.getDueDate() == null) {
if (loanCharge.isInstallmentFee() && accrualData.getDueDateAsLocaldate().isEqual(endDate)) { if (loanCharge.isInstallmentFee() && accrualData.getDueDateAsLocaldate().isEqual(endDate)) {
BigDecimal accruedAmount = BigDecimal.ZERO;
Collection<LoanInstallmentChargeData> installmentData = loanCharge.getInstallmentChargeData(); Collection<LoanInstallmentChargeData> installmentData = loanCharge.getInstallmentChargeData();
for (LoanInstallmentChargeData installmentChargeData : installmentData) { for (LoanInstallmentChargeData installmentChargeData : installmentData) {
BigDecimal accruableForInstallment = installmentChargeData.getAmount();
if (installmentChargeData.getAmountWaived() != null) {
accruableForInstallment = accruableForInstallment.subtract(installmentChargeData.getAmountWaived());
}
if (installmentChargeData.getInstallmentNumber().equals(accrualData.getInstallmentNumber())) { if (installmentChargeData.getInstallmentNumber().equals(accrualData.getInstallmentNumber())) {
BigDecimal accruableForInstallment = installmentChargeData.getAmount();
if (installmentChargeData.getAmountUnrecognized() != null) {
accruableForInstallment = accruableForInstallment.subtract(installmentChargeData.getAmountUnrecognized());
}
chargeAmount = accruableForInstallment; chargeAmount = accruableForInstallment;
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1; boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
if (canAddCharge if (canAddCharge && installmentChargeData.getAmountAccrued() == null) {
&& (loanCharge.getAmountAccrued() == null || loanCharge.getAmountAccrued().compareTo(accruedAmount) == 0)) {
applicableCharges.put(loanCharge, chargeAmount); applicableCharges.put(loanCharge, chargeAmount);
BigDecimal amountAccrued = chargeAmount; BigDecimal amountAccrued = chargeAmount;
if (loanCharge.getAmountAccrued() != null) { if (loanCharge.getAmountAccrued() != null) {
@@ -413,13 +437,12 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
} }
break; break;
} }
accruedAmount = accruedAmount.add(accruableForInstallment);
} }
} }
} else if (loanCharge.getDueDate().isAfter(startDate) && !loanCharge.getDueDate().isAfter(endDate)) { } else if (loanCharge.getDueDate().isAfter(startDate) && !loanCharge.getDueDate().isAfter(endDate)) {
chargeAmount = loanCharge.getAmount(); chargeAmount = loanCharge.getAmount();
if (loanCharge.getAmountWaived() != null) { if (loanCharge.getAmountUnrecognized() != null) {
chargeAmount = chargeAmount.subtract(loanCharge.getAmountWaived()); chargeAmount = chargeAmount.subtract(loanCharge.getAmountUnrecognized());
} }
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1; boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
if (canAddCharge && loanCharge.getAmountAccrued() == null) { if (canAddCharge && loanCharge.getAmountAccrued() == null) {
@@ -444,4 +467,61 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
accrualData.updateChargeDetails(applicableCharges, dueDateFeeIncome, dueDatePenaltyIncome); accrualData.updateChargeDetails(applicableCharges, dueDateFeeIncome, dueDatePenaltyIncome);
} }
private void updateInterestIncome(final LoanScheduleAccrualData accrualData,
final Map<Long, Collection<LoanTransactionData>> loanWaiverTansactions,
final Map<Long, Collection<LoanSchedulePeriodData>> loanWaiverSchedules, final LocalDate tilldate) {
BigDecimal interestIncome = accrualData.getInterestIncome();
if (accrualData.getWaivedInterestIncome() != null) {
BigDecimal recognized = BigDecimal.ZERO;
BigDecimal unrecognized = BigDecimal.ZERO;
BigDecimal remainingAmt = BigDecimal.ZERO;
Collection<LoanTransactionData> loanTransactionDatas = new ArrayList<>();
Collection<LoanSchedulePeriodData> loanSchedulePeriodDatas = loanWaiverSchedules.get(accrualData.getLoanId());
for (LoanTransactionData loanTransactionData : loanWaiverTansactions.get(accrualData.getLoanId())) {
if (!loanTransactionData.dateOf().isAfter(accrualData.getFromDateAsLocaldate())
|| (loanTransactionData.dateOf().isAfter(accrualData.getFromDateAsLocaldate())
&& !loanTransactionData.dateOf().isAfter(accrualData.getDueDateAsLocaldate()) && !loanTransactionData
.dateOf().isAfter(tilldate))) {
loanTransactionDatas.add(loanTransactionData);
}
}
Iterator<LoanTransactionData> iterator = loanTransactionDatas.iterator();
for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDatas) {
if (recognized.compareTo(BigDecimal.ZERO) != 1 && unrecognized.compareTo(BigDecimal.ZERO) != 1 && iterator.hasNext()) {
LoanTransactionData loanTransactionData = iterator.next();
recognized = recognized.add(loanTransactionData.getInterestPortion());
unrecognized = unrecognized.add(loanTransactionData.getUnrecognizedIncomePortion());
}
if (loanSchedulePeriodData.periodDueDate().isBefore(accrualData.getDueDateAsLocaldate())) {
remainingAmt = remainingAmt.add(loanSchedulePeriodData.interestWaived());
if (recognized.compareTo(remainingAmt) == 1) {
recognized = recognized.subtract(remainingAmt);
remainingAmt = BigDecimal.ZERO;
} else {
remainingAmt = remainingAmt.subtract(recognized);
recognized = BigDecimal.ZERO;
if (unrecognized.compareTo(remainingAmt) >= 0) {
unrecognized = unrecognized.subtract(remainingAmt);
remainingAmt = BigDecimal.ZERO;
} else if (iterator.hasNext()) {
remainingAmt = remainingAmt.subtract(unrecognized);
unrecognized = BigDecimal.ZERO;
}
}
}
}
BigDecimal interestWaived = accrualData.getWaivedInterestIncome();
if (interestWaived.compareTo(recognized) == 1) {
interestIncome = interestIncome.subtract(interestWaived.subtract(recognized));
}
}
accrualData.updateAccruableIncome(interestIncome);
}
} }
@@ -9,7 +9,9 @@ import java.util.Collection;
import org.mifosplatform.portfolio.charge.data.ChargeData; import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
public interface LoanChargeReadPlatformService { public interface LoanChargeReadPlatformService {
@@ -26,4 +28,6 @@ public interface LoanChargeReadPlatformService {
Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(Long loanId, Long chargeId, Integer periodNumber); Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(Long loanId, Long chargeId, Integer periodNumber);
Collection<LoanChargeData> retrieveLoanChargesForAccural(Long loanId); Collection<LoanChargeData> retrieveLoanChargesForAccural(Long loanId);
Collection<LoanChargePaidByData> retriveLoanChargesPaidBy(Long chargeId, LoanTransactionType transactionType, Integer installmentNumber);
} }
@@ -10,7 +10,9 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
@@ -23,6 +25,7 @@ import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformServ
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations; import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService; import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -251,6 +254,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
Collection<LoanChargeData> charges = this.jdbcTemplate.query(sql, rm, Collection<LoanChargeData> charges = this.jdbcTemplate.query(sql, rm,
new Object[] { LoanTransactionType.ACCRUAL.getValue(), loanId }); new Object[] { LoanTransactionType.ACCRUAL.getValue(), loanId });
charges = updateLoanChargesWithUnrecognizedIncome(loanId, charges);
Collection<LoanChargeData> removeCharges = new ArrayList<>(); Collection<LoanChargeData> removeCharges = new ArrayList<>();
for (LoanChargeData loanChargeData : charges) { for (LoanChargeData loanChargeData : charges) {
@@ -261,7 +265,8 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
charges.removeAll(removeCharges); charges.removeAll(removeCharges);
for (LoanChargeData loanChargeData : removeCharges) { for (LoanChargeData loanChargeData : removeCharges) {
if (loanChargeData.isInstallmentFee()) { if (loanChargeData.isInstallmentFee()) {
Collection<LoanInstallmentChargeData> installmentChargeDatas = retrieveInstallmentLoanCharges(loanChargeData.getId(), false); Collection<LoanInstallmentChargeData> installmentChargeDatas = retrieveInstallmentLoanChargesForAccrual(loanChargeData
.getId());
LoanChargeData modifiedChargeData = new LoanChargeData(loanChargeData, installmentChargeDatas); LoanChargeData modifiedChargeData = new LoanChargeData(loanChargeData, installmentChargeDatas);
charges.add(modifiedChargeData); charges.add(modifiedChargeData);
} }
@@ -316,4 +321,205 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
} }
} }
private Collection<LoanChargeData> updateLoanChargesWithUnrecognizedIncome(final Long loanId, Collection<LoanChargeData> loanChargeDatas) {
final LoanChargeUnRecognizedIncomeMapper rm = new LoanChargeUnRecognizedIncomeMapper(loanChargeDatas);
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = 1 group by lc.id "
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { LoanTransactionType.WAIVE_CHARGES.getValue(), loanId });
}
private static final class LoanChargeUnRecognizedIncomeMapper implements RowMapper<LoanChargeData> {
private final String schemaSql;
private final Map<Long, LoanChargeData> chargeDataMap;
public LoanChargeUnRecognizedIncomeMapper(final Collection<LoanChargeData> datas) {
this.chargeDataMap = new HashMap<>();
for (LoanChargeData chargeData : datas) {
this.chargeDataMap.put(chargeData.getId(), chargeData);
}
StringBuilder sb = new StringBuilder(50);
sb.append("lc.id as id, ");
sb.append(" sum(wt.unrecognized_income_portion) as amountUnrecognized ");
sb.append(" from m_loan_charge lc ");
sb.append("left join (");
sb.append("select cpb.loan_charge_id, lt.unrecognized_income_portion");
sb.append(" from m_loan_charge_paid_by cpb ");
sb.append("inner join m_loan_transaction lt on lt.id = cpb.loan_transaction_id and lt.is_reversed = 0 and lt.transaction_type_enum = ?");
sb.append(") wt on wt.loan_charge_id= lc.id ");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
LoanChargeData chargeData = this.chargeDataMap.get(id);
return new LoanChargeData(amountUnrecognized, chargeData);
}
}
private Collection<LoanInstallmentChargeData> retrieveInstallmentLoanChargesForAccrual(Long loanChargeId) {
final LoanInstallmentChargeAccrualMapper rm = new LoanInstallmentChargeAccrualMapper();
String sql = "select " + rm.schema() + " where lic.loan_charge_id= ? group by lsi.installment";
Collection<LoanInstallmentChargeData> chargeDatas = this.jdbcTemplate.query(sql, rm,
new Object[] { LoanTransactionType.ACCRUAL.getValue(), loanChargeId });
final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas = new HashMap<>();
for (LoanInstallmentChargeData installmentChargeData : chargeDatas) {
installmentChargeDatas.put(installmentChargeData.getInstallmentNumber(), installmentChargeData);
}
chargeDatas = updateInstallmentLoanChargesWithUnrecognizedIncome(loanChargeId, installmentChargeDatas);
for (LoanInstallmentChargeData installmentChargeData : chargeDatas) {
installmentChargeDatas.put(installmentChargeData.getInstallmentNumber(), installmentChargeData);
}
return installmentChargeDatas.values();
}
private static final class LoanInstallmentChargeAccrualMapper implements RowMapper<LoanInstallmentChargeData> {
private final String schemaSql;
public LoanInstallmentChargeAccrualMapper() {
StringBuilder sb = new StringBuilder(50);
sb.append(" lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, ");
sb.append("lic.amount_outstanding_derived as amountOutstanding,");
sb.append("lic.amount as amount, ");
sb.append("lic.is_paid_derived as paid, ");
sb.append("lic.amount_waived_derived as amountWaived, ");
sb.append(" sum(cp.amount) as amountAccrued, ");
sb.append("lic.waived as waied ");
sb.append("from m_loan_installment_charge lic ");
sb.append("join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ");
sb.append("left join (");
sb.append("select lcp.loan_charge_id, lcp.amount as amount, lcp.installment_number ");
sb.append(" from m_loan_charge_paid_by lcp ");
sb.append("inner join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed = 0 and lt.transaction_type_enum = ?");
sb.append(") cp on cp.loan_charge_id= lic.loan_charge_id and cp.installment_number = lsi.installment ");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Integer installmentNumber = rs.getInt("installmentNumber");
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal amount = rs.getBigDecimal("amount");
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
final boolean paid = rs.getBoolean("paid");
final boolean waived = rs.getBoolean("waied");
final BigDecimal amountAccrued = rs.getBigDecimal("amountAccrued");
return new LoanInstallmentChargeData(installmentNumber, dueAsOfDate, amount, amountOutstanding, amountWaived, paid, waived,
amountAccrued);
}
}
private Collection<LoanInstallmentChargeData> updateInstallmentLoanChargesWithUnrecognizedIncome(final Long loanChargeId,
final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas) {
final LoanInstallmentChargeUnRecognizedIncomeMapper rm = new LoanInstallmentChargeUnRecognizedIncomeMapper(installmentChargeDatas);
String sql = "select " + rm.schema() + " where cpb.loan_charge_id = ? group by cpb.installment_number ";
return this.jdbcTemplate.query(sql, rm, new Object[] { LoanTransactionType.WAIVE_CHARGES.getValue(), loanChargeId });
}
private static final class LoanInstallmentChargeUnRecognizedIncomeMapper implements RowMapper<LoanInstallmentChargeData> {
private final String schemaSql;
private final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas;
public LoanInstallmentChargeUnRecognizedIncomeMapper(final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas) {
this.installmentChargeDatas = installmentChargeDatas;
StringBuilder sb = new StringBuilder(50);
sb.append(" cpb.installment_number as installmentNumber, ");
sb.append(" sum(lt.unrecognized_income_portion) as amountUnrecognized ");
sb.append(" from m_loan_charge_paid_by cpb ");
sb.append("inner join m_loan_transaction lt on lt.id = cpb.loan_transaction_id and lt.is_reversed = 0 and lt.transaction_type_enum = ?");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Integer installmentNumber = rs.getInt("installmentNumber");
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
LoanInstallmentChargeData installmentChargeData = this.installmentChargeDatas.get(installmentNumber);
return new LoanInstallmentChargeData(installmentChargeData, amountUnrecognized);
}
}
@Override
public Collection<LoanChargePaidByData> retriveLoanChargesPaidBy(Long chargeId, final LoanTransactionType transactionType,
final Integer installmentNumber) {
LoanChargesPaidByMapper rm = new LoanChargesPaidByMapper();
StringBuilder sb = new StringBuilder(100);
sb.append("select ");
sb.append(rm.schema());
sb.append(" where lcp.loan_charge_id = ?");
List<Object> args = new ArrayList<>(3);
args.add(chargeId);
if (transactionType != null) {
sb.append(" and lt.transaction_type_enum = ?");
args.add(transactionType.getValue());
}
if (installmentNumber != null) {
sb.append(" and lcp.installment_number = ?");
args.add(installmentNumber);
}
return this.jdbcTemplate.query(sb.toString(), rm, args.toArray());
}
private static final class LoanChargesPaidByMapper implements RowMapper<LoanChargePaidByData> {
private final String schemaSql;
public LoanChargesPaidByMapper() {
StringBuilder sb = new StringBuilder(100);
sb.append("lcp.id as id, lcp.loan_charge_id as chargeId, ");
sb.append("lcp.amount as amount, ");
sb.append("lcp.loan_transaction_id as transactionId, ");
sb.append("lcp.installment_number as installmentNumber ");
sb.append(" from m_loan_charge_paid_by lcp ");
sb.append(" join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed=0");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanChargePaidByData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId");
final BigDecimal amount = rs.getBigDecimal("amount");
final Long transactionId = rs.getLong("transactionId");
final Integer installmentNumber = rs.getInt("installmentNumber");
return new LoanChargePaidByData(id, amount, installmentNumber, chargeId, transactionId);
}
}
} }
@@ -18,7 +18,9 @@ import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData; import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
public interface LoanReadPlatformService { public interface LoanReadPlatformService {
@@ -91,4 +93,8 @@ public interface LoanReadPlatformService {
Collection<Long> fetchArrearLoans(); Collection<Long> fetchArrearLoans();
LoanTransactionData retrieveLoanPrePaymentTemplate(Long loanId); LoanTransactionData retrieveLoanPrePaymentTemplate(Long loanId);
Collection<LoanTransactionData> retrieveWaiverLoanTransactions(Long loanId);
Collection<LoanSchedulePeriodData> fetchWaiverInterestRepaymentData(Long loanId);
} }
@@ -363,12 +363,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
final BigDecimal outstandingLoanBalance = null; final BigDecimal outstandingLoanBalance = null;
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment
.getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency) .getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency)
.getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(), .getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, paymentOptions, null, null, loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, unrecognizedIncomePortion,
null, outstandingLoanBalance); paymentOptions, null, null, null, outstandingLoanBalance);
} }
@Override @Override
@@ -391,12 +392,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount(); final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount();
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment
.getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency) .getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency)
.getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(), .getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, paymentOptions, null, null, loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, unrecognizedIncomePortion,
null, outstandingLoanBalance); paymentOptions, null, null, null, outstandingLoanBalance);
} }
@Override @Override
@@ -420,8 +422,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount(); final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount();
final BigDecimal outstandingLoanBalance = null; final BigDecimal outstandingLoanBalance = null;
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, return new LoanTransactionData(null, null, null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount,
null, null, null, null, null, null, null, null, outstandingLoanBalance); null, null, null, null, null, null, null, null, outstandingLoanBalance, unrecognizedIncomePortion);
} }
@Override @Override
@@ -430,8 +433,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
this.context.authenticatedUser(); this.context.authenticatedUser();
final BigDecimal outstandingLoanBalance = null; final BigDecimal outstandingLoanBalance = null;
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, return new LoanTransactionData(null, null, null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null,
null, null, null, null, null, null, outstandingLoanBalance); null, null, null, null, null, null, outstandingLoanBalance, unrecognizedIncomePortion);
} }
@Override @Override
@@ -444,9 +448,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
paymentOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); paymentOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
} }
final BigDecimal outstandingLoanBalance = null; final BigDecimal outstandingLoanBalance = null;
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(), return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(),
loan.getDisburseAmountForTemplate(), null, null, null, null, null, paymentOptions, null, null, loan.retriveLastEmiAmount(), loan.getDisburseAmountForTemplate(), null, null, null, null, null, unrecognizedIncomePortion, paymentOptions, null, null,
outstandingLoanBalance); loan.retriveLastEmiAmount(), outstandingLoanBalance);
} }
@Override @Override
@@ -1065,6 +1070,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, " + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
+ " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, " + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, "
+ " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, " + " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
+ " tr.unrecognized_income_portion as unrecognizedIncome,"
+ " tr.submitted_on_date as submittedOnDate, " + " tr.submitted_on_date as submittedOnDate, "
+ " pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, " + " pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "
+ " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "
@@ -1127,6 +1133,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees");
final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties");
final BigDecimal overPaymentPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "overpayment"); final BigDecimal overPaymentPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "overpayment");
final BigDecimal unrecognizedIncomePortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "unrecognizedIncome");
final BigDecimal outstandingLoanBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "outstandingLoanBalance"); final BigDecimal outstandingLoanBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "outstandingLoanBalance");
final String externalId = rs.getString("externalId"); final String externalId = rs.getString("externalId");
@@ -1152,8 +1159,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalAmount, return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalAmount,
principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overPaymentPortion, externalId, transfer, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overPaymentPortion,
null, outstandingLoanBalance, submittedOnDate); unrecognizedIncomePortion, externalId, transfer, null, outstandingLoanBalance, submittedOnDate);
} }
} }
@@ -1414,9 +1421,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
sqlBuilder sqlBuilder
.append("select ") .append("select ")
.append(mapper.schema()) .append(mapper.schema())
.append(" where ((if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))") .append(" where ((ls.fee_charges_amount <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))")
.append(" or (if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))") .append(" or ( ls.penalty_charges_amount <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))")
.append(" or (if(ls.interest_waived_derived is null , ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))") .append(" or ( ls.interest_amount <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))")
.append(" and loan.loan_status_id=? and mpl.accounting_type=? and loan.is_npa=0 and ls.duedate <= CURDATE() order by loan.id,ls.duedate"); .append(" and loan.loan_status_id=? and mpl.accounting_type=? and loan.is_npa=0 and ls.duedate <= CURDATE() order by loan.id,ls.duedate");
return this.jdbcTemplate.query(sqlBuilder.toString(), mapper, new Object[] { LoanStatus.ACTIVE.getValue(), return this.jdbcTemplate.query(sqlBuilder.toString(), mapper, new Object[] { LoanStatus.ACTIVE.getValue(),
AccountingRuleType.ACCRUAL_PERIODIC.getValue() }); AccountingRuleType.ACCRUAL_PERIODIC.getValue() });
@@ -1430,9 +1437,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
sqlBuilder sqlBuilder
.append("select ") .append("select ")
.append(mapper.schema()) .append(mapper.schema())
.append(" where ((if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))") .append(" where ((ls.fee_charges_amount <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))")
.append(" or (if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))") .append(" or (ls.penalty_charges_amount <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))")
.append(" or (if(ls.interest_waived_derived is null , ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))") .append(" or (ls.interest_amount <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))")
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=0 and (ls.duedate <= :tilldate or (ls.duedate > :tilldate and ls.fromdate < :tilldate)) order by loan.id,ls.duedate"); .append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=0 and (ls.duedate <= :tilldate or (ls.duedate > :tilldate and ls.fromdate < :tilldate)) order by loan.id,ls.duedate");
Map<String, Object> paramMap = new HashMap<>(3); Map<String, Object> paramMap = new HashMap<>(3);
paramMap.put("active", LoanStatus.ACTIVE.getValue()); paramMap.put("active", LoanStatus.ACTIVE.getValue());
@@ -1453,9 +1460,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
.append("loan.repay_every as repayEvery,") .append("loan.repay_every as repayEvery,")
.append("ls.installment as installmentNumber, ") .append("ls.installment as installmentNumber, ")
.append("ls.duedate as duedate,ls.fromdate as fromdate ,ls.id as scheduleId,loan.product_id as productId,") .append("ls.duedate as duedate,ls.fromdate as fromdate ,ls.id as scheduleId,loan.product_id as productId,")
.append("if(ls.interest_waived_derived is null,ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) as interest,") .append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
.append("if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) as penalty,") .append("ls.penalty_charges_amount as penalty, ")
.append("if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) as charges,") .append("ls.fee_charges_amount as charges, ")
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,") .append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,") .append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode") .append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
@@ -1485,6 +1492,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest"); final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest");
final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges"); final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges");
final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty"); final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty");
final BigDecimal interestIncomeWaived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interestWaived");
final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest"); final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest");
final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege"); final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege");
final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty"); final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty");
@@ -1500,7 +1508,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromDate, return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromDate,
repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome, repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome,
accruedPenaltyIncome, currencyData, interestCalculatedFrom); accruedPenaltyIncome, currencyData, interestCalculatedFrom, interestIncomeWaived);
} }
} }
@@ -1513,9 +1521,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,") .append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
.append("ls.duedate as duedate,ls.fromdate as fromdate,ls.id as scheduleId,loan.product_id as productId,") .append("ls.duedate as duedate,ls.fromdate as fromdate,ls.id as scheduleId,loan.product_id as productId,")
.append("ls.installment as installmentNumber, ") .append("ls.installment as installmentNumber, ")
.append("if(ls.interest_waived_derived is null,ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) as interest,") .append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
.append("if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) as penalty,") .append("ls.penalty_charges_amount as penalty, ")
.append("if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) as charges,") .append("ls.fee_charges_amount as charges, ")
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,") .append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,") .append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode") .append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
@@ -1539,6 +1547,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest"); final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest");
final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges"); final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges");
final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty"); final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty");
final BigDecimal interestIncomeWaived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interestWaived");
final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest"); final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest");
final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege"); final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege");
final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty"); final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty");
@@ -1557,7 +1566,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LocalDate interestCalculatedFrom = null; final LocalDate interestCalculatedFrom = null;
return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromdate, return new LoanScheduleAccrualData(loanId, officeId, installmentNumber, accruedTill, frequency, repayEvery, dueDate, fromdate,
repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome, repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome, accruedInterestIncome, accruedFeeIncome,
accruedPenaltyIncome, currencyData, interestCalculatedFrom); accruedPenaltyIncome, currencyData, interestCalculatedFrom, interestIncomeWaived);
} }
} }
@@ -1569,8 +1578,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
BigDecimal outstandingLoanBalance = null; BigDecimal outstandingLoanBalance = null;
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, null, null, loan.getTotalWrittenOff(), null, null, null, return new LoanTransactionData(null, null, null, transactionType, null, null, null, loan.getTotalWrittenOff(), null, null, null,
null, null, paymentOptions, null, null, null, outstandingLoanBalance); null, null, unrecognizedIncomePortion, paymentOptions, null, null, null, outstandingLoanBalance);
} }
@@ -1580,8 +1590,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LoanAccountData loan = this.retrieveOne(loanId); final LoanAccountData loan = this.retrieveOne(loanId);
final BigDecimal outstandingLoanBalance = null; final BigDecimal outstandingLoanBalance = null;
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
final BigDecimal unrecognizedIncomePortion = null;
return new LoanTransactionData(null, null, null, transactionType, null, loan.currency(), DateUtils.getLocalDateOfTenant(), return new LoanTransactionData(null, null, null, transactionType, null, loan.currency(), DateUtils.getLocalDateOfTenant(),
loan.getTotalOutstandingAmount(), null, null, null, null, null, null, null, null, outstandingLoanBalance); loan.getTotalOutstandingAmount(), null, null, null, null, null, null, null, null, outstandingLoanBalance,
unrecognizedIncomePortion);
} }
@Override @Override
@@ -1606,4 +1618,132 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
} }
@Override
public Collection<LoanTransactionData> retrieveWaiverLoanTransactions(final Long loanId) {
try {
final LoanTransactionDerivedComponentMapper rm = new LoanTransactionDerivedComponentMapper();
final String sql = "select " + rm.schema()
+ " where tr.loan_id = ? and tr.transaction_type_enum = ? and tr.is_reversed=0 order by tr.transaction_date ASC,id ";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId, LoanTransactionType.WAIVE_INTEREST.getValue() });
} catch (final EmptyResultDataAccessException e) {
return null;
}
}
private static final class LoanTransactionDerivedComponentMapper implements RowMapper<LoanTransactionData> {
public String schema() {
return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, "
+ " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
+ " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, "
+ " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
+ " tr.unrecognized_income_portion as unrecognizedIncome " + " from m_loan_transaction tr ";
}
@Override
public LoanTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType");
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(transactionTypeInt);
final LocalDate date = JdbcSupport.getLocalDate(rs, "date");
final BigDecimal totalAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "total");
final BigDecimal principalPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principal");
final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest");
final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees");
final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties");
final BigDecimal overPaymentPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "overpayment");
final BigDecimal unrecognizedIncomePortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "unrecognizedIncome");
final BigDecimal outstandingLoanBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "outstandingLoanBalance");
return new LoanTransactionData(id, transactionType, date, totalAmount, principalPortion, interestPortion, feeChargesPortion,
penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, outstandingLoanBalance);
}
}
@Override
public Collection<LoanSchedulePeriodData> fetchWaiverInterestRepaymentData(final Long loanId) {
try {
final LoanRepaymentWaiverMapper rm = new LoanRepaymentWaiverMapper();
final String sql = "select " + rm.getSchema()
+ " where lrs.loan_id = ? and lrs.interest_waived_derived is not null order by lrs.installment ASC ";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId});
} catch (final EmptyResultDataAccessException e) {
return null;
}
}
private static final class LoanRepaymentWaiverMapper implements RowMapper<LoanSchedulePeriodData> {
private final String sqlSchema;
public String getSchema() {
return this.sqlSchema;
}
public LoanRepaymentWaiverMapper() {
StringBuilder sb = new StringBuilder();
sb.append("lrs.duedate as dueDate,lrs.interest_waived_derived interestWaived, lrs.installment as installment");
sb.append(" from m_loan_repayment_schedule lrs ");
sqlSchema = sb.toString();
}
@Override
public LoanSchedulePeriodData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final Integer period = JdbcSupport.getInteger(rs, "installment");
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
final LocalDate fromDate = null;
final LocalDate obligationsMetOnDate = null;
final Boolean complete = false;
final BigDecimal principalOriginalDue = null;
final BigDecimal principalPaid = null;
final BigDecimal principalWrittenOff = null;
final BigDecimal principalOutstanding = null;
final BigDecimal interestPaid = null;
final BigDecimal interestWrittenOff = null;
final BigDecimal interestOutstanding = null;
final BigDecimal feeChargesDue = null;
final BigDecimal feeChargesPaid = null;
final BigDecimal feeChargesWaived = null;
final BigDecimal feeChargesWrittenOff = null;
final BigDecimal feeChargesOutstanding = null;
final BigDecimal penaltyChargesDue = null;
final BigDecimal penaltyChargesPaid = null;
final BigDecimal penaltyChargesWaived = null;
final BigDecimal penaltyChargesWrittenOff = null;
final BigDecimal penaltyChargesOutstanding = null;
final BigDecimal totalDueForPeriod = null;
final BigDecimal totalPaidInAdvanceForPeriod = null;
final BigDecimal totalPaidLateForPeriod = null;
final BigDecimal totalActualCostOfLoanForPeriod = null;
final BigDecimal outstandingPrincipalBalanceOfLoan = null;
final BigDecimal interestDueOnPrincipalOutstanding = null;
Long loanId = null;
final BigDecimal totalWaived = null;
final BigDecimal totalWrittenOff = null;
final BigDecimal totalOutstanding = null;
final BigDecimal totalPaid = null;
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate, obligationsMetOnDate, complete,
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue,
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
totalPaid, totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaived, totalWrittenOff, totalOutstanding,
totalActualCostOfLoanForPeriod);
}
}
} }
@@ -87,6 +87,7 @@ import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants; import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail; import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
@@ -303,8 +304,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue()); CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
} }
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, regenerateScheduleOnDisbursement(command, loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
workingDays, recalculateSchedule); holidays, workingDays, recalculateSchedule);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(), this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
loan, null); loan, null);
@@ -502,8 +503,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays, regenerateScheduleOnDisbursement(command, loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays,
workingDays, recalculateSchedule); workingDays, recalculateSchedule);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(), this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
loan, null); loan.fetchRepaymentScheduleInstallments(), loan, null);
} }
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes, changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
@@ -716,7 +717,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanTransaction newTransactionDetail = LoanTransaction.repayment(loan.getOffice(), transactionAmountAsMoney, paymentDetail, LoanTransaction newTransactionDetail = LoanTransaction.repayment(loan.getOffice(), transactionAmountAsMoney, paymentDetail,
transactionDate, txnExternalId); transactionDate, txnExternalId);
if (transactionToAdjust.isInterestWaiver()) { if (transactionToAdjust.isInterestWaiver()) {
newTransactionDetail = LoanTransaction.waiver(loan.getOffice(), loan, transactionAmountAsMoney, transactionDate); Money unrecognizedIncome = transactionAmountAsMoney.zero();
Money interestComponent = transactionAmountAsMoney;
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
Money receivableInterest = loan.getReceivableInterest(transactionDate);
if (transactionAmountAsMoney.isGreaterThan(receivableInterest)) {
interestComponent = receivableInterest;
unrecognizedIncome = transactionAmountAsMoney.minus(receivableInterest);
}
}
newTransactionDetail = LoanTransaction.waiver(loan.getOffice(), loan, transactionAmountAsMoney, transactionDate,
interestComponent, unrecognizedIncome);
} }
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
@@ -827,8 +838,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = new ArrayList<>(); final List<Long> existingReversedTransactionIds = new ArrayList<>();
final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
Money unrecognizedIncome = transactionAmountAsMoney.zero();
Money interestComponent = transactionAmountAsMoney;
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
Money receivableInterest = loan.getReceivableInterest(transactionDate);
if (transactionAmountAsMoney.isGreaterThan(receivableInterest)) {
interestComponent = receivableInterest;
unrecognizedIncome = transactionAmountAsMoney.minus(receivableInterest);
}
}
final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan.getOffice(), loan, transactionAmountAsMoney, final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan.getOffice(), loan, transactionAmountAsMoney,
transactionDate); transactionDate, interestComponent, unrecognizedIncome);
CalendarInstance restCalendarInstance = null; CalendarInstance restCalendarInstance = null;
ApplicationCurrency applicationCurrency = null; ApplicationCurrency applicationCurrency = null;
@@ -1307,9 +1327,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
workingDays = this.workingDaysRepository.findOne(); workingDays = this.workingDaysRepository.findOne();
} }
Money accruedCharge = Money.zero(loan.getCurrency());
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) {
accruedCharge = accruedCharge.plus(chargePaidByData.getAmount());
}
}
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes, final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, holidays, workingDays, isHolidayEnabled, existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, holidays, workingDays, isHolidayEnabled,
loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance); loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance, accruedCharge);
this.loanTransactionRepository.save(waiveTransaction); this.loanTransactionRepository.save(waiveTransaction);
saveLoanWithDataIntegrityViolationChecks(loan); saveLoanWithDataIntegrityViolationChecks(loan);
@@ -1733,7 +1762,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// 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) {
if (loan != null) { if (loan != null) {
if(loan.repaymentScheduleDetail().isInterestRecalculationEnabled()){ if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
final String defaultUserMessage = "Meeting calendar type update is not supported"; final String defaultUserMessage = "Meeting calendar type update is not supported";
throw new CalendarParameterUpdateNotSupportedException("jlg.loan.recalculation", defaultUserMessage); throw new CalendarParameterUpdateNotSupportedException("jlg.loan.recalculation", defaultUserMessage);
} }
@@ -2321,9 +2350,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final ApplicationCurrency applicationCurrency, private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan,
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays, final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
final WorkingDays workingDays, final boolean recalculateSchedule) { final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays, final boolean recalculateSchedule) {
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName); BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
loan.regenerateScheduleOnDisbursement(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, loan.regenerateScheduleOnDisbursement(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
@@ -0,0 +1,5 @@
ALTER TABLE `m_loan_charge_paid_by`
ADD COLUMN `installment_number` SMALLINT(5) NULL AFTER `amount`;
ALTER TABLE `m_loan_transaction`
ADD COLUMN `unrecognized_income_portion` DECIMAL(19,6) NULL DEFAULT NULL AFTER `overpayment_portion_derived`;