diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java index 90cf457fb..e7a352613 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java @@ -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); + } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 282b31197..585f2985a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -251,6 +251,7 @@ public class Loan extends AbstractPersistable { private boolean accountNumberRequiresAutoGeneration = false; @Transient private LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory; + @Transient private LoanLifecycleStateMachine loanLifecycleStateMachine; @Transient @@ -532,7 +533,7 @@ public class Loan extends AbstractPersistable { } public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine, - final Map changes) { + final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds) { validateLoanIsNotClosed(loanCharge); @@ -553,6 +554,9 @@ public class Loan extends AbstractPersistable { } 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 { updateLoanSummaryDerivedFields(); } - public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,final List existingTransactionIds, + final List 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 { public Staff getLoanOfficer() { return this.loanOfficer; } + + public LoanSummary getSummary() { + return this.summary; + } public Map deriveAccountingBridgeData(final CurrencyData currencyData, final List existingTransactionIds, final List existingReversedTransactionIds) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 8f2b2fc56..aefc1c817 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -122,9 +122,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.noteRepository.save(note); } - final Map 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 existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); @@ -161,6 +160,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode()); final Map 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 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 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 existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + 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 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 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 changes = new LinkedHashMap(3); - - final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes); + + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + 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 existingTransactionIds, + final List existingReversedTransactionIds) { + final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode()); + final Map accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds, + existingReversedTransactionIds); + journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); + } } \ No newline at end of file