From e68db3551e05b7ffab1bc20bd07b3d6a5e1cd43c Mon Sep 17 00:00:00 2001 From: Madhukar Date: Wed, 26 Sep 2012 14:28:19 +0530 Subject: [PATCH] validation fix for renewing account validations for renew Account populate interest for preclosure deposit account get available interest for withdrawal for UI seperated renewing deposit functionality Renew for FD & validation fix --- .../api/DepositAccountsApiResource.java | 8 +- .../DepositAccountWithdrawalCommand.java | 16 +--- .../ClientAccountSummaryCollectionData.java | 9 +- .../platform/api/data/DepositAccountData.java | 48 +++++++++- .../api/data/DepositPermissionData.java | 18 +++- .../ApiDataConversionServiceImpl.java | 6 +- .../GoogleGsonApiJsonSerializerService.java | 2 +- .../ClientReadPlatformServiceImpl.java | 7 +- .../DepositAccountReopenException.java | 2 +- .../saving/domain/DepositAccount.java | 96 +++++++------------ .../DepositLifecycleStateMachineImpl.java | 2 +- .../service/DepositAccountAssembler.java | 61 ++++++++++-- ...DepositAccountReadPlatformServiceImpl.java | 7 +- ...ositAccountWithdrawalCommandValidator.java | 2 - .../DepositAccountWritePlatformService.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 85 +++++++--------- .../RenewDepositAccountCommandValidator.java | 43 +++++++++ 17 files changed, 252 insertions(+), 164 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/saving/service/RenewDepositAccountCommandValidator.java diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/DepositAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/DepositAccountsApiResource.java index ec49ed2c2..09376c2b0 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/DepositAccountsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/DepositAccountsApiResource.java @@ -1,6 +1,5 @@ package org.mifosng.platform.api; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -72,7 +71,8 @@ public class DepositAccountsApiResource { "interestCompoundedEvery","interestCompoundingAllowed", "interestCompoundedEveryPeriodType", "renewalAllowed", "preClosureAllowed", "preClosureInterestRate", - "withdrawnonDate","rejectedonDate","closedonDate","transactions","interestPaid","isInterestWithdrawable","availableInterestForWithdrawal")); + "withdrawnonDate","rejectedonDate","closedonDate","transactions","interestPaid","isInterestWithdrawable", + "availableInterestForWithdrawal","availableWithdrawalAmount")); @POST @@ -207,6 +207,10 @@ public class DepositAccountsApiResource { DepositAccountWithdrawInterestCommand command = apiDataConversionService.convertJsonToDepositAccountWithdrawInterestCommand(accountId, jsonRequestBody); EntityIdentifier identifier = this.depositAccountWritePlatformService.withdrawDepositAccountInterestMoney(command); response = Response.ok().entity(identifier).build(); + } else if(is(commandParam, "renew")){ + DepositAccountCommand command = apiDataConversionService.convertJsonToDepositAccountCommand(accountId, jsonRequestBody); + EntityIdentifier entityIdentifier = this.depositAccountWritePlatformService.renewDepositAccount(command); + return Response.ok().entity(entityIdentifier).build(); } else { DepositStateTransitionCommand command=apiDataConversionService.convertJsonToDepositStateTransitionCommand(accountId, jsonRequestBody); if (is(commandParam, "reject")) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountWithdrawalCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountWithdrawalCommand.java index fd2c677b3..9e7878acd 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountWithdrawalCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountWithdrawalCommand.java @@ -1,35 +1,21 @@ package org.mifosng.platform.api.commands; -import java.math.BigDecimal; public class DepositAccountWithdrawalCommand { private final Long accountId; - private final boolean renewAccount; - private final BigDecimal deposit; - private final String note; - public DepositAccountWithdrawalCommand(final Long accountId, final boolean renewAccount, final BigDecimal deposit, final String note) { + public DepositAccountWithdrawalCommand(final Long accountId, final String note) { this.accountId = accountId; - this.renewAccount = renewAccount; - this.deposit = deposit; this.note = note; } public Long getAccountId() { return accountId; } - - public boolean isRenewAccount() { - return renewAccount; - } public String getNote() { return note; } - - public BigDecimal getDeposit() { - return deposit; - } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientAccountSummaryCollectionData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientAccountSummaryCollectionData.java index a0d8f0205..3f10d668c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientAccountSummaryCollectionData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientAccountSummaryCollectionData.java @@ -28,6 +28,8 @@ public class ClientAccountSummaryCollectionData { private final List rejectedDepositAccounts; private final int preclosedDepositAccountsCount; private final List preclosedDepositAccounts; + private final int maturedDepositAccountsCount; + private final List maturedDepositAccounts; public ClientAccountSummaryCollectionData( final List pendingApprovalLoans, @@ -39,7 +41,8 @@ public class ClientAccountSummaryCollectionData { final List withdrawnByClientDespositAccounts, final List rejectedDepositAccounts, final List closedDepositAccounts, - final List preclosedDepositAccounts) { + final List preclosedDepositAccounts, + final List maturedDepositAccounts) { this.pendingApprovalLoans = pendingApprovalLoans; this.awaitingDisbursalLoans = awaitingDisbursalLoans; this.openLoans = openLoans; @@ -50,6 +53,7 @@ public class ClientAccountSummaryCollectionData { this.closedDepositAccounts = closedDepositAccounts; this.rejectedDepositAccounts = rejectedDepositAccounts; this.preclosedDepositAccounts = preclosedDepositAccounts; + this.maturedDepositAccounts = maturedDepositAccounts; this.pendingApprovalLoanCount = this.pendingApprovalLoans.size(); this.awaitingDisbursalLoanCount = this.awaitingDisbursalLoans.size(); @@ -62,10 +66,11 @@ public class ClientAccountSummaryCollectionData { this.closedDepositAccountsCount = this.closedDepositAccounts.size(); this.rejectedDepositAccountsCount = this.rejectedDepositAccounts.size(); this.preclosedDepositAccountsCount = this.preclosedDepositAccounts.size(); + this.maturedDepositAccountsCount = this.maturedDepositAccounts.size(); this.anyLoanCount = this.pendingApprovalLoanCount + this.awaitingDisbursalLoanCount + this.activeLoanCount + this.closedLoanCount +this.pendingApprovalDespositAccountsCount+this.approvedDespositAccountsCount+this.withdrawnByClientDespositAccountsCount+this.closedDepositAccountsCount - +this.rejectedDepositAccountsCount+this.preclosedDepositAccountsCount; + +this.rejectedDepositAccountsCount+this.preclosedDepositAccountsCount+this.maturedDepositAccountsCount; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositAccountData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositAccountData.java index 063b1bc99..15b2e0d76 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositAccountData.java @@ -1,6 +1,8 @@ package org.mifosng.platform.api.data; import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -53,6 +55,7 @@ public class DepositAccountData { private final Collection transactions; private final DepositPermissionData permissions; private final BigDecimal availableInterestForWithdrawal; + private final BigDecimal availableWithdrawalAmount; /* * used when returning account template data but only a clientId is passed, no selected product. @@ -102,6 +105,7 @@ public class DepositAccountData { this.isInterestWithdrawable=false; this.interestCompoundingAllowed = true; this.availableInterestForWithdrawal = new BigDecimal(0); + this.availableWithdrawalAmount= new BigDecimal(0); } public DepositAccountData( @@ -144,7 +148,8 @@ public class DepositAccountData { this.interestPaid=account.getInterestPaid(); this.isInterestWithdrawable=account.isInterestWithdrawable(); this.interestCompoundingAllowed=account.isInterestCompoundingAllowed(); - this.availableInterestForWithdrawal=new BigDecimal(0); + this.availableInterestForWithdrawal=determineAvailableInterestForWithdrawal(account); + this.availableWithdrawalAmount=determineAvalableWithdrawalAmount(account); } public DepositAccountData(final DepositAccountData account, final DepositPermissionData permissions, final Collection transactions) { @@ -184,7 +189,8 @@ public class DepositAccountData { this.interestPaid=account.getInterestPaid(); this.isInterestWithdrawable=account.isInterestWithdrawable(); this.interestCompoundingAllowed = account.isInterestCompoundingAllowed(); - this.availableInterestForWithdrawal=determineAvailableInterestForWithdrawal(account);; + this.availableInterestForWithdrawal=determineAvailableInterestForWithdrawal(account); + this.availableWithdrawalAmount=determineAvalableWithdrawalAmount(account); } public DepositAccountData( @@ -254,6 +260,7 @@ public class DepositAccountData { this.isInterestWithdrawable=isInterestWithdrawable; this.interestCompoundingAllowed = interestCompoundingAllowed; this.availableInterestForWithdrawal = new BigDecimal(0); + this.availableWithdrawalAmount = new BigDecimal(0); } public DepositAccountData( @@ -281,7 +288,7 @@ public class DepositAccountData { this.deposit = deposit; this.maturityInterestRate=interestRate; this.tenureInMonths = tenureInMonths; - this.projectedCommencementDate = null; + this.projectedCommencementDate = new LocalDate(); this.actualCommencementDate = null; this.maturedOn = null; this.projectedInterestAccrued = null; @@ -307,6 +314,7 @@ public class DepositAccountData { this.isInterestWithdrawable=false; this.interestCompoundingAllowed=interestCompoundingAllowed; this.availableInterestForWithdrawal= new BigDecimal(0); + this.availableWithdrawalAmount = new BigDecimal(0); } public Long getId() { @@ -444,10 +452,15 @@ public class DepositAccountData { public BigDecimal getAvailableInterestForWithdrawal() { return availableInterestForWithdrawal; } - + + public BigDecimal getAvailableWithdrawalAmount() { + return availableWithdrawalAmount; + } + private BigDecimal determineAvailableInterestForWithdrawal(final DepositAccountData account) { BigDecimal availableInterestForWithdrawal = BigDecimal.ZERO; + if(this.status!=null){ if (account.getStatus().getId() == 300) { BigDecimal interestGettingForPeriod = BigDecimal.valueOf(account .getActualInterestAccrued().doubleValue() @@ -462,8 +475,35 @@ public class DepositAccountData { .valueOf(interestGettingForPeriod.multiply( new BigDecimal(noOfPeriods)).doubleValue() - account.getInterestPaid().doubleValue()); + }else { + return new BigDecimal(0); } + }else return BigDecimal.ZERO; return availableInterestForWithdrawal; } + + private BigDecimal determineAvalableWithdrawalAmount(final DepositAccountData account) { + + BigDecimal avalablePreclosureWithdrawalAmount = BigDecimal.ZERO; + if(this.status != null){ + if (account.getStatus().getId() == 300) { + if(new LocalDate().isBefore(account.getMaturedOn())){ + MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); + Integer monthsInYear = 12; + BigDecimal interestRateAsFraction = account.getPreClosureInterestRate().divide(BigDecimal.valueOf(100), mc); + BigDecimal interestRateForOneMonth = interestRateAsFraction.divide(BigDecimal.valueOf(monthsInYear.doubleValue()), mc); + Integer noOfMonthsforInterestCal = Months.monthsBetween(account.getActualCommencementDate(), new LocalDate()).getMonths(); + BigDecimal interest = BigDecimal.valueOf(account.getDeposit().doubleValue()*new Double(noOfMonthsforInterestCal)*interestRateForOneMonth.doubleValue()); + avalablePreclosureWithdrawalAmount = interest.add(account.getDeposit()).subtract(account.getInterestPaid()); + }else if(new LocalDate().isAfter(account.getMaturedOn()) || new LocalDate().isEqual(account.getMaturedOn())){ + avalablePreclosureWithdrawalAmount=determineAvailableInterestForWithdrawal(account).add(account.getDeposit()); + } + }else{ + avalablePreclosureWithdrawalAmount=account.getDeposit(); + } + } + return avalablePreclosureWithdrawalAmount; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositPermissionData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositPermissionData.java index fedcf8b6b..421bdaa93 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositPermissionData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DepositPermissionData.java @@ -7,17 +7,23 @@ public class DepositPermissionData { private final boolean undoApprovalAllowed; private final boolean pendingApproval; private final boolean isInterestWithdrawalAllowed; + private final boolean renewelAllowed; + private final boolean isMaturedDepositAccount; private final boolean anyActionOnAccountAllowed; - public DepositPermissionData(final boolean rejectAllowed,final boolean withdrawnByApplicantAllowed,final boolean undoApprovalAllowed,final boolean pendingApproval,final boolean interestWithdrawalAllowed) { + public DepositPermissionData(final boolean rejectAllowed,final boolean withdrawnByApplicantAllowed,final boolean undoApprovalAllowed, + final boolean pendingApproval,final boolean interestWithdrawalAllowed, final boolean renewelAllowed, final boolean isMaturedDepositAccount) { this.rejectAllowed = rejectAllowed; this.withdrawnByApplicantAllowed = withdrawnByApplicantAllowed; this.undoApprovalAllowed = undoApprovalAllowed; this.pendingApproval = pendingApproval; this.isInterestWithdrawalAllowed = interestWithdrawalAllowed; - this.anyActionOnAccountAllowed = isRejectAllowed() || isPendingApproval() || isUndoApprovalAllowed() || isWithdrawnByApplicantAllowed() || isInterestWithdrawalAllowed(); + this.renewelAllowed = renewelAllowed; + this.isMaturedDepositAccount = isMaturedDepositAccount; + this.anyActionOnAccountAllowed = isRejectAllowed() || isPendingApproval() || isUndoApprovalAllowed() || isWithdrawnByApplicantAllowed() || + isInterestWithdrawalAllowed() || isRenewelAllowed() || isMaturedDepositAccount(); } public boolean isRejectAllowed() { @@ -43,4 +49,12 @@ public class DepositPermissionData { public boolean isInterestWithdrawalAllowed() { return isInterestWithdrawalAllowed; } + + public boolean isRenewelAllowed() { + return renewelAllowed; + } + + public boolean isMaturedDepositAccount() { + return isMaturedDepositAccount; + } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index bc00b0d31..bdf9bb26f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -1398,17 +1398,15 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { Type typeOfMap = new TypeToken>(){}.getType(); Map requestMap = gsonConverter.fromJson(json, typeOfMap); - Set supportedParams = new HashSet(Arrays.asList("renewAccount","deposit", "note","locale")); + Set supportedParams = new HashSet(Arrays.asList("note","locale")); checkForUnsupportedParameters(requestMap, supportedParams); Set modifiedParameters = new HashSet(); - BigDecimal deposit=extractBigDecimalParameter("deposit", requestMap, modifiedParameters); - boolean renewAccount=extractBooleanParameter("renewAccount", requestMap, modifiedParameters); String note = extractStringParameter("note", requestMap, modifiedParameters); - return new DepositAccountWithdrawalCommand(resourceIdentifier,renewAccount, deposit, note); + return new DepositAccountWithdrawalCommand(resourceIdentifier,note); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java index 3d4debd19..11a3c143c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java @@ -111,7 +111,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ "renewalAllowed","preClosureAllowed","preClosureInterestRate", "withdrawnonDate","rejectedonDate","closedonDate","transactions", "permissions","isInterestWithdrawable","interestPaid","interestCompoundingAllowed", - "availableInterestForWithdrawal") + "availableInterestForWithdrawal","availableWithdrawalAmount") ); private static final Set CLIENT_DATA_PARAMETERS = new HashSet( diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java index 26df06a60..b04fd4d38 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java @@ -200,6 +200,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService List closedDepositAccounts = new ArrayList(); List rejectedDepositAccounts = new ArrayList(); List preclosedDepositAccounts = new ArrayList(); + List maturedDepositAccounts = new ArrayList(); ClientDespoitAccountSummaryDataMapper depositAccountMapper = new ClientDespoitAccountSummaryDataMapper(); @@ -218,7 +219,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService rejectedDepositAccounts.add(row); } else if (row.getAccountStatusId() == 600) { closedDepositAccounts.add(row); - } else if (row.getAccountStatusId() == 800) { + } else if (row.getAccountStatusId() == 700) { + maturedDepositAccounts.add(row); + }else if (row.getAccountStatusId() == 800) { preclosedDepositAccounts.add(row); } } @@ -226,7 +229,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return new ClientAccountSummaryCollectionData(pendingApprovalLoans, awaitingDisbursalLoans, openLoans, closedLoans, pendingApprovalDepositAccounts, approvedDepositAccounts, withdrawnByClientDespositAccounts, - rejectedDepositAccounts,closedDepositAccounts,preclosedDepositAccounts); + rejectedDepositAccounts,closedDepositAccounts,preclosedDepositAccounts,maturedDepositAccounts); } catch (EmptyResultDataAccessException e) { throw new ClientNotFoundException(clientId); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/DepositAccountReopenException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/DepositAccountReopenException.java index 680531524..b4c64238e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/DepositAccountReopenException.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/DepositAccountReopenException.java @@ -2,7 +2,7 @@ package org.mifosng.platform.exceptions; import org.joda.time.LocalDate; -public class DepositAccountReopenException extends AbstractPlatformResourceNotFoundException{ +public class DepositAccountReopenException extends AbstractPlatformDomainRuleException{ public DepositAccountReopenException(LocalDate maturedDate) { super("error.msg.depositaccount.cannot.reopen.before.mature", "You cannot renew the account that is not matured:"+maturedDate); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java index 2a8f1453e..2dbfd8cbd 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java @@ -359,6 +359,7 @@ public class DepositAccount extends AbstractAuditableCustom { DepositAccountStatus.fromInt(this.depositStatus)); this.depositStatus = statusEnum.getValue(); + this.maturesOnDate=null; this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate(); this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate(); @@ -389,6 +390,7 @@ public class DepositAccount extends AbstractAuditableCustom { DepositAccountStatus.fromInt(this.depositStatus)); this.depositStatus = statusEnum.getValue(); + this.maturesOnDate = null; this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate(); this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate(); @@ -466,64 +468,27 @@ public class DepositAccount extends AbstractAuditableCustom { public Integer getInterestCompoundedEvery() { return this.interestCompoundedEvery; } - - /*public void matureDepositApplication(LocalDate maturedOnDate, DepositLifecycleStateMachine depositLifecycleStateMachine) { - - if (maturedOnDate.isAfter(maturesOnDate()) || (new LocalDate().equals(maturesOnDate()) && maturedOnDate.equals(maturesOnDate()))) { - DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition( - DepositAccountEvent.DEPOSIT_MATURED, - DepositAccountStatus.fromInt(this.depositStatus)); - this.depositStatus = statusEnum.getValue(); - - if(!this.renewalAllowed){ - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(Money.of(this.currency, this.total), maturedOnDate); - depositaccountTransaction.updateAccount(this); - this.depositaccountTransactions.add(depositaccountTransaction); - - DepositAccountStatus statusEnumForClose = depositLifecycleStateMachine.transition( - DepositAccountEvent.DEPOSIT_CLOSED, - DepositAccountStatus.fromInt(this.depositStatus)); - this.depositStatus = statusEnumForClose.getValue(); - - this.depositAmount = null; - this.interestRate = null; - this.tenureInMonths = null; - this.interestCompoundedEvery = null; - this.interestCompoundedFrequencyType = null; - this.closedOnDate = maturedOnDate.toDate(); - this.withdrawnOnDate = null; - this.rejectedOnDate = null; - - }else if(this.renewalAllowed){ - DepositAccountStatus statusEnumForClose = depositLifecycleStateMachine.transition( - DepositAccountEvent.DEPOSIT_CLOSED, - DepositAccountStatus.fromInt(this.depositStatus)); - this.depositStatus = statusEnumForClose.getValue(); - this.closedOnDate = maturedOnDate.toDate(); - this.withdrawnOnDate = null; - this.rejectedOnDate = null; - } - } - if (maturedOnDate.isBefore(new LocalDate()) || maturedOnDate.isBefore(maturesOnDate())) { - - final String errorMessage = "The date on which a deposit matured is cannot be before its matured date: " - + new LocalDate().toString(); - throw new InvalidDepositStateTransitionException("matured", - "cannot.be.before.mature.date", errorMessage, - maturedOnDate, new LocalDate()); + public BigDecimal getInterestRate() { + return interestRate; + } + + public Date getMaturesOnDate() { + return maturesOnDate; + } + + public BigDecimal getPreClosureInterestRate() { + return preClosureInterestRate; + } + + public boolean isPreClosureAllowed() { + return preClosureAllowed; + } + + public Integer getDepositStatus() { + return depositStatus; + } - } - - if(maturedOnDate.equals(maturesOnDate())&& !(maturesOnDate().equals(new LocalDate()))){ - final String errorMessage = "You can not manually mature the deposit account till the maturity date reached " - + new LocalDate().toString(); - throw new InvalidDepositStateTransitionException("matured", - "cannot.manual.mature.deposit.account.date", errorMessage, - maturedOnDate, new LocalDate()); - } - }*/ - public LocalDate maturesOnDate() { LocalDate date = null; if (this.maturesOnDate != null) { @@ -538,20 +503,17 @@ public class DepositAccount extends AbstractAuditableCustom { } - public void withdrawDepositAccountMoney(boolean renewAccount, DepositLifecycleStateMachine depositLifecycleStateMachine) { + public void withdrawDepositAccountMoney(DepositLifecycleStateMachine depositLifecycleStateMachine) { if (new LocalDate().isAfter(maturesOnDate()) || new LocalDate().equals(maturesOnDate())) { - DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_MATURED, DepositAccountStatus.fromInt(this.depositStatus)); + DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_CLOSED, DepositAccountStatus.fromInt(this.depositStatus)); this.depositStatus = statusEnum.getValue(); DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), new LocalDate(),getAccuredInterest()); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); - - DepositAccountStatus statusEnumForClose = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_CLOSED, DepositAccountStatus.fromInt(this.depositStatus)); - this.depositStatus = statusEnumForClose.getValue(); - + this.closedOnDate = new LocalDate().toDate(); this.withdrawnOnDate = null; this.rejectedOnDate = null; @@ -609,4 +571,14 @@ public class DepositAccount extends AbstractAuditableCustom { } } + public void closeDepositAccount(DepositLifecycleStateMachine depositLifecycleStateMachine){ + DepositAccountStatus statusEnumForClose = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_CLOSED, DepositAccountStatus.fromInt(this.depositStatus)); + this.depositStatus = statusEnumForClose.getValue(); + + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), new LocalDate(),Money.of(getDeposit().getCurrency(), getTotal()).minus(getDeposit())); + depositaccountTransaction.updateAccount(this); + this.depositaccountTransactions.add(depositaccountTransaction); + + this.closedOnDate = new LocalDate().toDate(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositLifecycleStateMachineImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositLifecycleStateMachineImpl.java index a9411b410..56ed6a348 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositLifecycleStateMachineImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositLifecycleStateMachineImpl.java @@ -70,7 +70,7 @@ public class DepositLifecycleStateMachineImpl implements } break; case DEPOSIT_CLOSED: - if (from.hasStateOf(DepositAccountStatus.MATURED) ){ + if (from.hasStateOf(DepositAccountStatus.ACTIVE) ){ newState = stateOf(DepositAccountStatus.CLOSED, allowedDepositStatuses); } else{ diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountAssembler.java index 27889b73d..d4c5443d7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountAssembler.java @@ -118,31 +118,72 @@ public class DepositAccountAssembler { return new DepositLifecycleStateMachineImpl(allowedDepositStatuses); } - public DepositAccount assembleFrom(DepositAccount account, BigDecimal newDepositAmount) { + public DepositAccount assembleFrom(DepositAccount account, DepositAccountCommand command) { Client client = account.client(); DepositProduct product = account.product(); + if(command.getProductId() != null){ + product = this.depositProductRepository.findOne(command.getProductId()); + if (product == null || product.isDeleted()) { + throw new DepositProductNotFoundException(command.getProductId()); + } + } - Money deposit = Money.of(product.getCurrency(), newDepositAmount); + Money deposit = account.getDeposit(); + if(command.getDepositAmount() != null){ + deposit = Money.of(account.getDeposit().getCurrency(), command.getDepositAmount()); + } - Integer tenureInMonths = product.getTenureInMonths(); + Integer tenureInMonths = account.getTenureInMonths(); + if(command.getTenureInMonths() != null){ + tenureInMonths =command.getTenureInMonths(); + } - BigDecimal maturityInterestRate = product.getMaturityDefaultInterestRate(); + BigDecimal maturityInterestRate = account.getInterestRate(); + if(command.getMaturityInterestRate() != null){ + maturityInterestRate = command.getMaturityInterestRate(); + } + + BigDecimal preClosureInterestRate = account.getPreClosureInterestRate(); + if(command.getPreClosureInterestRate() != null){ + preClosureInterestRate = command.getPreClosureInterestRate(); + } - BigDecimal preClosureInterestRate = product.getPreClosureInterestRate(); if(product.getMaturityMinInterestRate().compareTo(preClosureInterestRate)==-1){ throw new DepositAccounDataValidationtException(preClosureInterestRate, product.getMaturityMinInterestRate()); } - Integer compoundingInterestEvery = product.getInterestCompoundedEvery(); + Integer compoundingInterestEvery = account.getInterestCompoundedEvery(); + if(command.getInterestCompoundedEvery() != null){ + compoundingInterestEvery = command.getInterestCompoundedEvery(); + } - PeriodFrequencyType compoundingInterestFrequency = product.getInterestCompoundedEveryPeriodType(); + PeriodFrequencyType compoundingInterestFrequency = account.getInterestCompoundedFrequencyType(); + if(command.getInterestCompoundedEveryPeriodType() !=null){ + compoundingInterestFrequency = PeriodFrequencyType.fromInt(command.getInterestCompoundedEveryPeriodType()); + } - boolean renewalAllowed = product.isRenewalAllowed(); + boolean renewalAllowed = account.isRenewalAllowed(); + if(command.isRenewalAllowedChanged()){ + renewalAllowed = command.isRenewalAllowed(); + } - boolean preClosureAllowed = product.isPreClosureAllowed(); + boolean preClosureAllowed = account.isPreClosureAllowed(); + if(command.isPreClosureAllowedChanged()){ + preClosureAllowed = command.isPreClosureAllowed(); + } + + boolean isInterestWithdrawable = account.isInterestWithdrawable(); + if(command.isInterestWithdrawableChanged()){ + isInterestWithdrawable = command.isInterestWithdrawable(); + } + + boolean isInterestCompoundingAllowed = account.isInterestCompoundingAllowed(); + if(command.isInterestCompoundingAllowedChanged()){ + isInterestCompoundingAllowed = command.isInterestCompoundingAllowed(); + } DepositAccount newAccount =new DepositAccount().openNew(client, product, null, deposit, @@ -150,7 +191,7 @@ public class DepositAccountAssembler { tenureInMonths, compoundingInterestEvery, compoundingInterestFrequency, account.maturesOnDate(), renewalAllowed, preClosureAllowed, this.fixedTermDepositInterestCalculator, defaultDepositLifecycleStateMachine(), - account.isInterestWithdrawable(),product.isInterestCompoundingAllowed()); + isInterestWithdrawable,isInterestCompoundingAllowed); newAccount.updateAccount(account); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountReadPlatformServiceImpl.java index a43d79583..abab527ca 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountReadPlatformServiceImpl.java @@ -5,6 +5,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; +import javax.security.auth.login.AccountException; + import org.joda.time.LocalDate; import org.joda.time.Months; import org.mifosng.platform.api.data.ClientData; @@ -254,12 +256,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead public DepositPermissionData retrieveDepositAccountsPermissions(DepositAccountData depositAccountData) { boolean pendingApproval = (depositAccountData.getStatus().getId().equals(100L)); boolean undoApprovalAllowed = (depositAccountData.getStatus().getId().equals(300L)); - //boolean isActive = (depositAccountData.getStatus().getId().equals(300L)); + boolean renewelAllowed = depositAccountData.isRenewalAllowed()&&(new LocalDate().isAfter(depositAccountData.getMaturedOn())||depositAccountData.getMaturedOn().isEqual(new LocalDate())); boolean rejectAllowed = pendingApproval; boolean withdrawnByApplicantAllowed = pendingApproval; boolean interestWithdrawAllowed = depositAccountData.isInterestWithdrawable(); + boolean isMaturedDepositAccount = (depositAccountData.getStatus().getId().equals(700L)); - return new DepositPermissionData(rejectAllowed, withdrawnByApplicantAllowed, undoApprovalAllowed, pendingApproval, interestWithdrawAllowed); + return new DepositPermissionData(rejectAllowed, withdrawnByApplicantAllowed, undoApprovalAllowed, pendingApproval, interestWithdrawAllowed, renewelAllowed, isMaturedDepositAccount); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWithdrawalCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWithdrawalCommandValidator.java index d7f942be0..9f97f0a5c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWithdrawalCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWithdrawalCommandValidator.java @@ -20,8 +20,6 @@ public class DepositAccountWithdrawalCommandValidator { DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("deposit.transaction"); baseDataValidator.reset().parameter("accountId").value(command.getAccountId()).notNull().integerGreaterThanZero(); - baseDataValidator.reset().parameter("renewAccount").value(command.isRenewAccount()).notNull(); - baseDataValidator.reset().parameter("deposit").value(command.getDeposit()).ignoreIfNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter("note").value(command.getNote()).notNull(); if (!dataValidationErrors.isEmpty()) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformService.java index 5b0a496bf..00f95e860 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformService.java @@ -38,4 +38,6 @@ public interface DepositAccountWritePlatformService { @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')") EntityIdentifier withdrawDepositAccountInterestMoney(DepositAccountWithdrawInterestCommand command); -} + @PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')") + EntityIdentifier renewDepositAccount(DepositAccountCommand command); + } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 9207db5fa..15f346e1d 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -1,9 +1,7 @@ package org.mifosng.platform.saving.service; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.apache.commons.lang.StringUtils; @@ -28,8 +26,6 @@ import org.mifosng.platform.exceptions.ProductNotFoundException; import org.mifosng.platform.saving.domain.DepositAccount; import org.mifosng.platform.saving.domain.DepositAccountRepository; import org.mifosng.platform.saving.domain.DepositAccountStatus; -import org.mifosng.platform.saving.domain.DepositAccountTransaction; -import org.mifosng.platform.saving.domain.DepositAccountTransactionType; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl; import org.mifosng.platform.saving.domain.FixedTermDepositInterestCalculator; @@ -300,27 +296,15 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if(new LocalDate().isBefore(account.maturesOnDate())){ this.depositAccountAssembler.adjustTotalAmountForPreclosureInterest(account); } - account.withdrawDepositAccountMoney(command.isRenewAccount(), defaultDepositLifecycleStateMachine()); + account.withdrawDepositAccountMoney(defaultDepositLifecycleStateMachine()); this.depositAccountRepository.save(account); - if(command.isRenewAccount()){ - - BigDecimal deposit = account.getTotal(); - if(command.getDeposit() != null) - deposit = command.getDeposit(); - if(new LocalDate().isBefore(account.maturesOnDate())){ - throw new DepositAccountReopenException(account.maturesOnDate()); - } - final DepositAccount renewedAccount = this.depositAccountAssembler.assembleFrom(account,deposit); - this.depositAccountRepository.save(renewedAccount); - return new EntityIdentifier(renewedAccount.getId()); //returns the new deposit application id - } - String noteText = command.getNote(); if (StringUtils.isNotBlank(noteText)) { Note note = Note.depositNote(account, noteText); this.noteRepository.save(note); } + return new EntityIdentifier(account.getId()); } @@ -338,12 +322,6 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo throw new DepositAccountNotFoundException(command.getAccountId()); } if(account.isInterestWithdrawable() && !account.isInterestCompoundingAllowed()){ - /*BigDecimal interstGettingForPeriod = BigDecimal.valueOf(account.getAccuredInterest().getAmount().doubleValue()/new Double(account.getTenureInMonths())); - LocalDate lastInterestTakenDate = getLastTxnDate(account); - Integer noOfMonthsforInterestCal = Months.monthsBetween(lastInterestTakenDate, new LocalDate()).getMonths(); - Integer noOfPeriods = noOfMonthsforInterestCal / account.getInterestCompoundedEvery(); - BigDecimal availableInterestAmountForWithDrawal = interstGettingForPeriod.multiply(new BigDecimal(noOfPeriods)); - Integer iswithdrawable = availableInterestAmountForWithDrawal.compareTo(command.getWithdrawInterest());*/ BigDecimal totalAvailableInterestForWithdrawal = getTotalWithdrawableInterestAvailable(account); BigDecimal interestPaid = account.getInterstPaid(); @@ -356,41 +334,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo }else { throw new DepositAccountTransactionsException("You can Withdraw "+remainInterestForWithdrawal+" only"); } - + }else { + throw new DepositAccountTransactionsException("You don't have enough money for withdrawal"); } - - /*if(noOfPeriods > 0 ){ - if(iswithdrawable >= 0){ - account.withdrawInterest(Money.of(account.getDeposit().getCurrency(), command.getWithdrawInterest())); - this.depositAccountRepository.save(account); - }else if(iswithdrawable == -1){ - throw new RuntimeException(); - } - }else if(noOfPeriods <= 0){ - throw new RuntimeException(); - }*/ }else{ throw new DepositAccountTransactionsException("You can not withdraw interst for this account"); } return new EntityIdentifier(account.getId()); } - - private LocalDate getLastTxnDate(DepositAccount account) { - List lastTransactionDates = new ArrayList(); - LocalDate lastTransactionDate=null; - List depositAccountTransactions=account.getDepositaccountTransactions(); - for(DepositAccountTransaction depositAccountTransaction : depositAccountTransactions){ - if(depositAccountTransaction.getTypeOf().equals(DepositAccountTransactionType.WITHDRAW)){ - lastTransactionDate = depositAccountTransaction.getTransactionDate(); - lastTransactionDates.add(lastTransactionDate); - } - } - if(lastTransactionDates.size()>0) - lastTransactionDate = Collections.max(lastTransactionDates); - else - lastTransactionDate = account.getActualCommencementDate(); - return lastTransactionDate; - } private BigDecimal getTotalWithdrawableInterestAvailable(DepositAccount account){ BigDecimal interstGettingForPeriod = BigDecimal.valueOf(account.getAccuredInterest().getAmount().doubleValue()/new Double(account.getTenureInMonths())); @@ -398,4 +349,32 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo Integer noOfPeriods = noOfMonthsforInterestCal / account.getInterestCompoundedEvery(); return interstGettingForPeriod.multiply(new BigDecimal(noOfPeriods)); } + + @Transactional + @Override + public EntityIdentifier renewDepositAccount(DepositAccountCommand command) { + + this.context.authenticatedUser(); + + RenewDepositAccountCommandValidator validator = new RenewDepositAccountCommandValidator(command); + validator.validateForCreate(); + + DepositAccount account = this.depositAccountRepository.findOne(command.getId()); + if (account == null || account.isDeleted()) { + throw new DepositAccountNotFoundException(command.getId()); + } + if(account.isRenewalAllowed()&&(new LocalDate().isAfter(account.maturesOnDate())||new LocalDate().isEqual(account.maturesOnDate()))){ + if(account.getDepositStatus().equals(DepositAccountStatus.ACTIVE.getValue())){ + final DepositAccount renewedAccount = this.depositAccountAssembler.assembleFrom(account,command); + this.depositAccountRepository.save(renewedAccount); + account.closeDepositAccount(defaultDepositLifecycleStateMachine()); + this.depositAccountRepository.save(account); + return new EntityIdentifier(renewedAccount.getId()); + }else{ + throw new DepositAccountReopenException(account.getMaturityDate()); + } + }else{ + throw new DepositAccountReopenException(account.getMaturityDate()); + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/RenewDepositAccountCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/RenewDepositAccountCommandValidator.java new file mode 100644 index 000000000..a806c324b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/service/RenewDepositAccountCommandValidator.java @@ -0,0 +1,43 @@ +package org.mifosng.platform.saving.service; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosng.platform.DataValidatorBuilder; +import org.mifosng.platform.api.commands.DepositAccountCommand; +import org.mifosng.platform.api.data.ApiParameterError; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; + +public class RenewDepositAccountCommandValidator { + + private final DepositAccountCommand command; + + public RenewDepositAccountCommandValidator(final DepositAccountCommand command) { + this.command=command; + } + + public void validateForCreate() { + + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("deposit.account"); + + baseDataValidator.reset().parameter("clientId").value(command.getClientId()).ignoreIfNull(); + baseDataValidator.reset().parameter("productId").value(command.getProductId()).ignoreIfNull(); + baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100); + + baseDataValidator.reset().parameter("deposit").value(command.getDepositAmount()).ignoreIfNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("maturityInterestRate").value(command.getMaturityInterestRate()).ignoreIfNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("preClosureInterestRate").value(command.getPreClosureInterestRate()).ignoreIfNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("tenureInMonths").value(command.getTenureInMonths()).ignoreIfNull().integerGreaterThanZero(); + + baseDataValidator.reset().parameter("interestCompoundedEvery").value(command.getInterestCompoundedEvery()).ignoreIfNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("interestCompoundedEveryPeriodType").value(command.getInterestCompoundedEveryPeriodType()).ignoreIfNull().inMinMaxRange(2, 2); + baseDataValidator.reset().parameter("commencementDate").value(command.getCommencementDate()).ignoreIfNull(); + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); + } + } + +}