adding accounting module specific dto's for loans

This commit is contained in:
Vishwas Babu AJ
2013-01-17 18:20:49 +05:30
parent bc20ae399e
commit 0a30e4e08c
6 changed files with 299 additions and 61 deletions
@@ -0,0 +1,72 @@
package org.mifosplatform.accounting.api.data;
import java.util.List;
public class LoanDTO {
private Long loanId;
private Long loanProductId;
private Long officeId;
private boolean cashBasedAccountingEnabled;
private boolean accrualBasedAccountingEnabled;
private List<LoanTransactionDTO> newLoanTransactions;
public LoanDTO(Long loanId, Long loanProductId, Long officeId, boolean cashBasedAccountingEnabled,
boolean accrualBasedAccountingEnabled, List<LoanTransactionDTO> newLoanTransactions) {
this.loanId = loanId;
this.loanProductId = loanProductId;
this.officeId = officeId;
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
this.newLoanTransactions = newLoanTransactions;
}
public Long getLoanId() {
return this.loanId;
}
public void setLoanId(Long loanId) {
this.loanId = loanId;
}
public Long getLoanProductId() {
return this.loanProductId;
}
public void setLoanProductId(Long loanProductId) {
this.loanProductId = loanProductId;
}
public Long getOfficeId() {
return this.officeId;
}
public void setOfficeId(Long officeId) {
this.officeId = officeId;
}
public boolean isCashBasedAccountingEnabled() {
return this.cashBasedAccountingEnabled;
}
public void setCashBasedAccountingEnabled(boolean cashBasedAccountingEnabled) {
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
}
public boolean isAccrualBasedAccountingEnabled() {
return this.accrualBasedAccountingEnabled;
}
public void setAccrualBasedAccountingEnabled(boolean accrualBasedAccountingEnabled) {
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
}
public List<LoanTransactionDTO> getNewLoanTransactions() {
return this.newLoanTransactions;
}
public void setNewLoanTransactions(List<LoanTransactionDTO> newLoanTransactions) {
this.newLoanTransactions = newLoanTransactions;
}
}
@@ -0,0 +1,138 @@
package org.mifosplatform.accounting.api.data;
import java.math.BigDecimal;
import java.util.Date;
public class LoanTransactionDTO {
private String transactionId;
private Date transactionDate;
private BigDecimal amount;
/*** Breakup of amounts in case of repayments **/
private BigDecimal principal;
private BigDecimal interest;
private BigDecimal fees;
private BigDecimal penalties;
/*** Boolean values determine type of loan transaction ***/
private boolean disbursement;
private boolean repayment;
private boolean repaymentAtDisbursement;
private boolean contra;
private boolean writeOff;
public LoanTransactionDTO(String transactionId, Date transactionDate, BigDecimal amount, BigDecimal principal, BigDecimal interest,
BigDecimal fees, BigDecimal penalties, boolean disbursement, boolean repayment, boolean repaymentAtDisbursement,
boolean contra, boolean writeOff) {
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
this.principal = principal;
this.interest = interest;
this.fees = fees;
this.penalties = penalties;
this.disbursement = disbursement;
this.repayment = repayment;
this.repaymentAtDisbursement = repaymentAtDisbursement;
this.contra = contra;
this.writeOff = writeOff;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public void setTransactionDate(Date transactionDate) {
this.transactionDate = transactionDate;
}
public String getTransactionId() {
return this.transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public BigDecimal getAmount() {
return this.amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public BigDecimal getPrincipal() {
return this.principal;
}
public void setPrincipal(BigDecimal principal) {
this.principal = principal;
}
public BigDecimal getInterest() {
return this.interest;
}
public void setInterest(BigDecimal interest) {
this.interest = interest;
}
public BigDecimal getFees() {
return this.fees;
}
public void setFees(BigDecimal fees) {
this.fees = fees;
}
public BigDecimal getPenalties() {
return this.penalties;
}
public void setPenalties(BigDecimal penalties) {
this.penalties = penalties;
}
public boolean isDisbursement() {
return this.disbursement;
}
public void setDisbursement(boolean disbursement) {
this.disbursement = disbursement;
}
public boolean isRepayment() {
return this.repayment;
}
public void setRepayment(boolean repayment) {
this.repayment = repayment;
}
public boolean isContra() {
return this.contra;
}
public void setContra(boolean contra) {
this.contra = contra;
}
public boolean isWriteOff() {
return this.writeOff;
}
public void setWriteOff(boolean writeOff) {
this.writeOff = writeOff;
}
public boolean isRepaymentAtDisbursement() {
return this.repaymentAtDisbursement;
}
public void setRepaymentAtDisbursement(boolean repaymentAtDisbursement) {
this.repaymentAtDisbursement = repaymentAtDisbursement;
}
}
@@ -1,10 +1,7 @@
package org.mifosplatform.accounting.service;
import java.util.List;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.accounting.api.data.LoanDTO;
import org.springframework.security.access.prepost.PreAuthorize;
public interface GLJournalEntryWritePlatformService {
@@ -15,8 +12,7 @@ public interface GLJournalEntryWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'REVERT_JOURNAL_ENTRY')")
String revertJournalEntry(String transactionId);
void createJournalEntriesForLoan(Loan loan, List<LoanTransaction> loanTransactions);
void createJournalEntriesForLoan(LoanDTO loanDTO);
void createJournalEntriesForLoan(Loan loan, LoanTransaction loanTransaction);
}
@@ -1,7 +1,6 @@
package org.mifosplatform.accounting.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -13,6 +12,8 @@ import org.mifosplatform.accounting.AccountingConstants.JOURNAL_ENTRY_TYPE;
import org.mifosplatform.accounting.AccountingConstants.PORTFOLIO_PRODUCT_TYPE;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.commands.SingleDebitOrCreditEntryCommand;
import org.mifosplatform.accounting.api.data.LoanDTO;
import org.mifosplatform.accounting.api.data.LoanTransactionDTO;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.domain.GLAccountRepository;
import org.mifosplatform.accounting.domain.GLClosure;
@@ -31,8 +32,6 @@ import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityExce
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -126,57 +125,56 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
@Transactional
@Override
public void createJournalEntriesForLoan(Loan loan, List<LoanTransaction> loanTransactions) {
public void createJournalEntriesForLoan(LoanDTO loanDTO) {
// shouldn't be before an accounting closure
if (loan.isCashBasedAccountingEnabledOnLoanProduct()) {
createJournalEntriesUsingCashRules(loan, loanTransactions);
} else if (loan.isAccrualBasedAccountingEnabledOnLoanProduct()) {
if (loanDTO.isCashBasedAccountingEnabled()) {
createJournalEntriesUsingCashRules(loanDTO);
} else if (loanDTO.isAccrualBasedAccountingEnabled()) {
// TODO Vishwas: add accrual based accounting rules
}
}
/**
* @param loan
* @param loanDTO
* @param loanTransactions
*/
private void createJournalEntriesUsingCashRules(Loan loan, List<LoanTransaction> loanTransactions) {
GLClosure latestGLClosure = glClosureRepository.getLatestGLClosureByBranch(loan.getOfficeId());
Office office = loan.getClient().getOffice();
Long loanProductId = loan.productId();
private void createJournalEntriesUsingCashRules(LoanDTO loanDTO) {
GLClosure latestGLClosure = glClosureRepository.getLatestGLClosureByBranch(loanDTO.getOfficeId());
Office office = officeRepository.findOne(loanDTO.getOfficeId());
Long loanProductId = loanDTO.getLoanProductId();
// TODO: Check for accounting type
for (LoanTransaction loanTransaction : loanTransactions) {
Date entryDate = loanTransaction.getDateOf();
String transactionId = loanTransaction.getId().toString();
Long loanId = loan.getId();
for (LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) {
Date entryDate = loanTransactionDTO.getTransactionDate();
String transactionId = loanTransactionDTO.getTransactionId();
Long loanId = loanDTO.getLoanId();
/**
* check if an accounting closure has happened for this branch after
* the transaction Date
**/
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(loanTransaction.getDateOf())
|| latestGLClosure.getClosingDate().equals(loanTransaction.getDateOf())) { throw new GLJournalEntryInvalidException(
if (latestGLClosure.getClosingDate().after(entryDate) || latestGLClosure.getClosingDate().equals(entryDate)) { throw new GLJournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
/*** Debit loan Portfolio and credit Fund source for Disbursal **/
if (loanTransaction.isDisbursement()) {
if (loanTransactionDTO.isDisbursement()) {
GLAccount loanPortfolioAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
GLAccount fundSourceAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE);
BigDecimal disbursalAmount = loanTransaction.getAmount();
BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, entryDate, disbursalAmount);
createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, entryDate, disbursalAmount);
} else if (loanTransaction.isRepayment() || loanTransaction.isRepaymentAtDisbursement() || loanTransaction.isContra()) {
createJournalEntriesForRepaymentOrContraUsingCashRules(loan, office, loanProductId, loanTransaction, entryDate,
transactionId, loanId);
} else if (loanTransactionDTO.isRepayment() || loanTransactionDTO.isRepaymentAtDisbursement() || loanTransactionDTO.isContra()) {
createJournalEntriesForRepaymentOrContraUsingCashRules(office, loanProductId, loanTransactionDTO, entryDate, transactionId,
loanId);
}/***
* Only principal write off affects cash based accounting (interest
* and fee write off need not be considered). Debit losses written
* off and credit Loan Portfolio
**/
else if (loanTransaction.isWriteOff()) {
BigDecimal principalAmount = loanTransaction.getPrincipalPortion(loan.getCurrency()).getAmount();
else if (loanTransactionDTO.isWriteOff()) {
BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
GLAccount lossesWrittenOffAccount = getLinkedFinAccountForLoanProduct(loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF);
createDebitJournalEntryForLoanProduct(office, lossesWrittenOffAccount, loanId, transactionId, entryDate, principalAmount);
@@ -196,7 +194,6 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
* In case the loan transaction is a contra, all debits are turned into
* credits and vice versa
*
* @param loan
* @param office
* @param loanProductId
* @param loanTransaction
@@ -204,55 +201,56 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
* @param transactionId
* @param loanId
*/
private void createJournalEntriesForRepaymentOrContraUsingCashRules(Loan loan, Office office, Long loanProductId,
LoanTransaction loanTransaction, Date entryDate, String transactionId, Long loanId) {
BigDecimal principalAmount = loanTransaction.getPrincipalPortion(loan.getCurrency()).getAmount();
BigDecimal interestAmount = loanTransaction.getInterestPortion(loan.getCurrency()).getAmount();
BigDecimal feesAmount = loanTransaction.getFeeChargesPortion(loan.getCurrency()).getAmount();
BigDecimal penaltiesAmount = loanTransaction.getPenaltyChargesPortion(loan.getCurrency()).getAmount();
private void createJournalEntriesForRepaymentOrContraUsingCashRules(Office office, Long loanProductId,
LoanTransactionDTO loanTransactionDTO, Date entryDate, String transactionId, Long loanId) {
BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
BigDecimal interestAmount = loanTransactionDTO.getInterest();
BigDecimal feesAmount = loanTransactionDTO.getFees();
BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
boolean isContraTransaction = loanTransactionDTO.isContra();
BigDecimal totalDebitAmount = new BigDecimal(0);
if (loanTransaction.isContra()) {
if (isContraTransaction) {
transactionId = "reversal-" + transactionId;
}
if (!(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
GLAccount loanPortfolioAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
if (loanTransaction.isContra()) {
if (isContraTransaction) {
createDebitJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, entryDate, principalAmount);
} else {
createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, entryDate, principalAmount);
}
}
if (!(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (principalAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
GLAccount interestAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS);
if (loanTransaction.isContra()) {
if (isContraTransaction) {
createDebitJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, entryDate, interestAmount);
} else {
createCreditJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, entryDate, interestAmount);
}
}
if (!(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
GLAccount incomeFromFeesAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES);
if (loanTransaction.isContra()) {
if (isContraTransaction) {
createDebitJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, entryDate, feesAmount);
} else {
createCreditJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, entryDate, feesAmount);
}
}
if (!(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
GLAccount incomeFromPenaltiesAccount = getLinkedFinAccountForLoanProduct(loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES);
if (loanTransaction.isContra()) {
if (isContraTransaction) {
createDebitJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, entryDate, penaltiesAmount);
} else {
createCreditJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, entryDate,
@@ -260,21 +258,13 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
}
}
GLAccount fundSourceAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE);
if (loanTransaction.isContra()) {
if (isContraTransaction) {
createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, entryDate, totalDebitAmount);
} else {
createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, entryDate, totalDebitAmount);
}
}
@Transactional
@Override
public void createJournalEntriesForLoan(Loan loan, LoanTransaction loanTransaction) {
List<LoanTransaction> loanTransactions = new ArrayList<LoanTransaction>();
loanTransactions.add(loanTransaction);
createJournalEntriesForLoan(loan, loanTransactions);
}
private GLAccount getLinkedFinAccountForLoanProduct(Long loanProductId, CASH_ACCOUNTS_FOR_LOAN finAccountType) {
ProductToGLAccountMapping accountMapping = accountMappingRepository.findByProductIdAndProductTypeAndFinancialAccountType(
loanProductId, PORTFOLIO_PRODUCT_TYPE.LOAN.getValue(), finAccountType.getValue());
@@ -239,19 +239,35 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
public Money getPrincipalPortion(final MonetaryCurrency currency) {
return Money.of(currency, this.principalPortion);
}
public BigDecimal getPrincipalPortion() {
return this.principalPortion;
}
public Money getInterestPortion(final MonetaryCurrency currency) {
return Money.of(currency, this.interestPortion);
}
public BigDecimal getInterestPortion() {
return this.interestPortion;
}
public Money getFeeChargesPortion(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesPortion);
}
public BigDecimal getFeePortion() {
return this.feeChargesPortion;
}
public Money getPenaltyChargesPortion(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesPortion);
}
public BigDecimal getPenaltyChargesPortion() {
return this.penaltyChargesPortion;
}
public void resetDerivedComponents() {
this.principalPortion = null;
this.interestPortion = null;
@@ -11,6 +11,8 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.api.data.LoanDTO;
import org.mifosplatform.accounting.api.data.LoanTransactionDTO;
import org.mifosplatform.accounting.service.GLJournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -146,7 +148,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
newLoanTransactions.add(loanTransaction);
}
}
journalEntryWritePlatformService.createJournalEntriesForLoan(loan, newLoanTransactions);
LoanDTO loanDTO = populateLoanDTO(loan, newLoanTransactions);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
return new CommandProcessingResultBuilder() //
@@ -226,7 +229,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) {
journalEntryWritePlatformService.createJournalEntriesForLoan(loan, loanRepayment);
LoanDTO loanDTO = populateLoanData(loan, loanRepayment);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
return new CommandProcessingResultBuilder() //
@@ -289,7 +293,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
newLoanTransactions.add(newTransactionDetail);
}
newLoanTransactions.add(transactionToAdjust);
journalEntryWritePlatformService.createJournalEntriesForLoan(loan, newLoanTransactions);
LoanDTO loanDTO = populateLoanDTO(loan, newLoanTransactions);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
return new CommandProcessingResultBuilder() //
@@ -376,7 +381,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) {
journalEntryWritePlatformService.createJournalEntriesForLoan(loan, writeoff);
LoanDTO loanDTO = populateLoanData(loan, writeoff);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
return new CommandProcessingResultBuilder() //
@@ -685,4 +691,24 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.withCommandId(command.commandId()) //
.build();
}
private LoanDTO populateLoanDTO(Loan loan, List<LoanTransaction> loanTransactions) {
List<LoanTransactionDTO> loanTransactionDTOs = new ArrayList<LoanTransactionDTO>();
for (LoanTransaction loanTransaction : loanTransactions) {
LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(),
loanTransaction.getDateOf(), loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(),
loanTransaction.getInterestPortion(), loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(),
loanTransaction.isDisbursement(), loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(),
loanTransaction.isContra(), loanTransaction.isWriteOff());
loanTransactionDTOs.add(loanTransactionDTO);
}
return new LoanDTO(loan.getId(), loan.productId(), loan.getOfficeId(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
loan.isAccrualBasedAccountingEnabledOnLoanProduct(), loanTransactionDTOs);
}
private LoanDTO populateLoanData(Loan loan, LoanTransaction loanTransaction) {
List<LoanTransaction> loanTransactions = new ArrayList<LoanTransaction>();
loanTransactions.add(loanTransaction);
return populateLoanDTO(loan, loanTransactions);
}
}