diff --git a/mifosng-db/migrations/core_db/V135__added_permission_for_undo_written_off.sql b/mifosng-db/migrations/core_db/V135__added_permission_for_undo_written_off.sql new file mode 100755 index 000000000..716265740 --- /dev/null +++ b/mifosng-db/migrations/core_db/V135__added_permission_for_undo_written_off.sql @@ -0,0 +1 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_loan', 'UNDOWRITEOFF_LOAN', 'LOAN', 'UNDOWRITEOFF', 0); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java index 138f8cbbd..e38bab18a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java @@ -65,11 +65,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF * and fee write off need not be considered). Debit losses written * off and credit Loan Portfolio **/ - else if (loanTransactionDTO.getTransactionType().isWriteOff() && !loanTransactionDTO.isReversed()) { + else if (loanTransactionDTO.getTransactionType().isWriteOff()) { final BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, paymentTypeId, - loanId, transactionId, transactionDate, principalAmount, false); + loanId, transactionId, transactionDate, principalAmount, loanTransactionDTO.isReversed()); } else if (loanTransactionDTO.getTransactionType().isInitiateTransfer() || loanTransactionDTO.getTransactionType().isApproveTransfer() || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 9ebc65d32..82d3f8ff8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -377,6 +377,10 @@ public class CommandWrapper { public boolean isLoanWriteOff() { return this.actionName.equalsIgnoreCase("WRITEOFF") && this.entityName.equalsIgnoreCase("LOAN"); } + + public boolean isUndoLoanWriteOff() { + return this.actionName.equalsIgnoreCase("UNDOWRITEOFF") && this.entityName.equalsIgnoreCase("LOAN"); + } public boolean isCloseLoanAsObligationsMet() { return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("LOAN"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 338d480a1..535ad77c8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -573,6 +573,16 @@ public class CommandWrapperBuilder { this.href = "/loans/" + loanId + "/transactions/template?command=writeoff"; return this; } + + public CommandWrapperBuilder undoWriteOffLoanTransaction(final Long loanId) { + this.actionName = "UNDOWRITEOFF"; + this.entityName = "LOAN"; + this.entityId = null; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/transactions/template?command=undowriteoff"; + return this; + } + public CommandWrapperBuilder closeLoanAsRescheduledTransaction(final Long loanId) { this.actionName = "CLOSEASRESCHEDULED"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 761499204..2a1e9b931 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -346,7 +346,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("loanApplicationModificationCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDelete()) { handler = this.applicationContext.getBean("loanApplicationDeletionCommandHandler", NewCommandSourceHandler.class); - } else { + } else if (wrapper.isUndoLoanWriteOff()) { + handler = this.applicationContext.getBean("undoWriteOffLoanCommandHandler", NewCommandSourceHandler.class); + }else { throw new UnsupportedCommandException(wrapper.commandName()); } } else if (wrapper.isLoanChargeResource()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java index 23fa5d035..69093b829 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java @@ -149,7 +149,10 @@ public class LoanTransactionsApiResource { } else if (is(commandParam, "close")) { final CommandWrapper commandRequest = builder.closeLoanTransaction(loanId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - } + }else if (is(commandParam, "undowriteoff")) { + final CommandWrapper commandRequest = builder.undoWriteOffLoanTransaction(loanId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam); } 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 6d08b5129..acbec2897 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 @@ -2097,6 +2097,21 @@ public class Loan extends AbstractPersistable { return changedTransactionDetail; } + + public ChangedTransactionDetail undoWrittenOff(final List existingTransactionIds, final List existingReversedTransactionIds){ + existingTransactionIds.addAll(findExistingTransactionIds()); + existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); + final LoanTransaction writeOffTransaction = findWriteOffTransaction(); + writeOffTransaction.reverse(); + this.loanStatus = LoanStatus.ACTIVE.getValue(); + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory + .determineProcessor(this.transactionProcessingStrategy); + final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); + ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), + allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); + updateLoanSummaryDerivedFields(); + return changedTransactionDetail; + } private LoanTransaction findWriteOffTransaction() { @@ -2350,7 +2365,7 @@ public class Loan extends AbstractPersistable { return status().isClosedObligationsMet(); } - private boolean isClosedWrittenOff() { + public boolean isClosedWrittenOff() { return status().isClosedWrittenOff(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UndoWriteOffLoanCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UndoWriteOffLoanCommandHandler.java new file mode 100755 index 000000000..83f757f13 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UndoWriteOffLoanCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UndoWriteOffLoanCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public UndoWriteOffLoanCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.undoWriteOff(command.getLoanId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java index 7c9870675..5bde6dce9 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java @@ -74,6 +74,6 @@ public interface LoanWritePlatformService { void applyChargeForOverdueLoans() throws JobExecutionException; - + CommandProcessingResult undoWriteOff(Long loanId); } \ No newline at end of file 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 240551ff7..54cfd973c 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 @@ -546,6 +546,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)) { throw new PlatformServiceUnavailableException( "error.msg.loan.transfer.transaction.update.not.allowed", "Loan transaction:" + transactionId + " update not allowed as it involves in account transfer", transactionId); } + if(loan.isClosedWrittenOff()){ throw new PlatformServiceUnavailableException( + "error.msg.loan.written.off.update.not.allowed", "Loan transaction:" + transactionId + + " update not allowed as loan status is written off", transactionId); } final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); @@ -1692,4 +1695,33 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } } + + @Override + public CommandProcessingResult undoWriteOff(Long loanId) { + final Loan loan = this.loanAssembler.assembleFrom(loanId); + checkClientOrGroupActive(loan); + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + if(!loan.isClosedWrittenOff()){ + throw new PlatformServiceUnavailableException( + "error.msg.loan.status.not.written.off.update.not.allowed", "Loan :" + loanId + + " update not allowed as loan status is not written off", loanId); + } + ChangedTransactionDetail changedTransactionDetail =loan.undoWrittenOff(existingTransactionIds,existingReversedTransactionIds); + if (changedTransactionDetail != null) { + for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { + this.loanTransactionRepository.save(mapEntry.getValue()); + this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); + } + } + this.loanRepository.save(loan); + + postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + return new CommandProcessingResultBuilder() // + .withOfficeId(loan.getOfficeId()) // + .withClientId(loan.getClientId()) // + .withGroupId(loan.getGroupId()) // + .withLoanId(loanId) // + .build(); + } } \ No newline at end of file