Merge pull request #187 from vishwasbabu/mifosx-183
bug fixes for accrual based accounting
This commit is contained in:
+2
-13
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
package org.mifosplatform.accounting.journalentry.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class LoanDTO {
|
||||
@@ -13,18 +12,15 @@ public class LoanDTO {
|
||||
private Long loanId;
|
||||
private Long loanProductId;
|
||||
private Long officeId;
|
||||
private BigDecimal calculatedInterest;
|
||||
private boolean cashBasedAccountingEnabled;
|
||||
private boolean accrualBasedAccountingEnabled;
|
||||
private List<LoanTransactionDTO> newLoanTransactions;
|
||||
|
||||
public LoanDTO(final Long loanId, final Long loanProductId, final Long officeId, final BigDecimal calculatedInterest,
|
||||
final boolean cashBasedAccountingEnabled, final boolean accrualBasedAccountingEnabled,
|
||||
final List<LoanTransactionDTO> newLoanTransactions) {
|
||||
public LoanDTO(final Long loanId, final Long loanProductId, final Long officeId, final boolean cashBasedAccountingEnabled,
|
||||
final boolean accrualBasedAccountingEnabled, final List<LoanTransactionDTO> newLoanTransactions) {
|
||||
this.loanId = loanId;
|
||||
this.loanProductId = loanProductId;
|
||||
this.officeId = officeId;
|
||||
this.calculatedInterest = calculatedInterest;
|
||||
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
|
||||
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
|
||||
this.newLoanTransactions = newLoanTransactions;
|
||||
@@ -78,11 +74,4 @@ public class LoanDTO {
|
||||
this.newLoanTransactions = newLoanTransactions;
|
||||
}
|
||||
|
||||
public BigDecimal getCalculatedInterest() {
|
||||
return this.calculatedInterest;
|
||||
}
|
||||
|
||||
public void setCalculatedInterest(BigDecimal calculatedInterest) {
|
||||
this.calculatedInterest = calculatedInterest;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -56,8 +56,6 @@ public class AccountingProcessorHelper {
|
||||
final Long loanId = (Long) accountingBridgeData.get("loanId");
|
||||
final Long loanProductId = (Long) accountingBridgeData.get("loanProductId");
|
||||
final Long officeId = (Long) accountingBridgeData.get("officeId");
|
||||
final BigDecimal calculatedInterest = (BigDecimal) accountingBridgeData.get("calculatedInterest");
|
||||
|
||||
final List<LoanTransactionDTO> newLoanTransactions = new ArrayList<LoanTransactionDTO>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -81,8 +79,7 @@ public class AccountingProcessorHelper {
|
||||
|
||||
}
|
||||
|
||||
return new LoanDTO(loanId, loanProductId, officeId, calculatedInterest, cashBasedAccountingEnabled, accrualBasedAccountingEnabled,
|
||||
newLoanTransactions);
|
||||
return new LoanDTO(loanId, loanProductId, officeId, cashBasedAccountingEnabled, accrualBasedAccountingEnabled, newLoanTransactions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+33
-9
@@ -34,6 +34,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
createJournalEntriesForDisbursements(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/*** Handle Interest Postings ***/
|
||||
if (loanTransactionDTO.getTransactionType().isApplyInterest()) {
|
||||
createJournalEntriesForInterestPostings(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
|
||||
/*** Handle Apply charges ***/
|
||||
else if (loanTransactionDTO.getTransactionType().isApplyCharges()) {
|
||||
createJournalEntriesForApplyCharges(loanDTO, loanTransactionDTO, office);
|
||||
@@ -58,9 +63,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
}
|
||||
|
||||
/**
|
||||
* Debit loan Portfolio and credit Fund source for Disbursement. Also
|
||||
* recognise the receivable interest (Debit "Interest Receivable" and credit
|
||||
* "Income from Interest")
|
||||
* Debit loan Portfolio and credit Fund source for Disbursement.
|
||||
*
|
||||
* @param loanDTO
|
||||
* @param loanTransactionDTO
|
||||
@@ -84,12 +87,33 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversed);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Recognise the receivable interest <br/>
|
||||
* Debit "Interest Receivable" and Credit "Income from Interest"
|
||||
*
|
||||
* @param loanDTO
|
||||
* @param loanTransactionDTO
|
||||
* @param office
|
||||
*/
|
||||
private void createJournalEntriesForInterestPostings(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
||||
final Office office) {
|
||||
|
||||
// loan properties
|
||||
final Long loanProductId = loanDTO.getLoanProductId();
|
||||
final Long loanId = loanDTO.getLoanId();
|
||||
|
||||
// transaction properties
|
||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||
final Date transactionDate = loanTransactionDTO.getTransactionDate();
|
||||
final BigDecimal amount = loanTransactionDTO.getAmount();
|
||||
final boolean isReversed = loanTransactionDTO.isReversed();
|
||||
|
||||
// create journal entries for recognising interest (or reversal)
|
||||
BigDecimal interestApplied = loanDTO.getCalculatedInterest();
|
||||
if (interestApplied != null && !(interestApplied.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
if (amount != null && !(amount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE,
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId, transactionId, transactionDate, disbursalAmount,
|
||||
isReversed);
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId, transactionId, transactionDate, amount, isReversed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,8 +261,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
// create journal entries for the penalties application (or reversal)
|
||||
else if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE,
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId, loanId, transactionId, transactionDate, feesAmount,
|
||||
isReversed);
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId, loanId, transactionId, transactionDate,
|
||||
penaltiesAmount, isReversed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -19,6 +19,7 @@ public class LoanTransactionEnumData {
|
||||
private final boolean repaymentAtDisbursement;
|
||||
private final boolean repayment;
|
||||
private final boolean contra;
|
||||
private final boolean applyInterest;
|
||||
private final boolean waiveInterest;
|
||||
private final boolean waiveCharges;
|
||||
private final boolean applyCharges;
|
||||
@@ -36,6 +37,7 @@ public class LoanTransactionEnumData {
|
||||
this.waiveInterest = Long.valueOf(4).equals(this.id);
|
||||
this.waiveCharges = Long.valueOf(9).equals(this.id);
|
||||
this.applyCharges = Long.valueOf(10).equals(this.id);
|
||||
this.applyInterest = Long.valueOf(11).equals(this.id);
|
||||
this.writeOff = Long.valueOf(6).equals(this.id);
|
||||
this.recoveryRepayment = Long.valueOf(8).equals(this.id);
|
||||
}
|
||||
@@ -76,4 +78,8 @@ public class LoanTransactionEnumData {
|
||||
return this.applyCharges;
|
||||
}
|
||||
|
||||
public boolean isApplyInterest() {
|
||||
return this.applyInterest;
|
||||
}
|
||||
|
||||
}
|
||||
+27
-8
@@ -411,6 +411,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
|
||||
// store Id's of existing loan transactions and existing reversed loan
|
||||
// transactions
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
@@ -426,16 +431,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a loanTransaction for "Apply Charge Event" with transaction date
|
||||
* set to "suppliedTransactionDate"
|
||||
* set to "suppliedTransactionDate". The newly created transaction is also
|
||||
* added to the Loan on which this method is called.
|
||||
*
|
||||
* If "suppliedTransactionDate" is not passed Id, the transaction date is
|
||||
* set to the loans due date if the due date is lesser than todays date. If
|
||||
@@ -445,7 +447,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
* @param suppliedTransactionDate
|
||||
* @return
|
||||
*/
|
||||
public LoanTransaction getChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate) {
|
||||
public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate) {
|
||||
final Money chargeAmount = loanCharge.getAmount(getCurrency());
|
||||
Money feeCharges = chargeAmount;
|
||||
Money penaltyCharges = Money.zero(loanCurrency());
|
||||
@@ -471,6 +473,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
final LoanTransaction applyLoanChargeTransaction = LoanTransaction.applyLoanCharge(this, chargeAmount, transactionDate, feeCharges,
|
||||
penaltyCharges);
|
||||
this.loanTransactions.add(applyLoanChargeTransaction);
|
||||
return applyLoanChargeTransaction;
|
||||
}
|
||||
|
||||
@@ -629,6 +632,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, amountWaived, transactionDate,
|
||||
feeChargesWaived, penaltyChargesWaived);
|
||||
this.loanTransactions.add(waiveLoanChargeTransaction);
|
||||
|
||||
// Waive of charges whose due date falls after latest 'repayment'
|
||||
// transaction dont require entire loan schedule to be reprocessed.
|
||||
@@ -1211,6 +1215,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
private void disburse(final LocalDate expectedDisbursedOnLocalDate) {
|
||||
this.actualDisbursementDate = expectedDisbursedOnLocalDate.toDate();
|
||||
updateLoanScheduleDependentDerivedFields();
|
||||
/****
|
||||
* Vishwas TODO: Refactor to skip unnecessary creation of Disbursement
|
||||
* and fees applied transactions (though they are not persisted) on
|
||||
* modifications to loan application modification
|
||||
*****/
|
||||
handleDisbursementTransaction(expectedDisbursedOnLocalDate);
|
||||
}
|
||||
|
||||
@@ -1271,6 +1280,17 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
} else {
|
||||
updateLoanSummaryDerivedFields();
|
||||
}
|
||||
|
||||
/***
|
||||
* Adding a temporary Interest applied transaction (applying the
|
||||
* full Interest as soon as the loan is disbursed), will have to
|
||||
* re-look into this logic once loans with Interest calculation need
|
||||
* to be created or more advances accrual accounting support has to
|
||||
* be provided
|
||||
***/
|
||||
Money interestApplied = Money.of(this.getCurrency(), this.summary.getTotalInterestCharged());
|
||||
final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(this, interestApplied, actualDisbursementDate);
|
||||
this.loanTransactions.add(interestAppliedTransaction);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
@@ -1338,8 +1358,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
if (charge.isDueAtDisbursement()) {
|
||||
charge.markAsFullyPaid();
|
||||
} else {
|
||||
LoanTransaction loanChargeAppliedTransaction = getChargeAppliedTransaction(charge, disbursedOn);
|
||||
this.loanTransactions.add(loanChargeAppliedTransaction);
|
||||
handleChargeAppliedTransaction(charge, disbursedOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -86,6 +86,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate);
|
||||
}
|
||||
|
||||
public static LoanTransaction applyInterest(final Loan loan, final Money amount, final LocalDate interestAppliedDate) {
|
||||
return new LoanTransaction(loan, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate);
|
||||
}
|
||||
|
||||
public static LoanTransaction copyTransactionProperties(LoanTransaction loanTransaction) {
|
||||
return new LoanTransaction(loanTransaction.loan, loanTransaction.typeOf, loanTransaction.dateOf, loanTransaction.amount,
|
||||
loanTransaction.principalPortion, loanTransaction.interestPortion, loanTransaction.feeChargesPortion,
|
||||
@@ -96,7 +100,6 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
final Money penaltyCharges) {
|
||||
LoanTransaction applyCharge = new LoanTransaction(loan, LoanTransactionType.APPLY_CHARGES, amount.getAmount(), applyDate);
|
||||
applyCharge.updateChargesComponents(feeCharges, penaltyCharges);
|
||||
|
||||
return applyCharge;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -21,7 +21,8 @@ public enum LoanTransactionType {
|
||||
*/
|
||||
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
|
||||
WAIVE_CHARGES(9, "loanTransactionType.waiveCharges"), //
|
||||
APPLY_CHARGES(10, "loanTransactionType.applyCharges");
|
||||
APPLY_CHARGES(10, "loanTransactionType.applyCharges"), //
|
||||
APPLY_INTEREST(11, "loanTransactionType.applyInterest");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -75,6 +76,9 @@ public enum LoanTransactionType {
|
||||
case 10:
|
||||
loanTransactionType = LoanTransactionType.APPLY_CHARGES;
|
||||
break;
|
||||
case 11:
|
||||
loanTransactionType = LoanTransactionType.APPLY_INTEREST;
|
||||
break;
|
||||
default:
|
||||
loanTransactionType = LoanTransactionType.INVALID;
|
||||
break;
|
||||
|
||||
+1
-1
@@ -552,7 +552,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
this.loanChargeRepository.save(loanCharge);
|
||||
|
||||
final LoanTransaction applyLoanChargeTransaction = loan.getChargeAppliedTransaction(loanCharge, null);
|
||||
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
|
||||
|
||||
+4
@@ -248,6 +248,10 @@ public class LoanEnumerations {
|
||||
optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_CHARGES.getValue().longValue(),
|
||||
LoanTransactionType.APPLY_CHARGES.getCode(), "Apply Charge");
|
||||
break;
|
||||
case APPLY_INTEREST:
|
||||
optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_INTEREST.getValue().longValue(),
|
||||
LoanTransactionType.APPLY_INTEREST.getCode(), "Apply Interest");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user