From 118f4904c6d5100cbf9ebec694358bc8c8935474 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Thu, 4 Dec 2014 11:31:12 +0530 Subject: [PATCH] MIFOSX-1709 : guarantor on hold funds functionality --- .../commands/domain/CommandWrapper.java | 4 + .../service/CommandWrapperBuilder.java | 11 +- .../SynchronousCommandProcessingService.java | 2 + .../account/data/AccountTransferDTO.java | 9 +- ...ountTransfersWritePlatformServiceImpl.java | 23 +- ...ngInstructionWritePlatformServiceImpl.java | 3 +- .../BusinessEventNotificationConstants.java | 39 ++ .../common/service/BusinessEventListner.java | 16 + .../service/BusinessEventNotifierService.java | 21 + .../BusinessEventNotifierServiceImpl.java | 63 ++ .../loanaccount/api/LoansApiResource.java | 3 + .../portfolio/loanaccount/domain/Loan.java | 74 +-- .../domain/LoanAccountDomainServiceJpa.java | 10 +- .../loanaccount/domain/LoanTransaction.java | 50 +- .../guarantor/GuarantorConstants.java | 7 + .../guarantor/data/GuarantorDTO.java | 18 + .../guarantor/domain/Guarantor.java | 8 + .../domain/GuarantorFundingDetails.java | 61 +- .../domain/GuarantorFundingRepository.java | 12 + .../domain/GuarantorFundingTransaction.java | 28 +- ...GuarantorFundingTransactionRepository.java | 21 + .../service/GuarantorDomainService.java | 22 + .../service/GuarantorDomainServiceImpl.java | 558 ++++++++++++++++++ ...ritePlatformServiceJpaRepositoryIImpl.java | 48 +- .../RecoverFromGuarantorCommandHandler.java | 30 + ...WritePlatformServiceJpaRepositoryImpl.java | 11 +- .../service/LoanWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 52 +- .../savings/SavingsApiConstants.java | 11 +- .../SavingsTransactionBooleanValues.java | 10 +- ...ountOnHoldFundTransactionsApiResource.java | 72 +++ .../savings/data/SavingsAccountData.java | 24 +- .../DepositAccountDomainServiceJpa.java | 15 +- .../DepositAccountOnHoldTransaction.java | 60 +- ...sitAccountOnHoldTransactionRepository.java | 15 + .../savings/domain/SavingsAccount.java | 20 +- .../SavingsAccountDomainServiceJpa.java | 2 +- ...tOnHoldTransactionReadPlatformService.java | 17 + ...oldTransactionReadPlatformServiceImpl.java | 109 ++++ ...DepositAccountReadPlatformServiceImpl.java | 10 +- ...WritePlatformServiceJpaRepositoryImpl.java | 6 +- ...SavingsAccountReadPlatformServiceImpl.java | 19 +- ..._on_hold_fund_changes_for_transactions.sql | 14 + 43 files changed, 1487 insertions(+), 123 deletions(-) create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/BusinessEventNotificationConstants.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventListner.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierServiceImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/data/GuarantorDTO.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransactionRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/RecoverFromGuarantorCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/DepositAccountOnHoldFundTransactionsApiResource.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java create mode 100755 mifosng-provider/src/main/resources/sql/migrations/core_db/V222__guarantor_on_hold_fund_changes_for_transactions.sql 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 39a47bf19..7a47f73c1 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -290,6 +290,10 @@ public class CommandWrapper { return this.entityName.equalsIgnoreCase("GUARANTOR"); } + public boolean isGuaranteeRecovery() { + return this.actionName.equalsIgnoreCase("RECOVERGUARANTEES"); + } + public boolean isGLAccountResource() { return this.entityName.equalsIgnoreCase("GLACCOUNT"); } 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 f12c2c948..ecb85aac0 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -186,7 +186,16 @@ public class CommandWrapperBuilder { this.entityName = "GUARANTOR"; this.entityId = null; this.loanId = loanId; - this.href = "/loans/" + loanId + "/guarantors/template"; + this.href = "/loans/" + loanId + "/guarantors"; + return this; + } + + public CommandWrapperBuilder recoverFromGuarantor(final Long loanId) { + this.actionName = "RECOVERGUARANTEES"; + this.entityName = "LOAN"; + this.entityId = loanId; + this.loanId = loanId; + this.href = "/loans/" + loanId + "?command=recoverGuarantees"; return this; } 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 ec44c6471..b4caafbf9 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -388,6 +388,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("loanApplicationDeletionCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUndoLoanWriteOff()) { handler = this.applicationContext.getBean("undoWriteOffLoanCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isGuaranteeRecovery()) { + handler = this.applicationContext.getBean("recoverFromGuarantorCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java index 220ad00ad..563c065df 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferDTO.java @@ -40,6 +40,7 @@ public class AccountTransferDTO { private final SavingsAccount toSavingsAccount; private final SavingsAccount fromSavingsAccount; private final Boolean isRegularTransaction; + private final Boolean isExceptionForBalanceCheck; public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount, final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId, @@ -47,7 +48,7 @@ public class AccountTransferDTO { final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId, Integer loanInstallmentNumber, Integer transferType, final AccountTransferDetails accountTransferDetails, final String noteText, final String txnExternalId, final Loan loan, SavingsAccount toSavingsAccount, - final SavingsAccount fromSavingsAccount,final Boolean isRegularTransaction) { + final SavingsAccount fromSavingsAccount, final Boolean isRegularTransaction, Boolean isExceptionForBalanceCheck) { this.transactionDate = transactionDate; this.transactionAmount = transactionAmount; this.fromAccountType = fromAccountType; @@ -70,6 +71,7 @@ public class AccountTransferDTO { this.toSavingsAccount = toSavingsAccount; this.fromSavingsAccount = fromSavingsAccount; this.isRegularTransaction = isRegularTransaction; + this.isExceptionForBalanceCheck = isExceptionForBalanceCheck; } public LocalDate getTransactionDate() { @@ -156,9 +158,12 @@ public class AccountTransferDTO { return this.fromSavingsAccount; } - public Boolean isRegularTransaction() { return this.isRegularTransaction; } + public Boolean isExceptionForBalanceCheck() { + return this.isExceptionForBalanceCheck; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java index 1363429ad..0059f4ec5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java @@ -240,7 +240,6 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer public Long transferFunds(final AccountTransferDTO accountTransferDTO) { Long transferTransactionId = null; final boolean isAccountTransfer = true; - final boolean isWithdrawBalance = false; final boolean isRegularTransaction = accountTransferDTO.isRegularTransaction(); AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails(); if (isSavingsToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) { @@ -248,8 +247,19 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer SavingsAccount fromSavingsAccount = null; Loan toLoanAccount = null; if (accountTransferDetails == null) { - fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); - toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + if (accountTransferDTO.getFromSavingsAccount() == null) { + fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); + } else { + fromSavingsAccount = accountTransferDTO.getFromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); + } + if (accountTransferDTO.getLoan() == null) { + toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); + } else { + toLoanAccount = accountTransferDTO.getLoan(); + this.loanAccountAssembler.setHelpers(toLoanAccount); + } + } else { fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); this.savingsAccountAssembler.setHelpers(fromSavingsAccount); @@ -259,7 +269,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), AccountTransferType.fromInt( - accountTransferDTO.getTransferType()).isInterestTransfer(), isWithdrawBalance); + accountTransferDTO.getTransferType()).isInterestTransfer(), accountTransferDTO.isExceptionForBalanceCheck()); final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), @@ -293,11 +303,13 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); } else { fromSavingsAccount = accountTransferDTO.getFromSavingsAccount(); + this.savingsAccountAssembler.setHelpers(fromSavingsAccount); } if (accountTransferDTO.getToSavingsAccount() == null) { toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); } else { toSavingsAccount = accountTransferDTO.getToSavingsAccount(); + this.savingsAccountAssembler.setHelpers(toSavingsAccount); } } else { fromSavingsAccount = accountTransferDetails.fromSavingsAccount(); @@ -308,7 +320,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer, isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), AccountTransferType.fromInt( - accountTransferDTO.getTransferType()).isInterestTransfer(), isWithdrawBalance); + accountTransferDTO.getTransferType()).isInterestTransfer(), accountTransferDTO.isExceptionForBalanceCheck()); final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), @@ -332,6 +344,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId()); } else { fromLoanAccount = accountTransferDTO.getLoan(); + this.loanAccountAssembler.setHelpers(fromLoanAccount); } toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId()); } else { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java index 8e25269d3..bebf56207 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java @@ -221,12 +221,13 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst final AccountTransferDetails accountTransferDetails = this.accountTransferDetailRepository.findOne(data.accountDetailId()); final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = false; accountTransferDetails.accountTransferStandingInstruction().updateLatsRunDate(transactionDate.toDate()); AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(), data.toAccountType(), data.fromAccount().accountId(), data.toAccount().accountId(), data.name() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data .transferType().getValue(), accountTransferDetails, null, null, null, null, fromSavingsAccount, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); transferAmount(sb, accountTransferDTO, data.getId()); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/BusinessEventNotificationConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/BusinessEventNotificationConstants.java new file mode 100755 index 000000000..1e159da76 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/BusinessEventNotificationConstants.java @@ -0,0 +1,39 @@ +/** + * 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.common; + +import java.util.HashSet; +import java.util.Set; + +public class BusinessEventNotificationConstants { + + public static enum BUSINESS_EVENTS { + LOAN_APPROVED("loan_approved"), LOAN_UNDO_APPROVAL("loan_undo_approval"), LOAN_UNDO_DISBURSAL("loan_undo_disbursal"), LOAN_UNDO_TRANSACTION( + "loan_undo_transaction"), LOAN_MAKE_REPAYMENT("loan_repayment_transaction"), LOAN_WRITTEN_OFF("loan_writtenoff"), LOAN_UNDO_WRITTEN_OFF( + "loan_undo_writtenoff"); + + private final String value; + + private BUSINESS_EVENTS(final String value) { + this.value = value; + } + + private static final Set values = new HashSet<>(); + static { + for (final BUSINESS_EVENTS type : BUSINESS_EVENTS.values()) { + values.add(type.value); + } + } + + public static Set getAllValues() { + return values; + } + + public String getValue() { + return this.value; + } + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventListner.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventListner.java new file mode 100755 index 000000000..569d7d2ab --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventListner.java @@ -0,0 +1,16 @@ +/** + * 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.common.service; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +public interface BusinessEventListner { + + public void businessEventToBeExecuted(AbstractPersistable businessEventEntity); + + public void businessEventWasExecuted(AbstractPersistable businessEventEntity); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierService.java new file mode 100755 index 000000000..47163b6e3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierService.java @@ -0,0 +1,21 @@ +/** + * 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.common.service; + +import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.springframework.data.jpa.domain.AbstractPersistable; + +public interface BusinessEventNotifierService { + + public void notifyBusinessEventToBeExecuted(BUSINESS_EVENTS businessEvent, AbstractPersistable businessEventEntity); + + public void notifyBusinessEventWasExecuted(BUSINESS_EVENTS businessEvent, AbstractPersistable businessEventEntity); + + public void addBusinessEventPreListners(BUSINESS_EVENTS businessEvent, BusinessEventListner businessEventListner); + + public void addBusinessEventPostListners(BUSINESS_EVENTS businessEvent, BusinessEventListner businessEventListner); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierServiceImpl.java new file mode 100755 index 000000000..83b06b256 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/common/service/BusinessEventNotifierServiceImpl.java @@ -0,0 +1,63 @@ +/** + * 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.common.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.springframework.data.jpa.domain.AbstractPersistable; +import org.springframework.stereotype.Service; + +@Service +public class BusinessEventNotifierServiceImpl implements BusinessEventNotifierService { + + private final Map> preListners = new HashMap<>(5); + private final Map> postListners = new HashMap<>(5); + + @Override + public void notifyBusinessEventToBeExecuted(BUSINESS_EVENTS businessEvent, AbstractPersistable businessEventEntity) { + List businessEventListners = this.preListners.get(businessEvent); + if (businessEventListners != null) { + for (BusinessEventListner eventListner : businessEventListners) { + eventListner.businessEventToBeExecuted(businessEventEntity); + } + } + } + + @Override + public void notifyBusinessEventWasExecuted(BUSINESS_EVENTS businessEvent, AbstractPersistable businessEventEntity) { + List businessEventListners = this.postListners.get(businessEvent); + if (businessEventListners != null) { + for (BusinessEventListner eventListner : businessEventListners) { + eventListner.businessEventWasExecuted(businessEventEntity); + } + } + } + + @Override + public void addBusinessEventPreListners(BUSINESS_EVENTS businessEvent, BusinessEventListner businessEventListner) { + addBusinessEventListners(businessEvent, businessEventListner, preListners); + } + + @Override + public void addBusinessEventPostListners(BUSINESS_EVENTS businessEvent, BusinessEventListner businessEventListner) { + addBusinessEventListners(businessEvent, businessEventListner, postListners); + } + + private void addBusinessEventListners(BUSINESS_EVENTS businessEvent, BusinessEventListner businessEventListner, + final Map> businessEventListnerMap) { + List businessEventListners = businessEventListnerMap.get(businessEvent); + if (businessEventListners == null) { + businessEventListners = new ArrayList<>(); + businessEventListnerMap.put(businessEvent, businessEventListners); + } + businessEventListners.add(businessEventListner); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index 3fdf7e176..8435e4dab 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -638,6 +638,9 @@ public class LoansApiResource { } else if (is(commandParam, "unassignloanofficer")) { final CommandWrapper commandRequest = builder.unassignLoanOfficer(loanId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "recoverGuarantees")) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(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 24fb49206..2b4bc943c 100755 --- 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 @@ -345,6 +345,9 @@ public class Loan extends AbstractPersistable { @Column(name = "create_standing_instruction_at_disbursement", nullable = true) private Boolean createStandingInstructionAtDisbursement; + @Column(name = "guarantee_amount_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal guaranteeAmountDerived; + public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final LoanTransactionProcessingStrategy transactionProcessingStrategy, @@ -586,7 +589,7 @@ public class Loan extends AbstractPersistable { * @return */ public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate, - final AppUser currentUser) { + final AppUser currentUser) { final Money chargeAmount = loanCharge.getAmount(getCurrency()); Money feeCharges = chargeAmount; Money penaltyCharges = Money.zero(loanCurrency()); @@ -905,7 +908,8 @@ public class Loan extends AbstractPersistable { existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, getOffice(), amountWaived, - transactionDate, feeChargesWaived, penaltyChargesWaived, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), currentUser ); + transactionDate, feeChargesWaived, penaltyChargesWaived, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), + currentUser); final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge, waiveLoanChargeTransaction .getAmount(getCurrency()).getAmount(), loanInstallmentNumber); waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy); @@ -1040,8 +1044,7 @@ public class Loan extends AbstractPersistable { this.collateral.addAll(associateWithThisLoan(loanCollateral)); } - public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule, - AppUser currentUser) { + public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule, AppUser currentUser) { this.repaymentScheduleInstallments.clear(); for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) { @@ -1101,8 +1104,7 @@ public class Loan extends AbstractPersistable { } } - private void updateAccrualsForNonPeriodicAccruals(final Collection accruals, - final AppUser currentUser) { + private void updateAccrualsForNonPeriodicAccruals(final Collection accruals, final AppUser currentUser) { final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged()); for (LoanTransaction loanTransaction : accruals) { @@ -2256,8 +2258,7 @@ public class Loan extends AbstractPersistable { * Ability to regenerate the repayment schedule based on the loans current * details/state. */ - public void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO, - AppUser currentUser) { + public void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO, AppUser currentUser) { final LoanScheduleModel loanSchedule = regenerateScheduleModel(scheduleGeneratorDTO); @@ -2489,15 +2490,13 @@ public class Loan extends AbstractPersistable { public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, - final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, - final AppUser currentUser) { + final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, waiveInterestTransaction.getTransactionDate()); - validateActivityNotBeforeLastTransactionDateForInterestRecalculation(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, - waiveInterestTransaction.getTransactionDate()); + validateActivityNotBeforeLastTransactionDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, waiveInterestTransaction.getTransactionDate()); existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); @@ -2523,7 +2522,7 @@ public class Loan extends AbstractPersistable { HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO(); validateAccountStatus(event); validateActivityNotBeforeClientOrGroupTransferDate(event, repaymentTransaction.getTransactionDate()); - validateActivityNotBeforeLastTransactionDateForInterestRecalculation(event, repaymentTransaction.getTransactionDate()); + validateActivityNotBeforeLastTransactionDate(event, repaymentTransaction.getTransactionDate()); validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays()); validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(), @@ -2544,8 +2543,7 @@ public class Loan extends AbstractPersistable { validateAccountStatus(LoanEvent.LOAN_CHARGE_PAYMENT); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate()); - validateActivityNotBeforeLastTransactionDateForInterestRecalculation(LoanEvent.LOAN_CHARGE_PAYMENT, - paymentTransaction.getTransactionDate()); + validateActivityNotBeforeLastTransactionDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate()); validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays()); validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(), @@ -2936,8 +2934,7 @@ public class Loan extends AbstractPersistable { return loanRepaymentScheduleInstallment; } - public LoanTransaction deriveDefaultInterestWaiverTransaction( - final LocalDateTime createdDate, final AppUser currentUser) { + public LoanTransaction deriveDefaultInterestWaiverTransaction(final LocalDateTime createdDate, final AppUser currentUser) { final Money totalInterestOutstanding = getTotalInterestOutstandingOnLoan(); Money possibleInterestToWaive = totalInterestOutstanding.copy(); @@ -2968,6 +2965,7 @@ public class Loan extends AbstractPersistable { final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO(); + validateActivityNotBeforeLastTransactionDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, transactionForAdjustment.getTransactionDate()); validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays()); validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), holidayDetailDTO.getWorkingDays(), @@ -3009,8 +3007,7 @@ public class Loan extends AbstractPersistable { } public ChangedTransactionDetail undoWrittenOff(final List existingTransactionIds, - final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, - final AppUser currentUser) { + final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO); existingTransactionIds.addAll(findExistingTransactionIds()); @@ -3031,7 +3028,7 @@ public class Loan extends AbstractPersistable { return changedTransactionDetail; } - private LoanTransaction findWriteOffTransaction() { + public LoanTransaction findWriteOffTransaction() { LoanTransaction writeOff = null; for (final LoanTransaction transaction : this.loanTransactions) { @@ -3132,8 +3129,7 @@ public class Loan extends AbstractPersistable { } LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant(); - loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId, - createdDate, currentUser); + loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId, createdDate, currentUser); final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions); if (!isLastTransaction) { final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions."; @@ -3155,8 +3151,7 @@ public class Loan extends AbstractPersistable { } private ChangedTransactionDetail closeDisbursements(final ScheduleGeneratorDTO scheduleGeneratorDTO, - final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, - final AppUser currentUser) { + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final AppUser currentUser) { ChangedTransactionDetail changedTransactionDetail = null; if (isDisbursementAllowed() && atleastOnceDisbursed()) { this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount()); @@ -3236,7 +3231,7 @@ public class Loan extends AbstractPersistable { } this.closedOnDate = closureDate.toDate(); loanTransaction = LoanTransaction.writeoff(this, getOffice(), closureDate, txnExternalId, - DateUtils.getLocalDateTimeOfTenant(), currentUser); + DateUtils.getLocalDateTimeOfTenant(), currentUser); final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions); if (!isLastTransaction) { final String errorMessage = "The closing date of the loan must be on or after latest transaction date."; @@ -3330,7 +3325,7 @@ public class Loan extends AbstractPersistable { return status().isSubmittedAndPendingApproval(); } - private boolean isApproved() { + public boolean isApproved() { return status().isApproved(); } @@ -4102,8 +4097,8 @@ public class Loan extends AbstractPersistable { } } - private void validateActivityNotBeforeLastTransactionDateForInterestRecalculation(final LoanEvent event, final LocalDate activityDate) { - if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return; } + private void validateActivityNotBeforeLastTransactionDate(final LoanEvent event, final LocalDate activityDate) { + if (!(this.repaymentScheduleDetail().isInterestRecalculationEnabled() || this.loanProduct().isHoldGuaranteeFundsEnabled())) { return; } LocalDate lastTransactionDate = getLastUserTransactionDate(); final LocalDate clientOfficeJoiningDate = this.client.getOfficeJoiningLocalDate(); if (lastTransactionDate.isAfter(activityDate)) { @@ -4332,8 +4327,7 @@ public class Loan extends AbstractPersistable { public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails, final JsonCommand command, final List existingTransactionIds, final List existingReversedTransactionIds, - final Map actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO, - final AppUser currentUser) { + final Map actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE); final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName); disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate()); @@ -4426,8 +4420,7 @@ public class Loan extends AbstractPersistable { } public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final ScheduleGeneratorDTO generatorDTO, - final List existingTransactionIds, final List existingReversedTransactionIds, - final AppUser currentUser) { + final List existingTransactionIds, final List existingReversedTransactionIds, final AppUser currentUser) { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); /* @@ -4448,7 +4441,7 @@ public class Loan extends AbstractPersistable { } public ChangedTransactionDetail handleRegenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, - final AppUser currentUser) { + final AppUser currentUser) { regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO, currentUser); return processTransactions(); @@ -4478,8 +4471,7 @@ public class Loan extends AbstractPersistable { return changedTransactionDetail; } - private void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, - final AppUser currentUser) { + private void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, final AppUser currentUser) { LocalDate lastTransactionDate = getLastUserTransactionDate(); generatorDTO.setLastTransactionDate(lastTransactionDate); @@ -4897,4 +4889,16 @@ public class Loan extends AbstractPersistable { return loanCharges; } + + public void setGuaranteeAmount(BigDecimal guaranteeAmountDerived) { + this.guaranteeAmountDerived = guaranteeAmountDerived; + } + + public void updateGuaranteeAmount(BigDecimal guaranteeAmount) { + this.guaranteeAmountDerived = getGuaranteeAmount().add(guaranteeAmount); + } + + public BigDecimal getGuaranteeAmount() { + return this.guaranteeAmountDerived == null ? BigDecimal.ZERO : this.guaranteeAmountDerived; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index e9eeefdec..317ce9881 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -44,7 +44,9 @@ import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; +import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; +import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO; @@ -83,6 +85,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository; private final LoanAccrualWritePlatformService accrualWritePlatformService; private final PlatformSecurityContext context; + private final BusinessEventNotifierService businessEventNotifierService; @Autowired public LoanAccountDomainServiceJpa(final LoanAssembler loanAccountAssembler, final LoanRepository loanRepository, @@ -95,7 +98,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, final CalendarInstanceRepository calendarInstanceRepository, final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository, - final LoanAccrualWritePlatformService accrualWritePlatformService, final PlatformSecurityContext context) { + final LoanAccrualWritePlatformService accrualWritePlatformService, final PlatformSecurityContext context, + final BusinessEventNotifierService businessEventNotifierService) { this.loanAccountAssembler = loanAccountAssembler; this.loanRepository = loanRepository; this.loanTransactionRepository = loanTransactionRepository; @@ -112,6 +116,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository; this.accrualWritePlatformService = accrualWritePlatformService; this.context = context; + this.businessEventNotifierService = businessEventNotifierService; } @Transactional @@ -215,6 +220,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); recalculateAccruals(loan); + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, newRepaymentTransaction); + builderResult.withEntityId(newRepaymentTransaction.getId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 3d77661be..b9b1d3be6 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -100,13 +100,13 @@ public final class LoanTransaction extends AbstractPersistable { @Column(name = "external_id", length = 100, nullable = true, unique = true) private String externalId; - + @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_date", nullable = false) private final Date createdDate; - + @ManyToOne - @JoinColumn(name="appuser_id", nullable = true) + @JoinColumn(name = "appuser_id", nullable = true) private final AppUser appUser; @LazyCollection(LazyCollectionOption.FALSE) @@ -137,7 +137,7 @@ public final class LoanTransaction extends AbstractPersistable { public static LoanTransaction repayment(final Office office, final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) { return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId, - createdDate, appUser); + createdDate, appUser); } public static LoanTransaction recoveryRepayment(final Office office, final Money amount, final PaymentDetail paymentDetail, @@ -148,9 +148,9 @@ public final class LoanTransaction extends AbstractPersistable { public static LoanTransaction loanPayment(final Loan loan, final Office office, final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId, final LoanTransactionType transactionType, - final LocalDateTime createdDate, final AppUser appUser){ - return new LoanTransaction(loan, office, transactionType, paymentDetail, amount.getAmount(), paymentDate, externalId, - createdDate, appUser); + final LocalDateTime createdDate, final AppUser appUser) { + return new LoanTransaction(loan, office, transactionType, paymentDetail, amount.getAmount(), paymentDate, externalId, createdDate, + appUser); } public static LoanTransaction repaymentAtDisbursement(final Office office, final Money amount, final PaymentDetail paymentDetail, @@ -183,7 +183,7 @@ public final class LoanTransaction extends AbstractPersistable { } public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate, - final LocalDateTime createdDate, final AppUser appUser) { + final LocalDateTime createdDate, final AppUser appUser) { return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate.toDateTimeAtStartOfDay() .toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() @@ -191,7 +191,7 @@ public final class LoanTransaction extends AbstractPersistable { } public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate, - final LocalDateTime createdDate, final AppUser appUser) { + final LocalDateTime createdDate, final AppUser appUser) { return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate.toDateTimeAtStartOfDay() .toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() @@ -199,7 +199,7 @@ public final class LoanTransaction extends AbstractPersistable { } public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate, - final LocalDateTime createdDate, final AppUser appUser) { + final LocalDateTime createdDate, final AppUser appUser) { return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate.toDateTimeAtStartOfDay() .toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() @@ -209,15 +209,15 @@ public final class LoanTransaction extends AbstractPersistable { public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) { return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId, - createdDate, appUser); + createdDate, appUser); } public static LoanTransaction copyTransactionProperties(final LoanTransaction loanTransaction) { return new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf, loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion, loanTransaction.feeChargesPortion, loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion, - loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId, - new LocalDateTime(loanTransaction.createdDate), loanTransaction.appUser); + loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId, new LocalDateTime( + loanTransaction.createdDate), loanTransaction.appUser); } public static LoanTransaction accrueLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate, @@ -274,7 +274,7 @@ public final class LoanTransaction extends AbstractPersistable { } public static LoanTransaction writeoff(final Loan loan, final Office office, final LocalDate writeOffDate, final String externalId, - final LocalDateTime createdDate, final AppUser appUser) { + final LocalDateTime createdDate, final AppUser appUser) { return new LoanTransaction(loan, office, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId, createdDate, appUser); } @@ -621,4 +621,26 @@ public final class LoanTransaction extends AbstractPersistable { public void manuallyAdjustedOrReversed() { this.manuallyAdjustedOrReversed = true; } + + private LocalDate getCreatedDate() { + return new LocalDate(this.createdDate); + } + + public boolean isLastTransaction(final LoanTransaction loanTransaction) { + boolean isLatest = false; + if (loanTransaction != null) { + isLatest = this.getTransactionDate().isBefore(loanTransaction.getTransactionDate()) + || (this.getTransactionDate().isEqual(loanTransaction.getTransactionDate()) && this.getCreatedDate().isBefore( + loanTransaction.getCreatedDate())); + } + return isLatest; + } + + public boolean isLatestTransaction(final LoanTransaction loanTransaction) { + boolean isLatest = false; + if (loanTransaction != null) { + isLatest = this.getCreatedDate().isBefore(loanTransaction.getCreatedDate()); + } + return isLatest; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/GuarantorConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/GuarantorConstants.java index dfed1e137..fda161816 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/GuarantorConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/GuarantorConstants.java @@ -47,4 +47,11 @@ public class GuarantorConstants { return this.value; } } + + public static final String GUARANTOR_SELF_GUARANTEE_ERROR = "min.self.guarantee.required"; + public static final String GUARANTOR_EXTERNAL_GUARANTEE_ERROR = "min.external.guarantee.required"; + public static final String GUARANTOR_MANDATORY_GUARANTEE_ERROR = "mandated.guarantee.required"; + public static final String GUARANTOR_INSUFFICIENT_BALANCE_ERROR = "insufficient.balance"; + public static final String GUARANTOR_NOT_ACTIVE_ERROR = "not.active"; + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/data/GuarantorDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/data/GuarantorDTO.java new file mode 100755 index 000000000..231c50f55 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/data/GuarantorDTO.java @@ -0,0 +1,18 @@ +/** + * 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.guarantor.data; + +import java.math.BigDecimal; + +import org.mifosplatform.portfolio.loanaccount.domain.Loan; + + +public class GuarantorDTO { + + private BigDecimal paidAmount; + private Loan loan; + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/Guarantor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/Guarantor.java index a5a46a00b..933c1149a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/Guarantor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/Guarantor.java @@ -345,4 +345,12 @@ public class Guarantor extends AbstractPersistable { } return hasGuarantee; } + + public boolean isSelfGuarantee() { + boolean isSelf = false; + if (isExistingCustomer() && getEntityId().equals(getClientId())) { + isSelf = true; + } + return isSelf; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingDetails.java index 6c805b52f..d76558852 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingDetails.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingDetails.java @@ -20,6 +20,7 @@ import javax.persistence.Table; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; import org.mifosplatform.portfolio.account.domain.AccountAssociations; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -46,7 +47,7 @@ public class GuarantorFundingDetails extends AbstractPersistable { @Column(name = "amount_remaining_derived", scale = 6, precision = 19, nullable = true) private BigDecimal amountRemaining; - + @Column(name = "amount_transfered_derived", scale = 6, precision = 19, nullable = true) private BigDecimal amountTransfered; @@ -70,12 +71,62 @@ public class GuarantorFundingDetails extends AbstractPersistable { public void updateStatus(final GuarantorFundStatusType guarantorFundStatusType) { this.status = guarantorFundStatusType.getValue(); } - - public GuarantorFundStatusType getStatus(){ + + public GuarantorFundStatusType getStatus() { return GuarantorFundStatusType.fromInt(this.status); } - - public SavingsAccount getLinkedSavingsAccount(){ + + public SavingsAccount getLinkedSavingsAccount() { return accountAssociations.linkedSavingsAccount(); } + + public Loan getLoanAccount() { + return this.guarantor.getLoan(); + } + + public BigDecimal getAmount() { + return this.amount; + } + + public BigDecimal getAmountReleased() { + return this.amountReleased == null ? BigDecimal.ZERO : this.amountReleased; + } + + public BigDecimal getAmountRemaining() { + return this.amountRemaining == null ? BigDecimal.ZERO : this.amountRemaining; + } + + public BigDecimal getAmountTransfered() { + return this.amountTransfered == null ? BigDecimal.ZERO : this.amountTransfered; + } + + public void releaseFunds(final BigDecimal amount) { + this.amountReleased = getAmountReleased().add(amount); + this.amountRemaining = getAmountRemaining().subtract(amount); + if (this.amountRemaining.compareTo(BigDecimal.ZERO) == 0) { + this.updateStatus(GuarantorFundStatusType.COMPLETED); + } + } + + public void undoReleaseFunds(final BigDecimal amount) { + this.amountReleased = getAmountReleased().subtract(amount); + this.amountRemaining = getAmountRemaining().add(amount); + if (getStatus().isCompleted() && this.amountRemaining.compareTo(BigDecimal.ZERO) == 1) { + this.updateStatus(GuarantorFundStatusType.ACTIVE); + } + } + + public void withdrawFunds(final BigDecimal amount) { + this.amountTransfered = amount; + } + + public void addGuarantorFundingTransactions(final GuarantorFundingTransaction guarantorFundingTransaction) { + this.guarantorFundingTransactions.add(guarantorFundingTransaction); + } + + public void undoAllTransactions() { + for (GuarantorFundingTransaction fundingTransaction : this.guarantorFundingTransactions) { + fundingTransaction.reverseTransaction(); + } + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingRepository.java new file mode 100755 index 000000000..3c21650bf --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingRepository.java @@ -0,0 +1,12 @@ +/** + * 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.guarantor.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface GuarantorFundingRepository extends JpaRepository, + JpaSpecificationExecutor {} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransaction.java index dc9b9c809..f69cf3721 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransaction.java @@ -5,10 +5,13 @@ */ package org.mifosplatform.portfolio.loanaccount.guarantor.domain; +import java.math.BigDecimal; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; import javax.persistence.Table; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; @@ -24,14 +27,35 @@ public class GuarantorFundingTransaction extends AbstractPersistable { private GuarantorFundingDetails guarantorFundingDetails; @ManyToOne - @JoinColumn(name = "loan_transaction_id", nullable = false) + @JoinColumn(name = "loan_transaction_id", nullable = true) private LoanTransaction loanTransaction; - @ManyToOne + @OneToOne @JoinColumn(name = "deposit_on_hold_transaction_id", nullable = false) private DepositAccountOnHoldTransaction depositAccountOnHoldTransaction; @Column(name = "is_reversed", nullable = false) private boolean reversed; + protected GuarantorFundingTransaction() {} + + public GuarantorFundingTransaction(final GuarantorFundingDetails guarantorFundingDetails, final LoanTransaction loanTransaction, + final DepositAccountOnHoldTransaction depositAccountOnHoldTransaction) { + this.depositAccountOnHoldTransaction = depositAccountOnHoldTransaction; + this.guarantorFundingDetails = guarantorFundingDetails; + this.loanTransaction = loanTransaction; + this.reversed = false; + } + + public void reverseTransaction() { + if (!this.reversed) { + this.reversed = true; + BigDecimal amountForReverse = this.depositAccountOnHoldTransaction.getAmount(); + this.depositAccountOnHoldTransaction.reverseTransaction(); + if (this.depositAccountOnHoldTransaction.getTransactionType().isRelease()) { + this.guarantorFundingDetails.undoReleaseFunds(amountForReverse); + } + } + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransactionRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransactionRepository.java new file mode 100755 index 000000000..d906026b8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/domain/GuarantorFundingTransactionRepository.java @@ -0,0 +1,21 @@ +/** + * 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.guarantor.domain; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface GuarantorFundingTransactionRepository extends JpaRepository, + JpaSpecificationExecutor { + + @Query("from GuarantorFundingTransaction ft where ft.loanTransaction.id in (:loanTransactions)") + List fetchGuarantorFundingTransactions(@Param("loanTransactions") List loanTransactions); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainService.java new file mode 100755 index 000000000..d6e6c7ba8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainService.java @@ -0,0 +1,22 @@ +/** + * 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.guarantor.service; + +import org.joda.time.LocalDate; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingDetails; + +public interface GuarantorDomainService { + + void releaseGuarantor(GuarantorFundingDetails guarantorFundingDetails, LocalDate transactionDate); + + void validateGuarantorBusinessRules(Loan loan); + + void assignGuarantor(GuarantorFundingDetails guarantorFundingDetails, LocalDate transactionDate); + + void transaferFundsFromGuarantor(Loan loan); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java new file mode 100755 index 000000000..a224aba52 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java @@ -0,0 +1,558 @@ +/** + * 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.guarantor.service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.annotation.PostConstruct; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.data.AccountTransferDTO; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; +import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService; +import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.mifosplatform.portfolio.common.service.BusinessEventListner; +import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.guarantor.GuarantorConstants; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.Guarantor; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingDetails; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingRepository; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransaction; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransactionRepository; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorRepository; +import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; +import org.mifosplatform.portfolio.loanproduct.domain.LoanProductGuaranteeDetails; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.domain.DepositAccountOnHoldTransaction; +import org.mifosplatform.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.AbstractPersistable; +import org.springframework.stereotype.Service; + +@Service +public class GuarantorDomainServiceImpl implements GuarantorDomainService { + + private final GuarantorRepository guarantorRepository; + private final GuarantorFundingRepository guarantorFundingRepository; + private final GuarantorFundingTransactionRepository guarantorFundingTransactionRepository; + private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; + private final BusinessEventNotifierService businessEventNotifierService; + private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository; + private final Map releaseLoanIds = new HashMap<>(2); + private final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + + @Autowired + public GuarantorDomainServiceImpl(final GuarantorRepository guarantorRepository, + final GuarantorFundingRepository guarantorFundingRepository, + final GuarantorFundingTransactionRepository guarantorFundingTransactionRepository, + final AccountTransfersWritePlatformService accountTransfersWritePlatformService, + final BusinessEventNotifierService businessEventNotifierService, + final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) { + this.guarantorRepository = guarantorRepository; + this.guarantorFundingRepository = guarantorFundingRepository; + this.guarantorFundingTransactionRepository = guarantorFundingTransactionRepository; + this.accountTransfersWritePlatformService = accountTransfersWritePlatformService; + this.businessEventNotifierService = businessEventNotifierService; + this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository; + } + + @PostConstruct + public void addListners() { + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_APPROVED, new ValidateOnBusinessEvent()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_APPROVED, new HoldFundsOnBusinessEvent()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_UNDO_APPROVAL, new UndoAllFundTransactions()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_UNDO_DISBURSAL, + new ReverseAllFundsOnBusinessEvent()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_UNDO_TRANSACTION, + new ReverseFundsOnBusinessEvent()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, + new ReleaseFundsOnBusinessEvent()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_WRITTEN_OFF, new ReleaseAllFunds()); + this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_UNDO_WRITTEN_OFF, + new ReverseFundsOnBusinessEvent()); + } + + @Override + public void validateGuarantorBusinessRules(Loan loan) { + LoanProduct loanProduct = loan.loanProduct(); + BigDecimal principal = loan.getPrincpal().getAmount(); + if (loanProduct.isHoldGuaranteeFundsEnabled()) { + LoanProductGuaranteeDetails guaranteeData = loanProduct.getLoanProductGuaranteeDetails(); + final List existGuarantorList = this.guarantorRepository.findByLoan(loan); + BigDecimal mandatoryAmount = principal.multiply(guaranteeData.getMandatoryGuarantee()).divide(BigDecimal.valueOf(100)); + BigDecimal minSelfAmount = principal.multiply(guaranteeData.getMinimumGuaranteeFromOwnFunds()).divide(BigDecimal.valueOf(100)); + BigDecimal minExtGuarantee = principal.multiply(guaranteeData.getMinimumGuaranteeFromGuarantor()).divide( + BigDecimal.valueOf(100)); + + BigDecimal actualAmount = BigDecimal.ZERO; + BigDecimal actualSelfAmount = BigDecimal.ZERO; + BigDecimal actualExtGuarantee = BigDecimal.ZERO; + for (Guarantor guarantor : existGuarantorList) { + List fundingDetails = guarantor.getGuarantorFundDetails(); + for (GuarantorFundingDetails guarantorFundingDetails : fundingDetails) { + if (guarantorFundingDetails.getStatus().isActive() || guarantorFundingDetails.getStatus().isWithdrawn() + || guarantorFundingDetails.getStatus().isCompleted()) { + if (guarantor.isSelfGuarantee()) { + actualSelfAmount = actualSelfAmount.add(guarantorFundingDetails.getAmount()).subtract( + guarantorFundingDetails.getAmountTransfered()); + } else { + actualExtGuarantee = actualExtGuarantee.add(guarantorFundingDetails.getAmount()).subtract( + guarantorFundingDetails.getAmountTransfered()); + } + } + } + } + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.guarantor"); + if (actualSelfAmount.compareTo(minSelfAmount) == -1) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_SELF_GUARANTEE_ERROR, + minSelfAmount); + } + + if (actualExtGuarantee.compareTo(minExtGuarantee) == -1) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_EXTERNAL_GUARANTEE_ERROR, + minExtGuarantee); + } + actualAmount = actualAmount.add(actualExtGuarantee).add(actualSelfAmount); + if (actualAmount.compareTo(mandatoryAmount) == -1) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_MANDATORY_GUARANTEE_ERROR, + mandatoryAmount); + } + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + + } + + @Override + public void assignGuarantor(final GuarantorFundingDetails guarantorFundingDetails, final LocalDate transactionDate) { + if (guarantorFundingDetails.getStatus().isActive()) { + SavingsAccount savingsAccount = guarantorFundingDetails.getLinkedSavingsAccount(); + savingsAccount.holdFunds(guarantorFundingDetails.getAmount()); + if (savingsAccount.getWithdrawableBalance().compareTo(BigDecimal.ZERO) == -1) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.guarantor"); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_INSUFFICIENT_BALANCE_ERROR, + savingsAccount.getId()); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + } + DepositAccountOnHoldTransaction onHoldTransaction = DepositAccountOnHoldTransaction.hold(savingsAccount, + guarantorFundingDetails.getAmount(), transactionDate); + GuarantorFundingTransaction guarantorFundingTransaction = new GuarantorFundingTransaction(guarantorFundingDetails, null, + onHoldTransaction); + guarantorFundingDetails.addGuarantorFundingTransactions(guarantorFundingTransaction); + this.depositAccountOnHoldTransactionRepository.save(onHoldTransaction); + } + } + + @Override + public void releaseGuarantor(final GuarantorFundingDetails guarantorFundingDetails, final LocalDate transactionDate) { + BigDecimal amoutForWithdraw = guarantorFundingDetails.getAmountRemaining(); + if (amoutForWithdraw.compareTo(BigDecimal.ZERO) == 1 && (guarantorFundingDetails.getStatus().isActive())) { + SavingsAccount savingsAccount = guarantorFundingDetails.getLinkedSavingsAccount(); + savingsAccount.releaseFunds(amoutForWithdraw); + DepositAccountOnHoldTransaction onHoldTransaction = DepositAccountOnHoldTransaction.release(savingsAccount, amoutForWithdraw, + transactionDate); + GuarantorFundingTransaction guarantorFundingTransaction = new GuarantorFundingTransaction(guarantorFundingDetails, null, + onHoldTransaction); + guarantorFundingDetails.addGuarantorFundingTransactions(guarantorFundingTransaction); + guarantorFundingDetails.releaseFunds(amoutForWithdraw); + guarantorFundingDetails.withdrawFunds(amoutForWithdraw); + guarantorFundingDetails.getLoanAccount().updateGuaranteeAmount(amoutForWithdraw.negate()); + this.depositAccountOnHoldTransactionRepository.save(onHoldTransaction); + this.guarantorFundingRepository.save(guarantorFundingDetails); + } + } + + @Override + public void transaferFundsFromGuarantor(final Loan loan) { + if (loan.getGuaranteeAmount().compareTo(BigDecimal.ZERO) != 1) { return; } + final List existGuarantorList = this.guarantorRepository.findByLoan(loan); + final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = true; + LocalDate transactionDate = LocalDate.now(); + PortfolioAccountType fromAccountType = PortfolioAccountType.SAVINGS; + PortfolioAccountType toAccountType = PortfolioAccountType.LOAN; + final Long toAccountId = loan.getId(); + final String description = "Payment from guarantor savings"; + final Locale locale = null; + final DateTimeFormatter fmt = null; + final PaymentDetail paymentDetail = null; + final Integer fromTransferType = null; + final Integer toTransferType = null; + final Long chargeId = null; + final Integer loanInstallmentNumber = null; + final Integer transferType = AccountTransferType.LOAN_REPAYMENT.getValue(); + final AccountTransferDetails accountTransferDetails = null; + final String noteText = null; + + final String txnExternalId = null; + final SavingsAccount toSavingsAccount = null; + + Long loanId = loan.getId(); + + for (Guarantor guarantor : existGuarantorList) { + final List fundingDetails = guarantor.getGuarantorFundDetails(); + for (GuarantorFundingDetails guarantorFundingDetails : fundingDetails) { + if (guarantorFundingDetails.getStatus().isActive()) { + final SavingsAccount fromSavingsAccount = guarantorFundingDetails.getLinkedSavingsAccount(); + final Long fromAccountId = fromSavingsAccount.getId(); + releaseLoanIds.put(loanId, guarantorFundingDetails.getId()); + try { + AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, + guarantorFundingDetails.getAmountRemaining(), fromAccountType, toAccountType, fromAccountId, toAccountId, + description, locale, fmt, paymentDetail, fromTransferType, toTransferType, chargeId, loanInstallmentNumber, + transferType, accountTransferDetails, noteText, txnExternalId, loan, toSavingsAccount, fromSavingsAccount, + isRegularTransaction, isExceptionForBalanceCheck); + transferAmount(accountTransferDTO); + } finally { + releaseLoanIds.remove(loanId); + } + } + } + } + + } + + /** + * @param accountTransferDTO + */ + private void transferAmount(final AccountTransferDTO accountTransferDTO) { + try { + this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); + } catch (final InsufficientAccountBalanceException e) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.guarantor"); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_INSUFFICIENT_BALANCE_ERROR, + accountTransferDTO.getFromAccountId(), accountTransferDTO.getToAccountId()); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + + } + } + + private void reverseAllFundTransaction(final Loan loan) { + + if (loan.getGuaranteeAmount().compareTo(BigDecimal.ZERO) == 1) { + final List existGuarantorList = this.guarantorRepository.findByLoan(loan); + List guarantorFundingDetailList = new ArrayList<>(); + for (Guarantor guarantor : existGuarantorList) { + final List fundingDetails = guarantor.getGuarantorFundDetails(); + for (GuarantorFundingDetails guarantorFundingDetails : fundingDetails) { + guarantorFundingDetails.undoAllTransactions(); + guarantorFundingDetailList.add(guarantorFundingDetails); + } + } + + if (!guarantorFundingDetailList.isEmpty()) { + loan.setGuaranteeAmount(null); + this.guarantorFundingRepository.save(guarantorFundingDetailList); + } + } + } + + private void holdGuarantorFunds(final Loan loan) { + if (loan.loanProduct().isHoldGuaranteeFundsEnabled()) { + final List existGuarantorList = this.guarantorRepository.findByLoan(loan); + List guarantorFundingDetailList = new ArrayList<>(); + List onHoldTransactions = new ArrayList<>(); + BigDecimal totalGuarantee = BigDecimal.ZERO; + List insufficientBalanceIds = new ArrayList<>(); + for (Guarantor guarantor : existGuarantorList) { + final List fundingDetails = guarantor.getGuarantorFundDetails(); + for (GuarantorFundingDetails guarantorFundingDetails : fundingDetails) { + if (guarantorFundingDetails.getStatus().isActive()) { + SavingsAccount savingsAccount = guarantorFundingDetails.getLinkedSavingsAccount(); + savingsAccount.holdFunds(guarantorFundingDetails.getAmount()); + totalGuarantee = totalGuarantee.add(guarantorFundingDetails.getAmount()); + DepositAccountOnHoldTransaction onHoldTransaction = DepositAccountOnHoldTransaction.hold(savingsAccount, + guarantorFundingDetails.getAmount(), loan.getApprovedOnDate()); + onHoldTransactions.add(onHoldTransaction); + GuarantorFundingTransaction guarantorFundingTransaction = new GuarantorFundingTransaction(guarantorFundingDetails, + null, onHoldTransaction); + guarantorFundingDetails.addGuarantorFundingTransactions(guarantorFundingTransaction); + guarantorFundingDetailList.add(guarantorFundingDetails); + if (savingsAccount.getWithdrawableBalance().compareTo(BigDecimal.ZERO) == -1) { + insufficientBalanceIds.add(savingsAccount.getId()); + } + } + } + } + if (!insufficientBalanceIds.isEmpty()) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.guarantor"); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_INSUFFICIENT_BALANCE_ERROR, + insufficientBalanceIds); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + + } + loan.setGuaranteeAmount(totalGuarantee); + if (!guarantorFundingDetailList.isEmpty()) { + this.depositAccountOnHoldTransactionRepository.save(onHoldTransactions); + this.guarantorFundingRepository.save(guarantorFundingDetailList); + } + } + } + + private void releaseGuarantorFunds(final LoanTransaction loanTransaction) { + final Loan loan = loanTransaction.getLoan(); + if (loan.getGuaranteeAmount().compareTo(BigDecimal.ZERO) == 1) { + final List existGuarantorList = this.guarantorRepository.findByLoan(loan); + List externalGuarantorList = new ArrayList<>(); + List selfGuarantorList = new ArrayList<>(); + BigDecimal selfGuarantee = BigDecimal.ZERO; + BigDecimal guarantorGuarantee = BigDecimal.ZERO; + for (Guarantor guarantor : existGuarantorList) { + final List fundingDetails = guarantor.getGuarantorFundDetails(); + for (GuarantorFundingDetails guarantorFundingDetails : fundingDetails) { + if (guarantorFundingDetails.getStatus().isActive()) { + if (guarantor.isSelfGuarantee()) { + selfGuarantorList.add(guarantorFundingDetails); + selfGuarantee = selfGuarantee.add(guarantorFundingDetails.getAmountRemaining()); + } else if (guarantor.isExistingCustomer()) { + externalGuarantorList.add(guarantorFundingDetails); + guarantorGuarantee = guarantorGuarantee.add(guarantorFundingDetails.getAmountRemaining()); + } + } + } + } + + BigDecimal amountForRelease = loanTransaction.getPrincipalPortion(); + BigDecimal totalGuaranteeAmount = loan.getGuaranteeAmount(); + BigDecimal principal = loan.getPrincpal().getAmount(); + + amountForRelease = amountForRelease.multiply(totalGuaranteeAmount).divide(principal); + List accountOnHoldTransactions = new ArrayList<>(); + + BigDecimal amountLeft = calculateAndRelaseGuarantorFunds(externalGuarantorList, guarantorGuarantee, amountForRelease, + loanTransaction, accountOnHoldTransactions); + + if (amountLeft.compareTo(BigDecimal.ZERO) == 1) { + calculateAndRelaseGuarantorFunds(selfGuarantorList, selfGuarantee, amountLeft, loanTransaction, accountOnHoldTransactions); + externalGuarantorList.addAll(selfGuarantorList); + } + + if (!externalGuarantorList.isEmpty()) { + this.depositAccountOnHoldTransactionRepository.save(accountOnHoldTransactions); + this.guarantorFundingRepository.save(externalGuarantorList); + } + } + + } + + private void releaseAllGuarantors(final LoanTransaction loanTransaction) { + Loan loan = loanTransaction.getLoan(); + if (loan.getGuaranteeAmount().compareTo(BigDecimal.ZERO) == 1) { + final List existGuarantorList = this.guarantorRepository.findByLoan(loan); + List saveGuarantorFundingDetails = new ArrayList<>(); + List onHoldTransactions = new ArrayList<>(); + for (Guarantor guarantor : existGuarantorList) { + final List fundingDetails = guarantor.getGuarantorFundDetails(); + for (GuarantorFundingDetails guarantorFundingDetails : fundingDetails) { + BigDecimal amoutForRelease = guarantorFundingDetails.getAmountRemaining(); + if (amoutForRelease.compareTo(BigDecimal.ZERO) == 1 && (guarantorFundingDetails.getStatus().isActive())) { + SavingsAccount savingsAccount = guarantorFundingDetails.getLinkedSavingsAccount(); + savingsAccount.releaseFunds(amoutForRelease); + DepositAccountOnHoldTransaction onHoldTransaction = DepositAccountOnHoldTransaction.release(savingsAccount, + amoutForRelease, loanTransaction.getTransactionDate()); + onHoldTransactions.add(onHoldTransaction); + GuarantorFundingTransaction guarantorFundingTransaction = new GuarantorFundingTransaction(guarantorFundingDetails, + loanTransaction, onHoldTransaction); + guarantorFundingDetails.addGuarantorFundingTransactions(guarantorFundingTransaction); + guarantorFundingDetails.releaseFunds(amoutForRelease); + saveGuarantorFundingDetails.add(guarantorFundingDetails); + + } + } + + } + + if (!saveGuarantorFundingDetails.isEmpty()) { + this.depositAccountOnHoldTransactionRepository.save(onHoldTransactions); + this.guarantorFundingRepository.save(saveGuarantorFundingDetails); + } + } + } + + private void completeGuarantorFund(final LoanTransaction loanTransaction) { + Loan loan = loanTransaction.getLoan(); + GuarantorFundingDetails guarantorFundingDetails = this.guarantorFundingRepository.findOne(releaseLoanIds.get(loan.getId())); + if (guarantorFundingDetails != null) { + BigDecimal amountForRelease = loanTransaction.getAmount(loan.getCurrency()).getAmount(); + BigDecimal guarantorGuarantee = amountForRelease; + List guarantorList = Arrays.asList(guarantorFundingDetails); + final List accountOnHoldTransactions = new ArrayList<>(); + calculateAndRelaseGuarantorFunds(guarantorList, guarantorGuarantee, amountForRelease, loanTransaction, + accountOnHoldTransactions); + this.depositAccountOnHoldTransactionRepository.save(accountOnHoldTransactions); + this.guarantorFundingRepository.save(guarantorFundingDetails); + } + } + + private BigDecimal calculateAndRelaseGuarantorFunds(List guarantorList, BigDecimal totalGuaranteeAmount, + BigDecimal amountForRelease, LoanTransaction loanTransaction, + final List accountOnHoldTransactions) { + BigDecimal amountLeft = amountForRelease; + for (GuarantorFundingDetails fundingDetails : guarantorList) { + BigDecimal guarantorAmount = amountForRelease.multiply(fundingDetails.getAmountRemaining()).divide(totalGuaranteeAmount, + roundingMode); + if (fundingDetails.getAmountRemaining().compareTo(guarantorAmount) < 1) { + guarantorAmount = fundingDetails.getAmountRemaining(); + } + fundingDetails.releaseFunds(guarantorAmount); + SavingsAccount savingsAccount = fundingDetails.getLinkedSavingsAccount(); + savingsAccount.releaseFunds(guarantorAmount); + DepositAccountOnHoldTransaction onHoldTransaction = DepositAccountOnHoldTransaction.release(savingsAccount, guarantorAmount, + loanTransaction.getTransactionDate()); + accountOnHoldTransactions.add(onHoldTransaction); + GuarantorFundingTransaction guarantorFundingTransaction = new GuarantorFundingTransaction(fundingDetails, loanTransaction, + onHoldTransaction); + fundingDetails.addGuarantorFundingTransactions(guarantorFundingTransaction); + amountLeft = amountLeft.subtract(guarantorAmount); + } + return amountLeft; + } + + private void reverseTransaction(final List loanTransactionIds) { + + List fundingTransactions = this.guarantorFundingTransactionRepository + .fetchGuarantorFundingTransactions(loanTransactionIds); + for (GuarantorFundingTransaction fundingTransaction : fundingTransactions) { + fundingTransaction.reverseTransaction(); + } + if (!fundingTransactions.isEmpty()) { + this.guarantorFundingTransactionRepository.save(fundingTransactions); + } + } + + private class ValidateOnBusinessEvent implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof Loan) { + Loan loan = (Loan) businessEventEntity; + validateGuarantorBusinessRules(loan); + } + } + } + + private class HoldFundsOnBusinessEvent implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof Loan) { + Loan loan = (Loan) businessEventEntity; + holdGuarantorFunds(loan); + } + } + } + + private class ReleaseFundsOnBusinessEvent implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof LoanTransaction) { + LoanTransaction loanTransaction = (LoanTransaction) businessEventEntity; + if (releaseLoanIds.containsKey(loanTransaction.getLoan().getId())) { + completeGuarantorFund(loanTransaction); + } else { + releaseGuarantorFunds(loanTransaction); + } + } + } + } + + private class ReverseFundsOnBusinessEvent implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof LoanTransaction) { + LoanTransaction loanTransaction = (LoanTransaction) businessEventEntity; + List reersedTransactions = new ArrayList<>(1); + reersedTransactions.add(loanTransaction.getId()); + reverseTransaction(reersedTransactions); + } + } + } + + private class ReverseAllFundsOnBusinessEvent implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof Loan) { + Loan loan = (Loan) businessEventEntity; + List reersedTransactions = new ArrayList<>(1); + reersedTransactions.addAll(loan.findExistingTransactionIds()); + reverseTransaction(reersedTransactions); + } + } + } + + private class UndoAllFundTransactions implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof Loan) { + Loan loan = (Loan) businessEventEntity; + reverseAllFundTransaction(loan); + } + } + } + + private class ReleaseAllFunds implements BusinessEventListner { + + @Override + public void businessEventToBeExecuted(@SuppressWarnings("unused") AbstractPersistable businessEventEntity) {} + + @Override + public void businessEventWasExecuted(AbstractPersistable businessEventEntity) { + if (businessEventEntity instanceof LoanTransaction) { + LoanTransaction loanTransaction = (LoanTransaction) businessEventEntity; + releaseAllGuarantors(loanTransaction); + } + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java index c73aa974d..d49838537 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java @@ -10,11 +10,15 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; import org.mifosplatform.portfolio.account.domain.AccountAssociationType; @@ -57,13 +61,14 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto private final CodeValueRepositoryWrapper codeValueRepositoryWrapper; private final SavingsAccountAssembler savingsAccountAssembler; private final AccountAssociationsRepository accountAssociationsRepository; + private final GuarantorDomainService guarantorDomainService; @Autowired public GuarantorWritePlatformServiceJpaRepositoryIImpl(final LoanRepositoryWrapper loanRepositoryWrapper, final GuarantorRepository guarantorRepository, final ClientRepositoryWrapper clientRepositoryWrapper, final StaffRepositoryWrapper staffRepositoryWrapper, final GuarantorCommandFromApiJsonDeserializer fromApiJsonDeserializer, final CodeValueRepositoryWrapper codeValueRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler, - final AccountAssociationsRepository accountAssociationsRepository) { + final AccountAssociationsRepository accountAssociationsRepository, final GuarantorDomainService guarantorDomainService) { this.loanRepositoryWrapper = loanRepositoryWrapper; this.clientRepositoryWrapper = clientRepositoryWrapper; this.fromApiJsonDeserializer = fromApiJsonDeserializer; @@ -72,6 +77,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto this.codeValueRepositoryWrapper = codeValueRepositoryWrapper; this.savingsAccountAssembler = savingsAccountAssembler; this.accountAssociationsRepository = accountAssociationsRepository; + this.guarantorDomainService = guarantorDomainService; } @Override @@ -87,6 +93,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto final Collection existGuarantorList) { try { guarantorCommand.validateForCreate(); + validateLoanStatus(loan); final List guarantorFundingDetails = new ArrayList<>(); AccountAssociations accountAssociations = null; if (guarantorCommand.getSavingsId() != null) { @@ -97,6 +104,11 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto GuarantorFundingDetails fundingDetails = new GuarantorFundingDetails(accountAssociations, GuarantorFundStatusType.ACTIVE.getValue(), guarantorCommand.getAmount()); guarantorFundingDetails.add(fundingDetails); + if (loan.isDisbursed() || loan.isApproved() + || (loan.getGuaranteeAmount() != null || loan.loanProduct().isHoldGuaranteeFundsEnabled())) { + this.guarantorDomainService.assignGuarantor(fundingDetails, LocalDate.now()); + loan.updateGuaranteeAmount(fundingDetails.getAmount()); + } } final Long clientRelationshipId = guarantorCommand.getClientRelationshipTypeId(); @@ -162,6 +174,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto guarantorCommand.validateForUpdate(); final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); + validateLoanStatus(loan); final Guarantor guarantorForUpdate = this.guarantorRepository.findByLoanAndId(loan, guarantorId); if (guarantorForUpdate == null) { throw new GuarantorNotFoundException(loanId, guarantorId); } @@ -214,27 +227,42 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto @Transactional public CommandProcessingResult removeGuarantor(final Long loanId, final Long guarantorId, final Long guarantorFundingId) { final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); + validateLoanStatus(loan); final Guarantor guarantorForDelete = this.guarantorRepository.findByLoanAndId(loan, guarantorId); if (guarantorForDelete == null || (guarantorFundingId == null && !guarantorForDelete.getGuarantorFundDetails().isEmpty())) { throw new GuarantorNotFoundException( loanId, guarantorId, guarantorFundingId); } - return removeGuarantor(guarantorForDelete, loanId, guarantorFundingId); + CommandProcessingResult commandProcessingResult = removeGuarantor(guarantorForDelete, loanId, guarantorFundingId); + this.guarantorDomainService.validateGuarantorBusinessRules(loan); + return commandProcessingResult; } private CommandProcessingResult removeGuarantor(final Guarantor guarantorForDelete, final Long loanId, final Long guarantorFundingId) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("Guarantor"); + if (guarantorFundingId == null) { + if (!guarantorForDelete.isActive()) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_NOT_ACTIVE_ERROR); + } guarantorForDelete.updateStatus(false); } else { GuarantorFundingDetails guarantorFundingDetails = guarantorForDelete.getGuarantorFundingDetail(guarantorFundingId); if (guarantorFundingDetails == null) { throw new GuarantorNotFoundException(loanId, guarantorForDelete.getId(), guarantorFundingId); } + if (!guarantorFundingDetails.getStatus().isActive()) { + baseDataValidator.failWithCodeNoParameterAddedToErrorCode(GuarantorConstants.GUARANTOR_NOT_ACTIVE_ERROR); + } GuarantorFundStatusType fundStatusType = GuarantorFundStatusType.DELETED; - if (guarantorForDelete.getLoan().isDisbursed()) { + if (guarantorForDelete.getLoan().isDisbursed() || guarantorForDelete.getLoan().isApproved()) { fundStatusType = GuarantorFundStatusType.WITHDRAWN; + this.guarantorDomainService.releaseGuarantor(guarantorFundingDetails, LocalDate.now()); } guarantorForDelete.updateStatus(guarantorFundingDetails, fundStatusType); } - this.guarantorRepository.save(guarantorForDelete); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + this.guarantorRepository.saveAndFlush(guarantorForDelete); CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder() .withEntityId(guarantorForDelete.getId()).withLoanId(guarantorForDelete.getLoanId()) .withOfficeId(guarantorForDelete.getOfficeId()); @@ -244,7 +272,6 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto return commandProcessingResultBuilder.build(); } - private void validateGuarantorBusinessRules(final Guarantor guarantor) { // validate guarantor conditions if (guarantor.isExistingCustomer()) { @@ -266,11 +293,20 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto } } + private void validateLoanStatus(Loan loan) { + if (!loan.status().isActiveOrAwaitingApprovalOrDisbursal()) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.guarantor"); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("loan.is.closed"); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + } + } + private void handleGuarantorDataIntegrityIssues(final DataIntegrityViolationException dve) { final Throwable realCause = dve.getMostSpecificCause(); logger.error(dve.getMessage(), dve); throw new PlatformDataIntegrityException("error.msg.guarantor.unknown.data.integrity.issue", "Unknown data integrity issue with resource Guarantor: " + realCause.getMessage()); } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/RecoverFromGuarantorCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/RecoverFromGuarantorCommandHandler.java new file mode 100755 index 000000000..c51921679 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/RecoverFromGuarantorCommandHandler.java @@ -0,0 +1,30 @@ +/** + * 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; + +@Service +public class RecoverFromGuarantorCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public RecoverFromGuarantorCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.writePlatformService.recoverFromGuarantor(command.getLoanId()); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 554a13f37..1a312d96c 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -47,6 +47,8 @@ import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; import org.mifosplatform.portfolio.collateral.domain.LoanCollateral; import org.mifosplatform.portfolio.collateral.service.CollateralAssembler; +import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService; import org.mifosplatform.portfolio.fund.domain.Fund; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper; @@ -128,6 +130,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository; private final LoanAccountDomainService loanAccountDomainService; private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; + private final BusinessEventNotifierService businessEventNotifierService; @Autowired public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper, @@ -144,7 +147,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository, final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository, final LoanReadPlatformService loanReadPlatformService, final LoanAccountDomainService loanAccountDomainService, - final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository) { + final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, + final BusinessEventNotifierService businessEventNotifierService) { this.context = context; this.fromJsonHelper = fromJsonHelper; this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator; @@ -171,7 +175,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa this.loanReadPlatformService = loanReadPlatformService; this.loanAccountDomainService = loanAccountDomainService; this.accountNumberFormatRepository = accountNumberFormatRepository; - + this.businessEventNotifierService = businessEventNotifierService; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -751,6 +755,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa changes.put("note", noteText); this.noteRepository.save(note); } + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_APPROVED, loan); } return new CommandProcessingResultBuilder() // @@ -794,6 +800,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final Note note = Note.loanNote(loan, noteText); this.noteRepository.save(note); } + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_UNDO_APPROVAL, loan); } return new CommandProcessingResultBuilder() // 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 93e3151dd..9653811c5 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 @@ -81,4 +81,6 @@ public interface LoanWritePlatformService { void recalculateInterest(); + CommandProcessingResult recoverFromGuarantor(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 d8581ce96..0e35b6e2e 100755 --- 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 @@ -92,7 +92,9 @@ import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDi import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand; import org.mifosplatform.portfolio.collectionsheet.command.SingleDisbursalCommand; import org.mifosplatform.portfolio.collectionsheet.command.SingleRepaymentCommand; +import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; +import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants; @@ -124,6 +126,7 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException; +import org.mifosplatform.portfolio.loanaccount.guarantor.service.GuarantorDomainService; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; @@ -193,6 +196,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper; private final AccountAssociationsRepository accountAssociationRepository; private final AccountTransferDetailRepository accountTransferDetailRepository; + private final BusinessEventNotifierService businessEventNotifierService; + private final GuarantorDomainService guarantorDomainService; @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -217,7 +222,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService, final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper, final AccountAssociationsRepository accountAssociationRepository, - final AccountTransferDetailRepository accountTransferDetailRepository) { + final AccountTransferDetailRepository accountTransferDetailRepository, + final BusinessEventNotifierService businessEventNotifierService, final GuarantorDomainService guarantorDomainService) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -250,6 +256,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanApplicationCommandFromApiJsonHelper = loanApplicationCommandFromApiJsonHelper; this.accountAssociationRepository = accountAssociationRepository; this.accountTransferDetailRepository = accountTransferDetailRepository; + this.businessEventNotifierService = businessEventNotifierService; + this.guarantorDomainService = guarantorDomainService; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -383,10 +391,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null, - AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction); + AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction, + isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } @@ -625,11 +635,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .retriveLoanLinkedAssociation(loan.getId()); final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, - fromSavingsAccount, isRegularTransaction); + fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } updateRecurringCalendarDatesForInterestRecalculation(loan); @@ -689,6 +700,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Map accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(), existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_UNDO_DISBURSAL, loan); } return new CommandProcessingResultBuilder() // @@ -899,6 +911,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); this.loanAccountDomainService.recalculateAccruals(loan); + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_UNDO_TRANSACTION, transactionToAdjust); + if (newTransactionDetail.isRepayment() && newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, newTransactionDetail); + } return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // @@ -1083,6 +1099,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); this.loanAccountDomainService.recalculateAccruals(loan); + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_WRITTEN_OFF, writeoff); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(writeoff.getId()) // @@ -1643,10 +1660,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber, - AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction); + AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction, + isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); return new CommandProcessingResultBuilder() // @@ -1673,12 +1692,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf throw new LinkedAccountRequiredException("loan.disburse.to.savings", errorMessage, loan.getId()); } final SavingsAccount fromSavingsAccount = null; + final boolean isExceptionForBalanceCheck = false; final boolean isRegularTransaction = true; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount.getAmount(), PortfolioAccountType.LOAN, PortfolioAccountType.SAVINGS, loan.getId(), portfolioAccountData.accountId(), "Loan Disbursement", locale, fmt, paymentDetail, LoanTransactionType.DISBURSEMENT.getValue(), null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, txnExternalId, loan, null, fromSavingsAccount, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } @@ -1703,12 +1723,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .getLoanId()); } final SavingsAccount fromSavingsAccount = null; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), installmentChargeData.getInstallmentNumber(), AccountTransferType.CHARGE_PAYMENT.getValue(), null, - null, null, null, null, fromSavingsAccount, isRegularTransaction); + null, null, null, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); transferFeeCharge(sb, accountTransferDTO); } } @@ -1716,12 +1737,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService .retriveLoanLinkedAssociation(chargeData.getLoanId()); final SavingsAccount fromSavingsAccount = null; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); transferFeeCharge(sb, accountTransferDTO); } } @@ -2377,7 +2399,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf LocalDate recalculateFrom = null; Long overdurPenaltyWaitPeriod = null; LocalDate lastTransactionDate = null; - + LoanTransaction writeOffTransaction = loan.findWriteOffTransaction(); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue()); @@ -2412,6 +2434,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); this.loanAccountDomainService.recalculateAccruals(loan); + if (writeOffTransaction != null) { + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_UNDO_WRITTEN_OFF, writeOffTransaction); + } return new CommandProcessingResultBuilder() // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // @@ -2543,10 +2568,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } final SavingsAccount fromSavingsAccount = null; final boolean isRegularTransaction = true; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber, - AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction); + AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction, + isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); return transaction; @@ -2612,6 +2639,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanAccountDomainService.recalculateAccruals(loan); } + @Override + public CommandProcessingResult recoverFromGuarantor(final Long loanId) { + final Loan loan = this.loanAssembler.assembleFrom(loanId); + this.guarantorDomainService.transaferFundsFromGuarantor(loan); + return new CommandProcessingResultBuilder().withLoanId(loanId).build(); + } + private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) { final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId); if (transferTransaction != null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index 9cf393ff7..f2e368273 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -101,6 +101,7 @@ public class SavingsApiConstants { public static final String enforceMinRequiredBalanceParamName = "enforceMinRequiredBalance"; public static final String minBalanceForInterestCalculationParamName = "minBalanceForInterestCalculation"; public static final String withdrawBalanceParamName = "withdrawBalance"; + public static final String onHoldFundsParamName = "onHoldFunds"; // transaction parameters public static final String transactionDateParamName = "transactionDate"; @@ -138,6 +139,11 @@ public class SavingsApiConstants { public static final String charges = "charges"; public static final String linkedAccount = "linkedAccount"; + // Savings on hold transaction + public static final String onHoldTransactionTypeParamName = "transactionType"; + public static final String onHoldTransactionDateParamName = "transactionDate"; + public static final String onHoldReversedParamName = "reversed"; + public static final Set SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, @@ -195,7 +201,7 @@ public class SavingsApiConstants { withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, "summary", "transactions", "productOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", - "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee")); + "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee", onHoldFundsParamName)); public static final Set SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName, paymentTypeIdParamName, @@ -224,4 +230,7 @@ public class SavingsApiConstants { public static final Set SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList( amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName)); + + public static final Set SAVINGS_ACCOUNT_ON_HOLD_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, + amountParamName, onHoldTransactionTypeParamName, onHoldTransactionDateParamName, onHoldReversedParamName)); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsTransactionBooleanValues.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsTransactionBooleanValues.java index d5548268b..81a0ff0dd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsTransactionBooleanValues.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsTransactionBooleanValues.java @@ -11,16 +11,16 @@ public class SavingsTransactionBooleanValues { private final boolean isRegularTransaction; private final boolean isApplyWithdrawFee; private final boolean isInterestTransfer; - private final boolean isWithdrawBalance; + private final boolean isExceptionForBalanceCheck; public SavingsTransactionBooleanValues(final boolean isAccountTransfer, final boolean isRegularTransaction, - final boolean isApplyWithdrawFee, final boolean isInterestTransfer, final boolean isWithdrawBalance) { + final boolean isApplyWithdrawFee, final boolean isInterestTransfer, final boolean isExceptionForBalanceCheck) { this.isAccountTransfer = isAccountTransfer; this.isRegularTransaction = isRegularTransaction; this.isApplyWithdrawFee = isApplyWithdrawFee; this.isInterestTransfer = isInterestTransfer; - this.isWithdrawBalance = isWithdrawBalance; + this.isExceptionForBalanceCheck = isExceptionForBalanceCheck; } public boolean isAccountTransfer() { @@ -39,8 +39,8 @@ public class SavingsTransactionBooleanValues { return this.isInterestTransfer; } - public boolean isWithdrawBalance() { - return this.isWithdrawBalance; + public boolean isExceptionForBalanceCheck() { + return this.isExceptionForBalanceCheck; } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/DepositAccountOnHoldFundTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/DepositAccountOnHoldFundTransactionsApiResource.java new file mode 100755 index 000000000..6ca674a78 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/DepositAccountOnHoldFundTransactionsApiResource.java @@ -0,0 +1,72 @@ +/** + * 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.savings.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; +import org.mifosplatform.portfolio.savings.data.DepositAccountOnHoldTransactionData; +import org.mifosplatform.portfolio.savings.service.DepositAccountOnHoldTransactionReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/savingsaccounts/{savingsId}/onholdtransactions") +@Component +@Scope("singleton") +public class DepositAccountOnHoldFundTransactionsApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final DepositAccountOnHoldTransactionReadPlatformService depositAccountOnHoldTransactionReadPlatformService; + + @Autowired + public DepositAccountOnHoldFundTransactionsApiResource(final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final ApiRequestParameterHelper apiRequestParameterHelper, + final DepositAccountOnHoldTransactionReadPlatformService depositAccountOnHoldTransactionReadPlatformService) { + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.depositAccountOnHoldTransactionReadPlatformService = depositAccountOnHoldTransactionReadPlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@PathParam("savingsId") final Long savingsId, + @QueryParam("guarantorFundingId") final Long guarantorFundingId, @Context final UriInfo uriInfo, + @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, + @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) { + + this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); + + final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder); + + final Page transfers = this.depositAccountOnHoldTransactionReadPlatformService.retriveAll( + savingsId, guarantorFundingId, searchParameters); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer + .serialize(settings, transfers, SavingsApiConstants.SAVINGS_ACCOUNT_ON_HOLD_RESPONSE_DATA_PARAMETERS); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java index 8138b833a..24e8ad231 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java @@ -50,6 +50,7 @@ public class SavingsAccountData { private final BigDecimal minRequiredBalance; private final boolean enforceMinRequiredBalance; private final BigDecimal minBalanceForInterestCalculation; + private final BigDecimal onHoldFunds; // associations private final SavingsAccountSummaryData summary; @@ -83,7 +84,8 @@ public class SavingsAccountData { final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary, final boolean allowOverdraft, final BigDecimal overdraftLimit, - final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, + final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds) { final Collection productOptions = null; final Collection fieldOfficerOptions = null; @@ -105,7 +107,7 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); } public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) { @@ -141,6 +143,7 @@ public class SavingsAccountData { final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; + final BigDecimal onHoldFunds = null; final SavingsAccountSummaryData summary = null; final Collection transactions = null; @@ -164,7 +167,7 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); } public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo, @@ -198,6 +201,7 @@ public class SavingsAccountData { final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; + final BigDecimal onHoldFunds = null; final SavingsAccountSummaryData summary = null; final Collection transactions = null; @@ -221,7 +225,7 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); } public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template, @@ -253,7 +257,8 @@ public class SavingsAccountData { template.interestPostingPeriodTypeOptions, template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.allowOverdraft, account.overdraftLimit, - account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation); + account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation, + account.onHoldFunds); } public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, @@ -275,7 +280,8 @@ public class SavingsAccountData { transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, account.allowOverdraft, account.overdraftLimit, - account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation); + account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation, + account.onHoldFunds); } public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, @@ -311,6 +317,7 @@ public class SavingsAccountData { final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; + final BigDecimal onHoldFunds = null; final SavingsAccountSummaryData summary = null; final Collection transactions = null; @@ -334,7 +341,7 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); } private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId, @@ -354,7 +361,7 @@ public class SavingsAccountData { final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, final Collection charges, final Collection chargeOptions, final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds) { this.id = id; this.accountNo = accountNo; this.depositType = depositType; @@ -409,6 +416,7 @@ public class SavingsAccountData { this.minRequiredBalance = minRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; + this.onHoldFunds = onHoldFunds; } private SavingsAccountChargeData getWithdrawalFee() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java index 9a5ad8543..d796e3cfc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountDomainServiceJpa.java @@ -176,11 +176,11 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, DepositAccountType.SAVINGS_DEPOSIT); - + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(closedDate, account.getAccountBalance(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, null, null, transferDescription, locale, fmt, null, null, null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account, - isAccountTransfer); + isAccountTransfer, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } else { final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(), @@ -254,11 +254,11 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, DepositAccountType.SAVINGS_DEPOSIT); - + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(closedDate, transactionAmount, PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, null, null, transferDescription, locale, fmt, null, null, null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } else { final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(), @@ -315,7 +315,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue); if (closureType.isTransferToSavings()) { - + final boolean isExceptionForBalanceCheck = false; final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName); final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, @@ -323,7 +323,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(closedDate, account.getAccountBalance(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, null, null, transferDescription, locale, fmt, null, null, null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } else { final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(), @@ -371,6 +371,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue); if (closureType.isTransferToSavings()) { + final boolean isExceptionForBalanceCheck = false; final Long toSavingsId = command.longValueOfParameterNamed(toSavingsAccountIdParamName); final String transferDescription = command.stringValueOfParameterNamed(transferDescriptionParamName); final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId, @@ -378,7 +379,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(closedDate, account.getAccountBalance(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, null, null, transferDescription, locale, fmt, null, null, null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } else { final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(), diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java index 09142300c..99157ca8a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java @@ -8,14 +8,21 @@ package org.mifosplatform.portfolio.savings.domain; import java.math.BigDecimal; import java.util.Date; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; +import org.joda.time.LocalDate; +import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransaction; +import org.mifosplatform.portfolio.savings.DepositAccountOnHoldTransactionType; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity @@ -36,8 +43,59 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistable { @Temporal(TemporalType.DATE) @Column(name = "transaction_date", nullable = false) private Date transactionDate; - + @Column(name = "is_reversed", nullable = false) private boolean reversed; + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_date", nullable = false) + private Date createdDate; + + @OneToOne(cascade = CascadeType.ALL, mappedBy = "depositAccountOnHoldTransaction", optional = true, orphanRemoval = true) + private GuarantorFundingTransaction guarantorFundingTransaction; + + protected DepositAccountOnHoldTransaction() {} + + private DepositAccountOnHoldTransaction(final SavingsAccount savingsAccount, final BigDecimal amount, + final DepositAccountOnHoldTransactionType transactionType, final LocalDate transactionDate, final boolean reversed) { + this.savingsAccount = savingsAccount; + this.amount = amount; + this.transactionType = transactionType.getValue(); + this.transactionDate = transactionDate.toDate(); + this.createdDate = new Date(); + this.reversed = reversed; + } + + public static DepositAccountOnHoldTransaction hold(final SavingsAccount savingsAccount, final BigDecimal amount, + final LocalDate transactionDate) { + boolean reversed = false; + return new DepositAccountOnHoldTransaction(savingsAccount, amount, DepositAccountOnHoldTransactionType.HOLD, transactionDate, + reversed); + } + + public static DepositAccountOnHoldTransaction release(final SavingsAccount savingsAccount, final BigDecimal amount, + final LocalDate transactionDate) { + boolean reversed = false; + return new DepositAccountOnHoldTransaction(savingsAccount, amount, DepositAccountOnHoldTransactionType.RELEASE, transactionDate, + reversed); + } + + public BigDecimal getAmount() { + return this.amount; + } + + public void reverseTransaction() { + this.reversed = true; + if (this.getTransactionType().isHold()) { + this.savingsAccount.releaseFunds(this.amount); + } else { + this.savingsAccount.holdFunds(this.amount); + } + } + + public DepositAccountOnHoldTransactionType getTransactionType() { + return DepositAccountOnHoldTransactionType.fromInt(this.transactionType); + + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java new file mode 100755 index 000000000..89c10cd70 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java @@ -0,0 +1,15 @@ +/** + * 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.savings.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + + +public interface DepositAccountOnHoldTransactionRepository extends JpaRepository, +JpaSpecificationExecutor{ + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index c82a1aa66..b7d2af190 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -858,7 +858,7 @@ public class SavingsAccount extends AbstractPersistable { return transactionBeforeLastInterestPosting; } - public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isWithdrawBalance) { + public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isException) { final List transactionsSortedByDate = retreiveListOfTransactions(); Money runningBalance = Money.zero(this.currency); Money minRequiredBalance = minRequiredBalanceDerived(getCurrency()); @@ -874,7 +874,7 @@ public class SavingsAccount extends AbstractPersistable { final BigDecimal withdrawalFee = null; // deal with potential minRequiredBalance and // enforceMinRequiredBalance - if (!isWithdrawBalance && transaction.canProcessBalanceCheck()) { + if (!isException && transaction.canProcessBalanceCheck()) { if (runningBalance.minus(minRequiredBalance).isLessThanZero()) { throw new InsufficientAccountBalanceException( "transactionAmount", getAccountBalance(), withdrawalFee, transactionAmount); } } @@ -2445,4 +2445,20 @@ public class SavingsAccount extends AbstractPersistable { } return minReqBalance; } + + public BigDecimal getOnHoldFunds() { + return this.onHoldFunds == null ? BigDecimal.ZERO : this.onHoldFunds; + } + + public void holdFunds(BigDecimal onHoldFunds) { + this.onHoldFunds = getOnHoldFunds().add(onHoldFunds); + } + + public void releaseFunds(BigDecimal onHoldFunds) { + this.onHoldFunds = getOnHoldFunds().subtract(onHoldFunds); + } + + public BigDecimal getWithdrawableBalance() { + return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java index dbe3740c2..b4753400e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -84,7 +84,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); } - account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isWithdrawBalance()); + account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck()); saveTransactionToGenerateTransactionId(withdrawal); this.savingsAccountRepository.save(account); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformService.java new file mode 100755 index 000000000..6605efe06 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformService.java @@ -0,0 +1,17 @@ +/** + * 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.savings.service; + +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.data.DepositAccountOnHoldTransactionData; + +public interface DepositAccountOnHoldTransactionReadPlatformService { + + public Page retriveAll(final Long savingsId, final Long guarantorFundingId, + final SearchParameters searchParameters); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java new file mode 100755 index 000000000..e93b82897 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java @@ -0,0 +1,109 @@ +/** + * 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.savings.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.core.service.PaginationHelper; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.data.DepositAccountOnHoldTransactionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class DepositAccountOnHoldTransactionReadPlatformServiceImpl implements DepositAccountOnHoldTransactionReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final DepositAccountOnHoldTransactionsMapper mapper; + + @Autowired + public DepositAccountOnHoldTransactionReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + mapper = new DepositAccountOnHoldTransactionsMapper(); + } + + @Override + public Page retriveAll(Long savingsId, Long guarantorFundingId, SearchParameters searchParameters) { + final StringBuilder sqlBuilder = new StringBuilder(200); + List paramObj = new ArrayList<>(2); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(this.mapper.schema()); + + sqlBuilder.append(" where tr.savings_account_id = ? "); + paramObj.add(savingsId); + if (guarantorFundingId != null) { + sqlBuilder.append(" and gt.guarantor_fund_detail_id = ? "); + paramObj.add(guarantorFundingId); + } + + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + final String sqlCountRows = "SELECT FOUND_ROWS()"; + final Object[] finalObjectArray = paramObj.toArray(); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, this.mapper); + + } + + private static final class DepositAccountOnHoldTransactionsMapper implements RowMapper { + + private final String schemaSql; + + public DepositAccountOnHoldTransactionsMapper() { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, "); + sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount,"); + sqlBuilder.append("tr.is_reversed as reversed "); + sqlBuilder.append("from m_deposit_account_on_hold_transaction tr "); + sqlBuilder.append(" left join m_guarantor_transaction gt on gt.deposit_on_hold_transaction_id = tr.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public DepositAccountOnHoldTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) + throws SQLException { + final Long id = rs.getLong("transactionId"); + final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); + final EnumOptionData transactionType = SavingsEnumerations.onHoldTransactionType(transactionTypeInt); + + final LocalDate date = JdbcSupport.getLocalDate(rs, "transactionDate"); + final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transactionAmount"); + final boolean reversed = rs.getBoolean("reversed"); + return DepositAccountOnHoldTransactionData.instance(id, amount, transactionType, date, reversed); + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java index f7ee1ff1d..ddb8aff31 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -350,8 +350,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final Collection charges = fromChargesToSavingsCharges(productCharges); final boolean feeChargesOnly = false; - final Collection chargeOptions = this.chargeReadPlatformService - .retrieveSavingsApplicableCharges(feeChargesOnly); + final Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsApplicableCharges(feeChargesOnly); Collection fieldOfficerOptions = null; @@ -427,8 +426,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final Collection charges = null; final boolean feeChargesOnly = true; - final Collection chargeOptions = this.chargeReadPlatformService - .retrieveSavingsApplicableCharges(feeChargesOnly); + final Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsApplicableCharges(feeChargesOnly); if (depositAccountType.isFixedDeposit()) { @@ -1398,10 +1396,12 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final Long toAccountId = rs.getLong("toAcc"); final BigDecimal transactionAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); final boolean isRegularTransaction = false; + final boolean isExceptionForBalanceCheck = false; final LocalDate transactionDate = JdbcSupport.getLocalDate(rs, "transactionDate"); return new AccountTransferDTO(transactionDate, transactionAmount, PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, fromAccountId, toAccountId, "trasfer interest to savings", null, null, null, null, null, null, null, - AccountTransferType.INTEREST_TRANSFER.getValue(), null, null, null, null, null, null, isRegularTransaction); + AccountTransferType.INTEREST_TRANSFER.getValue(), null, null, null, null, null, null, isRegularTransaction, + isExceptionForBalanceCheck); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index e86671d2f..fdf5e78dc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -205,11 +205,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo } else { final SavingsAccount fromSavingsAccount = null; boolean isRegularTransaction = false; + final boolean isExceptionForBalanceCheck = false; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(account.getActivationLocalDate(), amountForDeposit.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, portfolioAccountData.accountId(), account.getId(), "Account Transfer", locale, fmt, null, null, null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, account, fromSavingsAccount, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } final boolean isInterestTransfer = false; @@ -279,12 +280,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo this.depositAccountDomainService.handleRDDeposit(account, fmt, account.getActivationLocalDate(), amountForDeposit.getAmount(), null, isRegularTransaction); } else { + final boolean isExceptionForBalanceCheck = false; final SavingsAccount fromSavingsAccount = null; final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(account.getActivationLocalDate(), amountForDeposit.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, portfolioAccountData.accountId(), account.getId(), "Account Transfer", locale, fmt, null, null, null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, account, fromSavingsAccount, - isRegularTransaction); + isRegularTransaction, isExceptionForBalanceCheck); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 241ca7384..7ac4bd03c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -252,7 +252,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sa.total_penalty_charge_derived as totalPenaltyCharge, "); sqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation,"); sqlBuilder.append("sa.min_required_balance as minRequiredBalance, "); - sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance "); + sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, "); + sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds "); sqlBuilder.append("from m_savings_account sa "); sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); @@ -414,6 +415,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge"); final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minBalanceForInterestCalculation"); + final BigDecimal onHoldFunds = rs.getBigDecimal("onHoldFunds"); final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge, @@ -423,7 +425,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, - allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation); + allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, + onHoldFunds); } } @@ -517,8 +520,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Collection charges = fromChargesToSavingsCharges(productCharges); final boolean feeChargesOnly = false; - final Collection chargeOptions = this.chargeReadPlatformService - .retrieveSavingsApplicableCharges(feeChargesOnly); + final Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsApplicableCharges(feeChargesOnly); Collection fieldOfficerOptions = null; @@ -576,8 +578,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Collection charges = null; final boolean feeChargesOnly = true; - final Collection chargeOptions = this.chargeReadPlatformService - .retrieveSavingsApplicableCharges(feeChargesOnly); + final Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsApplicableCharges(feeChargesOnly); template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, @@ -743,7 +744,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, - amount, runningBalance, reversed, transfer,submittedOnDate); + amount, runningBalance, reversed, transfer, submittedOnDate); } } @@ -927,6 +928,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final SavingsAccountStatusEnumData status = null; // final LocalDate annualFeeNextDueDate = null; final SavingsAccountSummaryData summary = null; + final BigDecimal onHoldFunds = null; final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault(); final EnumOptionData depositType = null; @@ -934,7 +936,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, - allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation); + allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, + onHoldFunds); } } diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V222__guarantor_on_hold_fund_changes_for_transactions.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V222__guarantor_on_hold_fund_changes_for_transactions.sql new file mode 100755 index 000000000..d394ea1a8 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V222__guarantor_on_hold_fund_changes_for_transactions.sql @@ -0,0 +1,14 @@ +ALTER TABLE `m_deposit_account_on_hold_transaction` + ADD COLUMN `created_date` DATETIME NOT NULL; + +ALTER TABLE `m_guarantor_transaction` + ALTER `loan_transaction_id` DROP DEFAULT; + +ALTER TABLE `m_guarantor_transaction` + CHANGE COLUMN `loan_transaction_id` `loan_transaction_id` BIGINT(20) NULL ; + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'RECOVERGUARANTEES_LOAN', 'LOAN', 'RECOVERGUARANTEES', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'RECOVERGUARANTEES_LOAN_CHECKER', 'LOAN', 'RECOVERGUARANTEES_CHECKER', 0); +ALTER TABLE `m_loan` + DROP COLUMN `guarantee_outstanding_amount_derived`; + \ No newline at end of file