wip: refactored accounting and added charge applied event
This commit is contained in:
+142
-22
@@ -85,34 +85,103 @@ public class AccountingProcessorHelper {
|
|||||||
newLoanTransactions);
|
newLoanTransactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GLAccount getLinkedCashAccountsForLoanProduct(final Long loanProductId, final CASH_ACCOUNTS_FOR_LOAN finAccountType) {
|
/**
|
||||||
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
|
* Convenience method that creates a pair of related Debits and Credits for
|
||||||
.findByProductIdAndProductTypeAndFinancialAccountType(loanProductId, PortfolioProductType.LOAN.getValue(),
|
* Accrual Based accounting.
|
||||||
finAccountType.getValue());
|
*
|
||||||
return accountMapping.getGlAccount();
|
* 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
|
* Convenience method that creates a pair of related Debits and Credits for
|
||||||
.findByProductIdAndProductTypeAndFinancialAccountType(loanProductId, PortfolioProductType.LOAN.getValue(),
|
* Cash Based accounting.
|
||||||
finAccountType.getValue());
|
*
|
||||||
return accountMapping.getGlAccount();
|
* 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,
|
public void createCreditJournalEntryOrReversalForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountMappingType,
|
||||||
final String transactionId, final Date transactionDate, final BigDecimal amount) {
|
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
|
||||||
final boolean manualEntry = false;
|
final Boolean isReversal) {
|
||||||
final JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, manualEntry, transactionDate,
|
int accountMappingTypeId = accountMappingType.getValue();
|
||||||
JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId);
|
createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
|
||||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
amount, isReversal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createDebitJournalEntryForLoanProduct(final Office office, final GLAccount account, final Long loanId,
|
public void createCreditJournalEntryOrReversalForLoan(final Office office, final ACCRUAL_ACCOUNTS_FOR_LOAN accountMappingType,
|
||||||
final String transactionId, final Date transactionDate, final BigDecimal amount) {
|
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
|
||||||
final boolean manualEntry = false;
|
final Boolean isReversal) {
|
||||||
final JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, manualEntry, transactionDate,
|
int accountMappingTypeId = accountMappingType.getValue();
|
||||||
JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId);
|
createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
|
||||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
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);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-173
@@ -5,7 +5,6 @@ import java.util.Date;
|
|||||||
|
|
||||||
import org.mifosplatform.accounting.closure.domain.GLClosure;
|
import org.mifosplatform.accounting.closure.domain.GLClosure;
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
|
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.LoanDTO;
|
||||||
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
|
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
|
||||||
import org.mifosplatform.organisation.office.domain.Office;
|
import org.mifosplatform.organisation.office.domain.Office;
|
||||||
@@ -36,7 +35,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*** Handle Apply charges ***/
|
/*** Handle Apply charges ***/
|
||||||
// TODO
|
else if (loanTransactionDTO.getTransactionType().isApplyCharges()) {
|
||||||
|
createJournalEntriesForApplyCharges(loanDTO, loanTransactionDTO, office);
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Handle repayments, repayments at disbursement and reversal of
|
* Handle repayments, repayments at disbursement and reversal of
|
||||||
@@ -44,13 +45,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
***/
|
***/
|
||||||
else if (loanTransactionDTO.getTransactionType().isRepayment()
|
else if (loanTransactionDTO.getTransactionType().isRepayment()
|
||||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()) {
|
|| 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
|
else if ((loanTransactionDTO.getTransactionType().isWriteOff() || loanTransactionDTO.getTransactionType().isWaiveInterest() || loanTransactionDTO
|
||||||
.getTransactionType().isWaiveCharges())) {
|
.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
|
// create journal entries for the disbursement (or disbursement
|
||||||
// reversal)
|
// reversal)
|
||||||
final GLAccount loanPortfolioAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
|
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
|
||||||
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
|
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversed);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create journal entries for recognising interest (or reversal)
|
// create journal entries for recognising interest (or reversal)
|
||||||
BigDecimal interestApplied = loanDTO.getCalculatedInterest();
|
BigDecimal interestApplied = loanDTO.getCalculatedInterest();
|
||||||
if (interestApplied != null && !(interestApplied.compareTo(BigDecimal.ZERO) == 0)) {
|
if (interestApplied != null && !(interestApplied.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
|
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE,
|
||||||
final GLAccount interestReceivableAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
|
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId, transactionId, transactionDate, disbursalAmount,
|
||||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE);
|
isReversed);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 <br/>
|
||||||
|
* <br/>
|
||||||
* <br/>
|
* <br/>
|
||||||
*
|
*
|
||||||
* In case the loan transaction has been reversed, all debits are turned
|
* <b>Principal Repayment</b>: Debits "Fund Source" and Credits
|
||||||
* into credits and vice versa
|
* "Loan Portfolio"<br/>
|
||||||
*/
|
|
||||||
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 <br/>
|
|
||||||
*
|
*
|
||||||
|
* <b>Interest Repayment</b>:Debits "Fund Source" and and Credits
|
||||||
|
* "Receivable Interest" <br/>
|
||||||
|
*
|
||||||
|
* <b>Fee Write Repayment</b>:Debits "Fund Source" and and Credits
|
||||||
|
* "Receivable Fees" <br/>
|
||||||
|
*
|
||||||
|
* <b>Penalty Repayment</b>: Debits "Fund Source" and and Credits
|
||||||
|
* "Receivable Penalties" <br/>
|
||||||
|
* <br/>
|
||||||
|
* Handles write offs using the following posting rules <br/>
|
||||||
|
* <br/>
|
||||||
* <b>Principal Write off</b>: Debits "Losses Written Off" and Credits
|
* <b>Principal Write off</b>: Debits "Losses Written Off" and Credits
|
||||||
* "Loan Portfolio"<br/>
|
* "Loan Portfolio"<br/>
|
||||||
*
|
*
|
||||||
@@ -220,12 +123,18 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
*
|
*
|
||||||
* <b>Penalty Write off</b>: Debits "Losses Written off" and and Credits
|
* <b>Penalty Write off</b>: Debits "Losses Written off" and and Credits
|
||||||
* "Receivable Penalties" <br/>
|
* "Receivable Penalties" <br/>
|
||||||
|
* <br/>
|
||||||
|
* <br/>
|
||||||
|
* In case the loan transaction has been reversed, all debits are turned
|
||||||
|
* into credits and vice versa
|
||||||
*
|
*
|
||||||
* @param loanTransactionDTO
|
* @param loanTransactionDTO
|
||||||
* @param loanDTO
|
* @param loanDTO
|
||||||
* @param office
|
* @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
|
// loan properties
|
||||||
final Long loanProductId = loanDTO.getLoanProductId();
|
final Long loanProductId = loanDTO.getLoanProductId();
|
||||||
final Long loanId = loanDTO.getLoanId();
|
final Long loanId = loanDTO.getLoanId();
|
||||||
@@ -237,71 +146,90 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
|
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
|
||||||
final BigDecimal feesAmount = loanTransactionDTO.getFees();
|
final BigDecimal feesAmount = loanTransactionDTO.getFees();
|
||||||
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
|
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
|
||||||
final boolean isReversed = loanTransactionDTO.isReversed();
|
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||||
|
|
||||||
BigDecimal totalDebitAmount = new BigDecimal(0);
|
BigDecimal totalDebitAmount = new BigDecimal(0);
|
||||||
|
|
||||||
|
// handle principle payment or writeOff (and reversals)
|
||||||
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(principalAmount);
|
totalDebitAmount = totalDebitAmount.add(principalAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
|
||||||
final GLAccount loanPortfolioAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, principalAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle interest payment of writeOff (and reversals)
|
||||||
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE, loanProductId, loanId,
|
||||||
final GLAccount interestAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, interestAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle fees payment of writeOff (and reversals)
|
||||||
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE, loanProductId, loanId,
|
||||||
final GLAccount incomeFromFeesAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, feesAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle penalties payment of writeOff (and reversals)
|
||||||
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE, loanProductId, loanId,
|
||||||
final GLAccount incomeFromPenaltiesAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, penaltiesAmount, isReversal);
|
||||||
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.LOSSES_WRITTEN_OFF);
|
/**
|
||||||
if (isReversed) {
|
* Single DEBIT transaction for write-offs or Repayments (and their
|
||||||
helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
|
* reversals)
|
||||||
totalDebitAmount);
|
***/
|
||||||
|
if (writeOff) {
|
||||||
|
helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF, loanProductId, loanId,
|
||||||
|
transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||||
} else {
|
} else {
|
||||||
helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
|
helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId,
|
||||||
totalDebitAmount);
|
transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Journal Entries for application of charges (fees and payments)
|
||||||
|
* using the following rules <br/>
|
||||||
|
*
|
||||||
|
* <b>Fees:</b> Debit <i>Fees Receivable</i> and credit <i>Income from
|
||||||
|
* Fees</i> <br/>
|
||||||
|
*
|
||||||
|
* <b>Penalties:</b> Debit <i>Penalties Receivable</i> and credit <i>Income
|
||||||
|
* from Penalties</i>
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-73
@@ -5,7 +5,6 @@ import java.util.Date;
|
|||||||
|
|
||||||
import org.mifosplatform.accounting.closure.domain.GLClosure;
|
import org.mifosplatform.accounting.closure.domain.GLClosure;
|
||||||
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
|
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.LoanDTO;
|
||||||
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
|
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
|
||||||
import org.mifosplatform.organisation.office.domain.Office;
|
import org.mifosplatform.organisation.office.domain.Office;
|
||||||
@@ -53,15 +52,9 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
**/
|
**/
|
||||||
else if (loanTransactionDTO.getTransactionType().isWriteOff() && !loanTransactionDTO.isReversed()) {
|
else if (loanTransactionDTO.getTransactionType().isWriteOff() && !loanTransactionDTO.isReversed()) {
|
||||||
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
|
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
|
||||||
final GLAccount lossesWrittenOffAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
|
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF,
|
||||||
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF);
|
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId, transactionId, transactionDate, principalAmount,
|
||||||
helper.createDebitJournalEntryForLoanProduct(office, lossesWrittenOffAccount, loanId, transactionId, transactionDate,
|
false);
|
||||||
principalAmount);
|
|
||||||
|
|
||||||
final GLAccount loanPortfolioAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
|
|
||||||
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
|
|
||||||
helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
|
|
||||||
principalAmount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,22 +80,10 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||||
final Date transactionDate = loanTransactionDTO.getTransactionDate();
|
final Date transactionDate = loanTransactionDTO.getTransactionDate();
|
||||||
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
|
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
|
||||||
final boolean isReversed = loanTransactionDTO.isReversed();
|
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||||
|
|
||||||
final GLAccount loanPortfolioAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
|
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
|
||||||
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
|
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,63 +113,30 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
|
|
||||||
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(principalAmount);
|
totalDebitAmount = totalDebitAmount.add(principalAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
|
||||||
final GLAccount loanPortfolioAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, principalAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId,
|
||||||
final GLAccount interestAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, interestAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
||||||
|
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, loanId,
|
||||||
final GLAccount incomeFromFeesAccount = helper.getLinkedCashAccountsForLoanProduct(loanProductId,
|
transactionId, transactionDate, feesAmount, isReversal);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
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,
|
/*** create a single debit entry (or reversal) for the entire amount **/
|
||||||
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES);
|
helper.createDebitJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId,
|
||||||
if (isReversal) {
|
transactionDate, totalDebitAmount, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -21,6 +21,7 @@ public class LoanTransactionEnumData {
|
|||||||
private final boolean contra;
|
private final boolean contra;
|
||||||
private final boolean waiveInterest;
|
private final boolean waiveInterest;
|
||||||
private final boolean waiveCharges;
|
private final boolean waiveCharges;
|
||||||
|
private final boolean applyCharges;
|
||||||
private final boolean writeOff;
|
private final boolean writeOff;
|
||||||
private final boolean recoveryRepayment;
|
private final boolean recoveryRepayment;
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ public class LoanTransactionEnumData {
|
|||||||
this.contra = Long.valueOf(3).equals(this.id);
|
this.contra = Long.valueOf(3).equals(this.id);
|
||||||
this.waiveInterest = Long.valueOf(4).equals(this.id);
|
this.waiveInterest = Long.valueOf(4).equals(this.id);
|
||||||
this.waiveCharges = Long.valueOf(9).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.writeOff = Long.valueOf(6).equals(this.id);
|
||||||
this.recoveryRepayment = Long.valueOf(8).equals(this.id);
|
this.recoveryRepayment = Long.valueOf(8).equals(this.id);
|
||||||
}
|
}
|
||||||
@@ -70,4 +72,8 @@ public class LoanTransactionEnumData {
|
|||||||
return this.recoveryRepayment;
|
return this.recoveryRepayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isApplyCharges() {
|
||||||
|
return this.applyCharges;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+61
-5
@@ -378,7 +378,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
|
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLoanCharge(final LoanCharge loanCharge) {
|
public LoanTransaction addLoanCharge(final LoanCharge loanCharge, final List<Long> existingTransactionIds,
|
||||||
|
final List<Long> existingReversedTransactionIds) {
|
||||||
|
|
||||||
validateLoanIsNotClosed(loanCharge);
|
validateLoanIsNotClosed(loanCharge);
|
||||||
|
|
||||||
@@ -420,6 +421,53 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
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) {
|
private void validateLoanIsNotClosed(final LoanCharge loanCharge) {
|
||||||
@@ -542,10 +590,10 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
changes.put("amount", amountWaived.getAmount());
|
changes.put("amount", amountWaived.getAmount());
|
||||||
|
|
||||||
Money feeChargesWaived = Money.of(loanCurrency(), loanCharge.amount());
|
Money feeChargesWaived = amountWaived;
|
||||||
Money penaltyChargesWaived = Money.zero(loanCurrency());
|
Money penaltyChargesWaived = Money.zero(loanCurrency());
|
||||||
if (loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
penaltyChargesWaived = Money.of(loanCurrency(), loanCharge.amount());
|
penaltyChargesWaived = amountWaived;
|
||||||
feeChargesWaived = Money.zero(loanCurrency());
|
feeChargesWaived = Money.zero(loanCurrency());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1244,9 +1292,17 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
chargesPayment.updateLoan(this);
|
chargesPayment.updateLoan(this);
|
||||||
this.loanTransactions.add(chargesPayment);
|
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()) {
|
for (LoanCharge charge : setOfLoanCharges()) {
|
||||||
if (charge.isDueAtDisbursement()) {
|
if (charge.isDueAtDisbursement()) {
|
||||||
charge.markAsFullyPaid();
|
charge.markAsFullyPaid();
|
||||||
|
} else {
|
||||||
|
LoanTransaction loanChargeAppliedTransaction = getChargeAppliedTransaction(charge, disbursedOn);
|
||||||
|
this.loanTransactions.add(loanChargeAppliedTransaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1843,7 +1899,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public boolean isSubmittedOnDateAfter(final LocalDate compareDate) {
|
public boolean isSubmittedOnDateAfter(final LocalDate compareDate) {
|
||||||
return this.submittedOnDate == null ? false : (new LocalDate(submittedOnDate)).isAfter(compareDate);
|
return this.submittedOnDate == null ? false : (new LocalDate(submittedOnDate)).isAfter(compareDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate getSubmittedOnDate() {
|
private LocalDate getSubmittedOnDate() {
|
||||||
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
|
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
|
||||||
}
|
}
|
||||||
@@ -2080,7 +2136,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastAssignmentRecordLatestEndDate == null){
|
if (lastAssignmentRecordLatestEndDate == null) {
|
||||||
lastAssignmentRecordLatestEndDate = historyRecord;
|
lastAssignmentRecordLatestEndDate = historyRecord;
|
||||||
} else if (historyRecord.isEndDateAfter(lastAssignmentRecordLatestEndDate.getEndDate())
|
} else if (historyRecord.isEndDateAfter(lastAssignmentRecordLatestEndDate.getEndDate())
|
||||||
&& !historyRecord.isSameLoanOfficer(newLoanOfficer)) {
|
&& !historyRecord.isSameLoanOfficer(newLoanOfficer)) {
|
||||||
|
|||||||
+1
-1
@@ -466,7 +466,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
|
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);
|
return Money.of(currency, this.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
@@ -85,6 +85,14 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) {
|
public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) {
|
||||||
return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), 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,
|
public static LoanTransaction waiveLoanCharge(final Loan loan, final Money waived, final LocalDate waiveDate,
|
||||||
final Money feeChargesWaived, final Money penaltyChargesWaived) {
|
final Money feeChargesWaived, final Money penaltyChargesWaived) {
|
||||||
|
|||||||
+11
-2
@@ -13,13 +13,15 @@ public enum LoanTransactionType {
|
|||||||
CONTRA(3, "loanTransactionType.contra"), //
|
CONTRA(3, "loanTransactionType.contra"), //
|
||||||
WAIVE_INTEREST(4, "loanTransactionType.waiver"), //
|
WAIVE_INTEREST(4, "loanTransactionType.waiver"), //
|
||||||
REPAYMENT_AT_DISBURSEMENT(5, "loanTransactionType.repaymentAtDisbursement"), //
|
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
|
* This type of transactions is allowed on written-off loans where mfi still
|
||||||
* attempts to recover payments from applicant after writing-off.
|
* attempts to recover payments from applicant after writing-off.
|
||||||
*/
|
*/
|
||||||
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
|
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 Integer value;
|
||||||
private final String code;
|
private final String code;
|
||||||
@@ -70,6 +72,9 @@ public enum LoanTransactionType {
|
|||||||
case 9:
|
case 9:
|
||||||
loanTransactionType = LoanTransactionType.WAIVE_CHARGES;
|
loanTransactionType = LoanTransactionType.WAIVE_CHARGES;
|
||||||
break;
|
break;
|
||||||
|
case 10:
|
||||||
|
loanTransactionType = LoanTransactionType.APPLY_CHARGES;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
loanTransactionType = LoanTransactionType.INVALID;
|
loanTransactionType = LoanTransactionType.INVALID;
|
||||||
break;
|
break;
|
||||||
@@ -101,6 +106,10 @@ public enum LoanTransactionType {
|
|||||||
return this.value.equals(LoanTransactionType.WAIVE_CHARGES.getValue());
|
return this.value.equals(LoanTransactionType.WAIVE_CHARGES.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isApplyCharges() {
|
||||||
|
return this.value.equals(LoanTransactionType.APPLY_CHARGES.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isWriteOff() {
|
public boolean isWriteOff() {
|
||||||
return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
|
return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -489,11 +489,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||||
|
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||||
this.loanChargeRepository.save(loanCharge);
|
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);
|
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() //
|
return new CommandProcessingResultBuilder() //
|
||||||
.withCommandId(command.commandId()) //
|
.withCommandId(command.commandId()) //
|
||||||
.withEntityId(loanCharge.getId()) //
|
.withEntityId(loanCharge.getId()) //
|
||||||
|
|||||||
+6
-4
@@ -206,8 +206,7 @@ public class LoanEnumerations {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case INVALID:
|
case INVALID:
|
||||||
optionData = new LoanTransactionEnumData(LoanTransactionType.INVALID.getValue().longValue(),
|
optionData = new LoanTransactionEnumData(LoanTransactionType.INVALID.getValue().longValue(),
|
||||||
LoanTransactionType.INVALID.getCode(),
|
LoanTransactionType.INVALID.getCode(), "Invalid");
|
||||||
"Invalid");
|
|
||||||
break;
|
break;
|
||||||
case DISBURSEMENT:
|
case DISBURSEMENT:
|
||||||
optionData = new LoanTransactionEnumData(LoanTransactionType.DISBURSEMENT.getValue().longValue(),
|
optionData = new LoanTransactionEnumData(LoanTransactionType.DISBURSEMENT.getValue().longValue(),
|
||||||
@@ -223,8 +222,7 @@ public class LoanEnumerations {
|
|||||||
break;
|
break;
|
||||||
case CONTRA:
|
case CONTRA:
|
||||||
optionData = new LoanTransactionEnumData(LoanTransactionType.CONTRA.getValue().longValue(),
|
optionData = new LoanTransactionEnumData(LoanTransactionType.CONTRA.getValue().longValue(),
|
||||||
LoanTransactionType.CONTRA.getCode(),
|
LoanTransactionType.CONTRA.getCode(), "Reversal");
|
||||||
"Reversal");
|
|
||||||
break;
|
break;
|
||||||
case WAIVE_INTEREST:
|
case WAIVE_INTEREST:
|
||||||
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_INTEREST.getValue().longValue(),
|
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_INTEREST.getValue().longValue(),
|
||||||
@@ -246,6 +244,10 @@ public class LoanEnumerations {
|
|||||||
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_CHARGES.getValue().longValue(),
|
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_CHARGES.getValue().longValue(),
|
||||||
LoanTransactionType.WAIVE_CHARGES.getCode(), "Waive loan charges");
|
LoanTransactionType.WAIVE_CHARGES.getCode(), "Waive loan charges");
|
||||||
break;
|
break;
|
||||||
|
case APPLY_CHARGES:
|
||||||
|
optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_CHARGES.getValue().longValue(),
|
||||||
|
LoanTransactionType.APPLY_CHARGES.getCode(), "Apply Charge");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return optionData;
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user