diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java
index d1121617f..b0fbb8820 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java
@@ -85,34 +85,103 @@ public class AccountingProcessorHelper {
newLoanTransactions);
}
- public GLAccount getLinkedCashAccountsForLoanProduct(final Long loanProductId, final CASH_ACCOUNTS_FOR_LOAN finAccountType) {
- final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
- .findByProductIdAndProductTypeAndFinancialAccountType(loanProductId, PortfolioProductType.LOAN.getValue(),
- finAccountType.getValue());
- return accountMapping.getGlAccount();
+ /**
+ * Convenience method that creates a pair of related Debits and Credits for
+ * Accrual Based accounting.
+ *
+ * The target accounts for debits and credits are switched in case of a
+ * reversal
+ *
+ * @param office
+ * @param accountTypeToBeDebited
+ * Enum of the placeholder GLAccount to be debited
+ * @param accountTypeToBeCredited
+ * Enum of the placeholder of the GLAccount to be credited
+ * @param loanProductId
+ * @param loanId
+ * @param transactionId
+ * @param transactionDate
+ * @param amount
+ * @param isReversal
+ */
+ public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office,
+ final ACCRUAL_ACCOUNTS_FOR_LOAN accountTypeToBeDebited, final ACCRUAL_ACCOUNTS_FOR_LOAN accountTypeToBeCredited,
+ final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
+ final Boolean isReversal) {
+ int accountTypeToDebitId = accountTypeToBeDebited.getValue();
+ int accountTypeToCreditId = accountTypeToBeCredited.getValue();
+ // reverse debits and credits for reversals
+ if (isReversal) {
+ accountTypeToDebitId = accountTypeToBeCredited.getValue();
+ accountTypeToCreditId = accountTypeToBeDebited.getValue();
+ }
+ createJournalEntriesForLoan(office, accountTypeToDebitId, accountTypeToCreditId, loanProductId, loanId, transactionId,
+ transactionDate, amount);
}
- public GLAccount getLinkedAccrualAccountsForLoanProduct(final Long loanProductId, final ACCRUAL_ACCOUNTS_FOR_LOAN finAccountType) {
- final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
- .findByProductIdAndProductTypeAndFinancialAccountType(loanProductId, PortfolioProductType.LOAN.getValue(),
- finAccountType.getValue());
- return accountMapping.getGlAccount();
+ /**
+ * Convenience method that creates a pair of related Debits and Credits for
+ * Cash Based accounting.
+ *
+ * The target accounts for debits and credits are switched in case of a
+ * reversal
+ *
+ * @param office
+ * @param accountTypeToBeDebited
+ * Enum of the placeholder GLAccount to be debited
+ * @param accountTypeToBeCredited
+ * Enum of the placeholder of the GLAccount to be credited
+ * @param loanProductId
+ * @param loanId
+ * @param transactionId
+ * @param transactionDate
+ * @param amount
+ * @param isReversal
+ */
+ public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountTypeToBeDebited,
+ final CASH_ACCOUNTS_FOR_LOAN accountTypeToBeCredited, final Long loanProductId, final Long loanId, final String transactionId,
+ final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
+ int accountTypeToDebitId = accountTypeToBeDebited.getValue();
+ int accountTypeToCreditId = accountTypeToBeCredited.getValue();
+ // reverse debits and credits for reversals
+ if (isReversal) {
+ accountTypeToDebitId = accountTypeToBeCredited.getValue();
+ accountTypeToCreditId = accountTypeToBeDebited.getValue();
+ }
+ createJournalEntriesForLoan(office, accountTypeToDebitId, accountTypeToCreditId, loanProductId, loanId, transactionId,
+ transactionDate, amount);
}
- public void createCreditJournalEntryForLoanProduct(final Office office, final GLAccount account, final Long loanId,
- final String transactionId, final Date transactionDate, final BigDecimal amount) {
- final boolean manualEntry = false;
- final JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, manualEntry, transactionDate,
- JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId);
- this.glJournalEntryRepository.saveAndFlush(journalEntry);
+ public void createCreditJournalEntryOrReversalForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountMappingType,
+ final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
+ final Boolean isReversal) {
+ int accountMappingTypeId = accountMappingType.getValue();
+ createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
+ amount, isReversal);
}
- public void createDebitJournalEntryForLoanProduct(final Office office, final GLAccount account, final Long loanId,
- final String transactionId, final Date transactionDate, final BigDecimal amount) {
- final boolean manualEntry = false;
- final JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, manualEntry, transactionDate,
- JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId);
- this.glJournalEntryRepository.saveAndFlush(journalEntry);
+ public void createCreditJournalEntryOrReversalForLoan(final Office office, final ACCRUAL_ACCOUNTS_FOR_LOAN accountMappingType,
+ final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
+ final Boolean isReversal) {
+ int accountMappingTypeId = accountMappingType.getValue();
+ createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
+ amount, isReversal);
+ }
+
+ public void createDebitJournalEntryOrReversalForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountMappingType,
+ final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
+ final Boolean isReversal) {
+ int accountMappingTypeId = accountMappingType.getValue();
+ createDebitJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
+ amount, isReversal);
+ }
+
+ public void createDebitJournalEntryOrReversalForLoan(final Office office, final ACCRUAL_ACCOUNTS_FOR_LOAN accountMappingType,
+ final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
+ final Boolean isReversal) {
+ int accountMappingTypeId = accountMappingType.getValue();
+ createDebitJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
+ amount, isReversal);
}
/**
@@ -138,4 +207,55 @@ public class AccountingProcessorHelper {
return this.officeRepository.findOne(officeId);
}
+ private void createJournalEntriesForLoan(final Office office, final int accountTypeToDebitId, final int accountTypeToCreditId,
+ final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
+ GLAccount debitAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToDebitId);
+ GLAccount creditAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToCreditId);
+ createCreditJournalEntryForLoan(office, debitAccount, loanId, transactionId, transactionDate, amount);
+ createDebitJournalEntryForLoan(office, creditAccount, loanId, transactionId, transactionDate, amount);
+ }
+
+ private void createDebitJournalEntryOrReversalForLoan(final Office office, final int accountMappingTypeId, final Long loanProductId,
+ final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
+ GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId);
+ if (isReversal) {
+ createCreditJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
+ } else {
+ createDebitJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
+ }
+ }
+
+ private void createCreditJournalEntryOrReversalForLoan(final Office office, final int accountMappingTypeId, final Long loanProductId,
+ final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
+ GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId);
+ if (isReversal) {
+ createDebitJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
+ } else {
+ createCreditJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
+ }
+ }
+
+ private void createCreditJournalEntryForLoan(final Office office, final GLAccount account, final Long loanId,
+ final String transactionId, final Date transactionDate, final BigDecimal amount) {
+ final boolean manualEntry = false;
+ final JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, manualEntry, transactionDate,
+ JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId);
+ this.glJournalEntryRepository.saveAndFlush(journalEntry);
+ }
+
+ private void createDebitJournalEntryForLoan(final Office office, final GLAccount account, final Long loanId,
+ final String transactionId, final Date transactionDate, final BigDecimal amount) {
+ final boolean manualEntry = false;
+ final JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, manualEntry, transactionDate,
+ JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId);
+ this.glJournalEntryRepository.saveAndFlush(journalEntry);
+ }
+
+ private GLAccount getLinkedGLAccountForLoanProduct(final Long loanProductId, final int accountMappingTypeId) {
+ final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
+ .findByProductIdAndProductTypeAndFinancialAccountType(loanProductId, PortfolioProductType.LOAN.getValue(),
+ accountMappingTypeId);
+ return accountMapping.getGlAccount();
+ }
+
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
index e7a352613..f156be428 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
@@ -5,7 +5,6 @@ import java.util.Date;
import org.mifosplatform.accounting.closure.domain.GLClosure;
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
-import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
import org.mifosplatform.organisation.office.domain.Office;
@@ -36,7 +35,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
/*** Handle Apply charges ***/
- // TODO
+ else if (loanTransactionDTO.getTransactionType().isApplyCharges()) {
+ createJournalEntriesForApplyCharges(loanDTO, loanTransactionDTO, office);
+ }
/***
* Handle repayments, repayments at disbursement and reversal of
@@ -44,13 +45,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
***/
else if (loanTransactionDTO.getTransactionType().isRepayment()
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()) {
- createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office);
+ createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false);
}
- /** Handle Write Offs and waivers **/
+ /** Handle Write Offs, waivers and thier reversals **/
else if ((loanTransactionDTO.getTransactionType().isWriteOff() || loanTransactionDTO.getTransactionType().isWaiveInterest() || loanTransactionDTO
.getTransactionType().isWaiveCharges())) {
- createJournalEntriesForWriteOff(loanDTO, loanTransactionDTO, office);
+ createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, true);
}
}
}
@@ -79,136 +80,38 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// create journal entries for the disbursement (or disbursement
// reversal)
- final GLAccount loanPortfolioAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
- final GLAccount fundSourceAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE);
- if (isReversed) {
- helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- disbursalAmount);
- helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate, disbursalAmount);
- } else {
- helper.createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- disbursalAmount);
- helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- disbursalAmount);
- }
+ helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
+ ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversed);
// create journal entries for recognising interest (or reversal)
BigDecimal interestApplied = loanDTO.getCalculatedInterest();
if (interestApplied != null && !(interestApplied.compareTo(BigDecimal.ZERO) == 0)) {
-
- final GLAccount interestReceivableAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE);
- final GLAccount interestOnLoansAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, interestOnLoansAccount, loanId, transactionId, transactionDate,
- interestApplied);
- helper.createCreditJournalEntryForLoanProduct(office, interestReceivableAccount, loanId, transactionId, transactionDate,
- interestApplied);
- } else {
- helper.createDebitJournalEntryForLoanProduct(office, interestReceivableAccount, loanId, transactionId, transactionDate,
- interestApplied);
- helper.createCreditJournalEntryForLoanProduct(office, interestOnLoansAccount, loanId, transactionId, transactionDate,
- interestApplied);
- }
+ helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE,
+ ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId, transactionId, transactionDate, disbursalAmount,
+ isReversed);
}
}
/**
- * Create a single Debit for fund source and multiple credits if applicable
- * (loan portfolio for principal repayments, Interest receivable for
- * interest repayments, Fee receivable for fees payment and Penalty
- * Receivable from penalties for penalty payment)
*
+ * Handles repayments using the following posting rules
+ *
*
*
- * In case the loan transaction has been reversed, all debits are turned
- * into credits and vice versa
- */
- private void createJournalEntriesForRepayments(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 principalAmount = loanTransactionDTO.getPrincipal();
- final BigDecimal interestAmount = loanTransactionDTO.getInterest();
- final BigDecimal feesAmount = loanTransactionDTO.getFees();
- final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
- final boolean isReversed = loanTransactionDTO.isReversed();
-
- BigDecimal totalDebitAmount = new BigDecimal(0);
-
- if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
- totalDebitAmount = totalDebitAmount.add(principalAmount);
-
- final GLAccount loanPortfolioAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
- }
- }
- if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
- totalDebitAmount = totalDebitAmount.add(interestAmount);
-
- final GLAccount interestAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate,
- interestAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate,
- interestAmount);
- }
- }
- if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
- totalDebitAmount = totalDebitAmount.add(feesAmount);
-
- final GLAccount incomeFromFeesAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate,
- feesAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate,
- feesAmount);
- }
- }
- if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
- totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
-
- final GLAccount incomeFromPenaltiesAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
- penaltiesAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
- penaltiesAmount);
- }
- }
- final GLAccount fundSourceAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE);
- if (isReversed) {
- helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- totalDebitAmount);
- } else {
- helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- totalDebitAmount);
- }
- }
-
- /**
- * Handles write offs using the following posting rules
+ * Principal Repayment: Debits "Fund Source" and Credits
+ * "Loan Portfolio"
*
+ * Interest Repayment:Debits "Fund Source" and and Credits
+ * "Receivable Interest"
+ *
+ * Fee Write Repayment:Debits "Fund Source" and and Credits
+ * "Receivable Fees"
+ *
+ * Penalty Repayment: Debits "Fund Source" and and Credits
+ * "Receivable Penalties"
+ *
+ * Handles write offs using the following posting rules
+ *
* Principal Write off: Debits "Losses Written Off" and Credits
* "Loan Portfolio"
*
@@ -220,12 +123,18 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
*
* Penalty Write off: Debits "Losses Written off" and and Credits
* "Receivable Penalties"
+ *
+ *
+ * In case the loan transaction has been reversed, all debits are turned
+ * into credits and vice versa
*
* @param loanTransactionDTO
* @param loanDTO
* @param office
+ *
*/
- private void createJournalEntriesForWriteOff(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
+ private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
+ final Office office, final boolean writeOff) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
@@ -237,71 +146,90 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
final BigDecimal feesAmount = loanTransactionDTO.getFees();
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
- final boolean isReversed = loanTransactionDTO.isReversed();
+ final boolean isReversal = loanTransactionDTO.isReversed();
BigDecimal totalDebitAmount = new BigDecimal(0);
+ // handle principle payment or writeOff (and reversals)
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
-
- final GLAccount loanPortfolioAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
- }
-
+ helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
+ transactionId, transactionDate, principalAmount, isReversal);
}
+
+ // handle interest payment of writeOff (and reversals)
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
-
- final GLAccount interestAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate,
- interestAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate,
- interestAmount);
- }
+ helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE, loanProductId, loanId,
+ transactionId, transactionDate, interestAmount, isReversal);
}
+
+ // handle fees payment of writeOff (and reversals)
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
-
- final GLAccount incomeFromFeesAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate,
- feesAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate,
- feesAmount);
- }
+ helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE, loanProductId, loanId,
+ transactionId, transactionDate, feesAmount, isReversal);
}
+
+ // handle penalties payment of writeOff (and reversals)
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
-
- final GLAccount incomeFromPenaltiesAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
- penaltiesAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
- penaltiesAmount);
- }
+ helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE, loanProductId, loanId,
+ transactionId, transactionDate, penaltiesAmount, isReversal);
}
- final GLAccount fundSourceAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
- ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF);
- if (isReversed) {
- helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- totalDebitAmount);
+
+ /**
+ * Single DEBIT transaction for write-offs or Repayments (and their
+ * reversals)
+ ***/
+ if (writeOff) {
+ helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF, loanProductId, loanId,
+ transactionId, transactionDate, totalDebitAmount, isReversal);
} else {
- helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- totalDebitAmount);
+ helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId,
+ transactionId, transactionDate, totalDebitAmount, isReversal);
+ }
+ }
+
+ /**
+ * Create Journal Entries for application of charges (fees and payments)
+ * using the following rules
+ *
+ * Fees: Debit Fees Receivable and credit Income from
+ * Fees
+ *
+ * Penalties: Debit Penalties Receivable and credit Income
+ * from Penalties
+ *
+ * Also handles reversals for both fees and payment applications
+ *
+ * @param loanDTO
+ * @param loanTransactionDTO
+ * @param office
+ */
+ private void createJournalEntriesForApplyCharges(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 feesAmount = loanTransactionDTO.getFees();
+ final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
+ final boolean isReversed = loanTransactionDTO.isReversed();
+
+ // create journal entries for the fees application (or reversal)
+ if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
+ helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
+ ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, feesAmount, isReversed);
+ }
+ // 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);
}
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
index 0779fb328..236c658a4 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
@@ -5,7 +5,6 @@ import java.util.Date;
import org.mifosplatform.accounting.closure.domain.GLClosure;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
-import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
import org.mifosplatform.organisation.office.domain.Office;
@@ -53,15 +52,9 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
**/
else if (loanTransactionDTO.getTransactionType().isWriteOff() && !loanTransactionDTO.isReversed()) {
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
- final GLAccount lossesWrittenOffAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF);
- helper.createDebitJournalEntryForLoanProduct(office, lossesWrittenOffAccount, loanId, transactionId, transactionDate,
- principalAmount);
-
- final GLAccount loanPortfolioAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
- helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
+ helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF,
+ CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId, transactionId, transactionDate, principalAmount,
+ false);
}
}
}
@@ -87,22 +80,10 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final String transactionId = loanTransactionDTO.getTransactionId();
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
- final boolean isReversed = loanTransactionDTO.isReversed();
+ final boolean isReversal = loanTransactionDTO.isReversed();
- final GLAccount loanPortfolioAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
- final GLAccount fundSourceAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE);
- if (isReversed) {
- helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate, disbursalAmount);
- helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- disbursalAmount);
-
- } else {
- helper.createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- disbursalAmount);
- helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- disbursalAmount);
- }
+ helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
+ CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversal);
}
/**
@@ -132,63 +113,30 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
-
- final GLAccount loanPortfolioAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
- if (isReversal) {
- helper.createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
- principalAmount);
- }
+ helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
+ transactionId, transactionDate, principalAmount, isReversal);
}
+
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
-
- final GLAccount interestAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS);
- if (isReversal) {
- helper.createDebitJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate,
- interestAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate,
- interestAmount);
- }
+ helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId,
+ transactionId, transactionDate, interestAmount, isReversal);
}
+
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
-
- final GLAccount incomeFromFeesAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES);
- if (isReversal) {
- helper.createDebitJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate,
- feesAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate,
- feesAmount);
- }
+ helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, loanId,
+ transactionId, transactionDate, feesAmount, isReversal);
}
+
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
+ helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId, loanId,
+ transactionId, transactionDate, penaltiesAmount, isReversal);
+ }
- final GLAccount incomeFromPenaltiesAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
- CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES);
- if (isReversal) {
- helper.createDebitJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
- penaltiesAmount);
- } else {
- helper.createCreditJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
- penaltiesAmount);
- }
- }
- final GLAccount fundSourceAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE);
- if (isReversal) {
- helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- totalDebitAmount);
- } else {
- helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
- totalDebitAmount);
- }
+ /*** create a single debit entry (or reversal) for the entire amount **/
+ helper.createDebitJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId,
+ transactionDate, totalDebitAmount, isReversal);
}
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java
index 560611bd7..95e6c20d5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java
@@ -21,6 +21,7 @@ public class LoanTransactionEnumData {
private final boolean contra;
private final boolean waiveInterest;
private final boolean waiveCharges;
+ private final boolean applyCharges;
private final boolean writeOff;
private final boolean recoveryRepayment;
@@ -34,6 +35,7 @@ public class LoanTransactionEnumData {
this.contra = Long.valueOf(3).equals(this.id);
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.writeOff = Long.valueOf(6).equals(this.id);
this.recoveryRepayment = Long.valueOf(8).equals(this.id);
}
@@ -70,4 +72,8 @@ public class LoanTransactionEnumData {
return this.recoveryRepayment;
}
+ public boolean isApplyCharges() {
+ return this.applyCharges;
+ }
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
index 6fd0dd243..61d288758 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
@@ -378,7 +378,8 @@ public class Loan extends AbstractPersistable {
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
}
- public void addLoanCharge(final LoanCharge loanCharge) {
+ public LoanTransaction addLoanCharge(final LoanCharge loanCharge, final List existingTransactionIds,
+ final List existingReversedTransactionIds) {
validateLoanIsNotClosed(loanCharge);
@@ -420,6 +421,53 @@ public class Loan extends AbstractPersistable {
}
updateLoanSummaryDerivedFields();
+
+ existingTransactionIds.addAll(findExistingTransactionIds());
+ existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
+ final LoanTransaction applyLoanChargeTransaction = getChargeAppliedTransaction(loanCharge, null);
+
+ return applyLoanChargeTransaction;
+ }
+
+ /**
+ * Creates a loanTransaction for "Apply Charge Event" with transaction date
+ * set to "suppliedTransactionDate"
+ *
+ * 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
+ * not, the transaction date is set to todays date
+ *
+ * @param loanCharge
+ * @param suppliedTransactionDate
+ * @return
+ */
+ private LoanTransaction getChargeAppliedTransaction(final LoanCharge loanCharge, LocalDate suppliedTransactionDate) {
+ final Money chargeAmount = loanCharge.getAmount(getCurrency());
+ Money feeCharges = chargeAmount;
+ Money penaltyCharges = Money.zero(loanCurrency());
+ if (loanCharge.isPenaltyCharge()) {
+ penaltyCharges = chargeAmount;
+ feeCharges = Money.zero(loanCurrency());
+ }
+
+ LocalDate transactionDate = null;
+
+ if (suppliedTransactionDate != null) {
+ transactionDate = suppliedTransactionDate;
+ } else {
+ transactionDate = loanCharge.getDueLocalDate();
+ LocalDate currentDate = DateUtils.getLocalDateOfTenant();
+
+ // if loan charge is to be applied on a future date, the loan
+ // transaction would show todays date as applied date
+ if (currentDate.isBefore(transactionDate)) {
+ transactionDate = currentDate;
+ }
+ }
+
+ final LoanTransaction applyLoanChargeTransaction = LoanTransaction.applyLoanCharge(this, chargeAmount, transactionDate, feeCharges,
+ penaltyCharges);
+ return applyLoanChargeTransaction;
}
private void validateLoanIsNotClosed(final LoanCharge loanCharge) {
@@ -542,10 +590,10 @@ public class Loan extends AbstractPersistable {
changes.put("amount", amountWaived.getAmount());
- Money feeChargesWaived = Money.of(loanCurrency(), loanCharge.amount());
+ Money feeChargesWaived = amountWaived;
Money penaltyChargesWaived = Money.zero(loanCurrency());
if (loanCharge.isPenaltyCharge()) {
- penaltyChargesWaived = Money.of(loanCurrency(), loanCharge.amount());
+ penaltyChargesWaived = amountWaived;
feeChargesWaived = Money.zero(loanCurrency());
}
@@ -1244,9 +1292,17 @@ public class Loan extends AbstractPersistable {
chargesPayment.updateLoan(this);
this.loanTransactions.add(chargesPayment);
+ /**
+ * all Charges repaid at disbursal is marked as repaid and
+ * "APPLY Charge" transactions are created for all other fees (
+ * which are created during disbursal but not repaid)
+ **/
for (LoanCharge charge : setOfLoanCharges()) {
if (charge.isDueAtDisbursement()) {
charge.markAsFullyPaid();
+ } else {
+ LoanTransaction loanChargeAppliedTransaction = getChargeAppliedTransaction(charge, disbursedOn);
+ this.loanTransactions.add(loanChargeAppliedTransaction);
}
}
}
@@ -1843,7 +1899,7 @@ public class Loan extends AbstractPersistable {
public boolean isSubmittedOnDateAfter(final LocalDate compareDate) {
return this.submittedOnDate == null ? false : (new LocalDate(submittedOnDate)).isAfter(compareDate);
}
-
+
private LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
}
@@ -2080,7 +2136,7 @@ public class Loan extends AbstractPersistable {
break;
}
- if (lastAssignmentRecordLatestEndDate == null){
+ if (lastAssignmentRecordLatestEndDate == null) {
lastAssignmentRecordLatestEndDate = historyRecord;
} else if (historyRecord.isEndDateAfter(lastAssignmentRecordLatestEndDate.getEndDate())
&& !historyRecord.isSameLoanOfficer(newLoanOfficer)) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java
index b383ca3be..454c8e937 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java
@@ -466,7 +466,7 @@ public class LoanCharge extends AbstractPersistable {
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
}
- private Money getAmount(final MonetaryCurrency currency) {
+ public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java
index 3be66bb50..90037fc16 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java
@@ -85,6 +85,14 @@ public final class LoanTransaction extends AbstractPersistable {
public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) {
return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate);
}
+
+ public static LoanTransaction applyLoanCharge(final Loan loan, final Money amount, final LocalDate applyDate,
+ final Money feeCharges, final Money penaltyCharges) {
+ LoanTransaction applyCharge = new LoanTransaction(loan, LoanTransactionType.APPLY_CHARGES, amount.getAmount(), applyDate);
+ applyCharge.updateChargesComponents(feeCharges, penaltyCharges);
+
+ return applyCharge;
+ }
public static LoanTransaction waiveLoanCharge(final Loan loan, final Money waived, final LocalDate waiveDate,
final Money feeChargesWaived, final Money penaltyChargesWaived) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java
index 62f6c3cad..1343fa6f7 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java
@@ -13,13 +13,15 @@ public enum LoanTransactionType {
CONTRA(3, "loanTransactionType.contra"), //
WAIVE_INTEREST(4, "loanTransactionType.waiver"), //
REPAYMENT_AT_DISBURSEMENT(5, "loanTransactionType.repaymentAtDisbursement"), //
- WRITEOFF(6, "loanTransactionType.writeOff"), MARKED_FOR_RESCHEDULING(7, "loanTransactionType.marked.for.rescheduling"), //
+ WRITEOFF(6, "loanTransactionType.writeOff"), //
+ MARKED_FOR_RESCHEDULING(7, "loanTransactionType.marked.for.rescheduling"), //
/**
* This type of transactions is allowed on written-off loans where mfi still
* attempts to recover payments from applicant after writing-off.
*/
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
- WAIVE_CHARGES(9, "loanTransactionType.waiveCharges");
+ WAIVE_CHARGES(9, "loanTransactionType.waiveCharges"), //
+ APPLY_CHARGES(10, "loanTransactionType.applyCharges");
private final Integer value;
private final String code;
@@ -70,6 +72,9 @@ public enum LoanTransactionType {
case 9:
loanTransactionType = LoanTransactionType.WAIVE_CHARGES;
break;
+ case 10:
+ loanTransactionType = LoanTransactionType.APPLY_CHARGES;
+ break;
default:
loanTransactionType = LoanTransactionType.INVALID;
break;
@@ -101,6 +106,10 @@ public enum LoanTransactionType {
return this.value.equals(LoanTransactionType.WAIVE_CHARGES.getValue());
}
+ public boolean isApplyCharges() {
+ return this.value.equals(LoanTransactionType.APPLY_CHARGES.getValue());
+ }
+
public boolean isWriteOff() {
return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index ec68ed406..724ec2e77 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -489,11 +489,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
}
+ final List existingTransactionIds = new ArrayList();
+ final List existingReversedTransactionIds = new ArrayList();
this.loanChargeRepository.save(loanCharge);
- loan.addLoanCharge(loanCharge);
+ LoanTransaction applyChargeTransaction = loan.addLoanCharge(loanCharge, existingTransactionIds, existingReversedTransactionIds);
+
+ // we want to apply charge transactions only for those loans charges
+ // that are applied when a loan is active
+ if (loan.status().isActive()) {
+ this.loanTransactionRepository.save(applyChargeTransaction);
+ }
+
this.loanRepository.save(loan);
+ // we post Journal entries only for loans in active status
+ if (loan.status().isActive()) {
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ }
+
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanCharge.getId()) //
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java
index ebe19ab67..93df32e6a 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java
@@ -206,8 +206,7 @@ public class LoanEnumerations {
switch (type) {
case INVALID:
optionData = new LoanTransactionEnumData(LoanTransactionType.INVALID.getValue().longValue(),
- LoanTransactionType.INVALID.getCode(),
- "Invalid");
+ LoanTransactionType.INVALID.getCode(), "Invalid");
break;
case DISBURSEMENT:
optionData = new LoanTransactionEnumData(LoanTransactionType.DISBURSEMENT.getValue().longValue(),
@@ -223,8 +222,7 @@ public class LoanEnumerations {
break;
case CONTRA:
optionData = new LoanTransactionEnumData(LoanTransactionType.CONTRA.getValue().longValue(),
- LoanTransactionType.CONTRA.getCode(),
- "Reversal");
+ LoanTransactionType.CONTRA.getCode(), "Reversal");
break;
case WAIVE_INTEREST:
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_INTEREST.getValue().longValue(),
@@ -246,6 +244,10 @@ public class LoanEnumerations {
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_CHARGES.getValue().longValue(),
LoanTransactionType.WAIVE_CHARGES.getCode(), "Waive loan charges");
break;
+ case APPLY_CHARGES:
+ optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_CHARGES.getValue().longValue(),
+ LoanTransactionType.APPLY_CHARGES.getCode(), "Apply Charge");
+ break;
}
return optionData;
}