Bug fixes for accrual based accounting

This commit is contained in:
Vishwas Babu AJ
2013-03-07 15:37:39 +05:30
parent 2b784cc0f9
commit 856f406e31
3 changed files with 95 additions and 40 deletions
@@ -8,7 +8,6 @@ import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
import org.mifosplatform.infrastructure.core.service.FileUtils;
import org.mifosplatform.organisation.office.domain.Office;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -50,7 +49,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
/** Handle Write Offs and waivers **/
else if ((loanTransactionDTO.getTransactionType().isWriteOff() || loanTransactionDTO.getTransactionType().isWaiveInterest() || loanTransactionDTO
.getTransactionType().isWaiveCharges()) && !loanTransactionDTO.isReversed()) {
.getTransactionType().isWaiveCharges())) {
createJournalEntriesForWriteOff(loanDTO, loanTransactionDTO, office);
}
}
@@ -68,8 +67,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
private void createJournalEntriesForDisbursements(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
final Office office) {
// TODO: remove the junk transaction Id
String junkTransactionId = "junk" + FileUtils.generateRandomString();
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
@@ -106,14 +103,14 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final GLAccount interestOnLoansAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS);
if (isReversed) {
helper.createDebitJournalEntryForLoanProduct(office, interestOnLoansAccount, loanId, junkTransactionId, transactionDate,
helper.createDebitJournalEntryForLoanProduct(office, interestOnLoansAccount, loanId, transactionId, transactionDate,
interestApplied);
helper.createCreditJournalEntryForLoanProduct(office, interestReceivableAccount, loanId, transactionId, transactionDate,
interestApplied);
helper.createCreditJournalEntryForLoanProduct(office, interestReceivableAccount, loanId, junkTransactionId,
transactionDate, interestApplied);
} else {
helper.createDebitJournalEntryForLoanProduct(office, interestReceivableAccount, loanId, junkTransactionId, transactionDate,
helper.createDebitJournalEntryForLoanProduct(office, interestReceivableAccount, loanId, transactionId, transactionDate,
interestApplied);
helper.createCreditJournalEntryForLoanProduct(office, interestOnLoansAccount, loanId, junkTransactionId, transactionDate,
helper.createCreditJournalEntryForLoanProduct(office, interestOnLoansAccount, loanId, transactionId, transactionDate,
interestApplied);
}
}
@@ -240,6 +237,7 @@ 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();
BigDecimal totalDebitAmount = new BigDecimal(0);
@@ -248,34 +246,63 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final GLAccount loanPortfolioAccount = helper.getLinkedAccrualAccountsForLoanProduct(loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
helper.createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, transactionDate,
principalAmount);
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);
helper.createCreditJournalEntryForLoanProduct(office, interestAccount, loanId, transactionId, transactionDate, interestAmount);
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);
helper.createCreditJournalEntryForLoanProduct(office, incomeFromFeesAccount, loanId, transactionId, transactionDate, feesAmount);
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);
helper.createCreditJournalEntryForLoanProduct(office, incomeFromPenaltiesAccount, loanId, transactionId, transactionDate,
penaltiesAmount);
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);
helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate, totalDebitAmount);
if (isReversed) {
helper.createCreditJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
totalDebitAmount);
} else {
helper.createDebitJournalEntryForLoanProduct(office, fundSourceAccount, loanId, transactionId, transactionDate,
totalDebitAmount);
}
}
}
@@ -251,6 +251,7 @@ public class Loan extends AbstractPersistable<Long> {
private boolean accountNumberRequiresAutoGeneration = false;
@Transient
private LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
@Transient
private LoanLifecycleStateMachine loanLifecycleStateMachine;
@Transient
@@ -532,7 +533,7 @@ public class Loan extends AbstractPersistable<Long> {
}
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes) {
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
validateLoanIsNotClosed(loanCharge);
@@ -553,6 +554,9 @@ public class Loan extends AbstractPersistable<Long> {
}
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, amountWaived, transactionDate,
feeChargesWaived, penaltyChargesWaived);
@@ -1319,13 +1323,17 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSummaryDerivedFields();
}
public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
final Money amountToWaive = Money.of(loanCurrency(), transactionAmount);
final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(this, amountToWaive, transactionDate);
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
handleRepaymentOrWaiverTransaction(waiveInterestTransaction, loanLifecycleStateMachine, null);
@@ -2060,6 +2068,10 @@ public class Loan extends AbstractPersistable<Long> {
public Staff getLoanOfficer() {
return this.loanOfficer;
}
public LoanSummary getSummary() {
return this.summary;
}
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
@@ -122,9 +122,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
}
return new CommandProcessingResultBuilder() //
@@ -145,6 +143,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
context.authenticatedUser();
final Loan loan = retrieveLoanBy(loanId);
final BigDecimal totalInterestCharged = loan.getSummary().getTotalInterestCharged();
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
@@ -161,6 +160,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
// add the previously calculated net charged interest back to
// accountingBridgedate, this would not work if interest
// recalculation in introduced
accountingBridgeData.put("calculatedInterest", totalInterestCharged);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@@ -209,10 +213,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
@@ -266,10 +267,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
@@ -298,7 +296,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Loan loan = retrieveLoanBy(loanId);
final LoanTransaction waiveTransaction = loan.waiveInterest(command, defaultLoanLifecycleStateMachine());
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final LoanTransaction waiveTransaction = loan.waiveInterest(command, defaultLoanLifecycleStateMachine(), existingTransactionIds,
existingReversedTransactionIds);
this.loanTransactionRepository.save(waiveTransaction);
this.loanRepository.save(loan);
@@ -309,6 +310,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
this.noteRepository.save(note);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
@@ -350,10 +353,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
@@ -399,10 +399,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
if (possibleClosingTransaction != null) {
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
}
CommandProcessingResult result = null;
@@ -539,11 +536,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
final Map<String, Object> changes = new LinkedHashMap<String, Object>(3);
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes);
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
existingTransactionIds, existingReversedTransactionIds);
this.loanTransactionRepository.save(waiveTransaction);
this.loanRepository.save(loan);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
@@ -556,6 +558,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.build();
}
@Transactional
@Override
public CommandProcessingResult deleteLoanCharge(final Long loanId, final Long loanChargeId, final JsonCommand command) {
@@ -685,4 +688,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.withLoanId(loanId) //
.build();
}
/**
* @param loan
* @param existingTransactionIds
* @param existingReversedTransactionIds
*/
private void postJournalEntries(final Loan loan, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
}