From 1957dba4ace4184a068a08646ec72bdf508007cc Mon Sep 17 00:00:00 2001 From: Madhukar Date: Thu, 13 Sep 2012 10:04:46 +0530 Subject: [PATCH 1/2] adding maturity for deposit account --- .../api/DepositAccountsApiResource.java | 3 ++ .../saving/domain/DepositAccount.java | 43 +++++++++++++++++++ .../DepositAccountWritePlatformService.java | 3 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 33 +++++++++++++- 4 files changed, 81 insertions(+), 1 deletion(-) 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 19681440c..b54cf9c63 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 @@ -205,6 +205,9 @@ public class DepositAccountsApiResource { } else if (is(commandParam, "withdrewbyclient")) { EntityIdentifier identifier = this.depositAccountWritePlatformService.withdrawDepositApplication(command); response = Response.ok().entity(identifier).build(); + } else if (is(commandParam, "matureAccount")){ + EntityIdentifier identifier = this.depositAccountWritePlatformService.matureDepositApplication(command); + response = Response.ok().entity(identifier).build(); } UndoStateTransitionCommand undoCommand = new UndoStateTransitionCommand(accountId, command.getNote()); 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 1a2160420..1f834d369 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 @@ -289,6 +289,14 @@ public class DepositAccount extends AbstractAuditableCustom { } return date; } + + public LocalDate getMaturityDate() { + LocalDate date = null; + if (this.maturesOnDate != null) { + date = new LocalDate(this.maturesOnDate); + } + return date; + } public void reject(final LocalDate rejectedOn, final DepositLifecycleStateMachine depositLifecycleStateMachine) { @@ -373,4 +381,39 @@ public class DepositAccount extends AbstractAuditableCustom { public Client client() { return this.client; } + + public void matureDepositApplication(LocalDate maturedOnDate, DepositLifecycleStateMachine depositLifecycleStateMachine) { + + if (maturedOnDate.isAfter(maturesOnDate()) || new LocalDate().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); + } + + } + + if (maturesOnDate().isBefore(new LocalDate())) { + + final String errorMessage = "The date on which a deposit is rejected cannot be before its submittal date: " + + new LocalDate().toString(); + throw new InvalidDepositStateTransitionException("reject", + "cannot.be.before.submittal.date", errorMessage, + maturedOnDate, new LocalDate()); + + } + } + + public LocalDate maturesOnDate() { + LocalDate date = null; + if (this.maturesOnDate != null) { + date = new LocalDate(this.maturesOnDate); + } + return date; + } } \ No newline at end of file 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 3d9a96921..223c227b2 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 @@ -27,4 +27,7 @@ public interface DepositAccountWritePlatformService { @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')") EntityIdentifier undoDepositApproval(UndoStateTransitionCommand undoCommand); + @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')") + EntityIdentifier matureDepositApplication(DepositStateTransitionCommand 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 18c35acc1..98de99761 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 @@ -18,9 +18,9 @@ import org.mifosng.platform.exceptions.PlatformDataIntegrityException; 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.DepositLifecycleStateMachine; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl; -import org.mifosng.platform.saving.domain.DepositAccountStatus; import org.mifosng.platform.saving.domain.FixedTermDepositInterestCalculator; import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.user.domain.AppUser; @@ -234,4 +234,35 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo } return new EntityIdentifier(account.getId()); } + + @Transactional + @Override + public EntityIdentifier matureDepositApplication(DepositStateTransitionCommand command) { + + AppUser currentUser = context.authenticatedUser(); + + DepositStateTransitionCommandValidator validator = new DepositStateTransitionCommandValidator(command); + validator.validate(); + + DepositAccount account = this.depositAccountRepository.findOne(command.getAccountId()); + if (account == null || account.isDeleted()) { + throw new DepositAccountNotFoundException(command.getAccountId()); + } + + LocalDate eventDate = command.getEventDate(); + if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) { + throw new NoAuthorizationException("User has no authority to mature deposit with a date in the past."); + } + + account.matureDepositApplication(eventDate, defaultDepositLifecycleStateMachine()); + this.depositAccountRepository.save(account); + + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.depositNote(account, noteText); + this.noteRepository.save(note); + } + + return new EntityIdentifier(account.getId()); + } } \ No newline at end of file From 41c35afa87e2f3beeea768c402eeaf78587251f8 Mon Sep 17 00:00:00 2001 From: Madhukar Date: Thu, 13 Sep 2012 19:19:49 +0530 Subject: [PATCH 2/2] withdraw_renew_of deposit application on maturity --- .../api/DepositAccountsApiResource.java | 2 +- .../saving/domain/DepositAccount.java | 67 +++++++++++++++++-- .../saving/domain/DepositAccountEvent.java | 2 +- .../DepositLifecycleStateMachineImpl.java | 11 +++ .../service/DepositAccountAssembler.java | 38 +++++++++++ ...WritePlatformServiceJpaRepositoryImpl.java | 6 ++ 6 files changed, 117 insertions(+), 9 deletions(-) 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 b54cf9c63..177195f92 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 @@ -205,7 +205,7 @@ public class DepositAccountsApiResource { } else if (is(commandParam, "withdrewbyclient")) { EntityIdentifier identifier = this.depositAccountWritePlatformService.withdrawDepositApplication(command); response = Response.ok().entity(identifier).build(); - } else if (is(commandParam, "matureAccount")){ + } else if (is(commandParam, "mature")){ EntityIdentifier identifier = this.depositAccountWritePlatformService.matureDepositApplication(command); response = Response.ok().entity(identifier).build(); } 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 1f834d369..943b8db56 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 @@ -12,6 +12,7 @@ import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.OneToOne; import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.Temporal; @@ -135,6 +136,10 @@ public class DepositAccount extends AbstractAuditableCustom { @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "depositAccount", orphanRemoval = true) private final List depositaccountTransactions = new ArrayList(); + + @OneToOne(optional=true, cascade={CascadeType.PERSIST}) + @JoinColumn(name = "renewed_account_id") + private DepositAccount renewdAccount; public DepositAccount openNew( final Client client, final DepositProduct product, @@ -381,10 +386,22 @@ public class DepositAccount extends AbstractAuditableCustom { public Client client() { return this.client; } + + public DepositProduct product(){ + return this.product; + } + + public String getExternalId() { + return this.externalId; + } + + public boolean isRenewalAllowed() { + return renewalAllowed; + } public void matureDepositApplication(LocalDate maturedOnDate, DepositLifecycleStateMachine depositLifecycleStateMachine) { - if (maturedOnDate.isAfter(maturesOnDate()) || new LocalDate().equals(maturesOnDate())) { + if (maturedOnDate.isAfter(maturesOnDate()) || (new LocalDate().equals(maturesOnDate()) && maturedOnDate.equals(maturesOnDate()))) { DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition( DepositAccountEvent.DEPOSIT_MATURED, DepositAccountStatus.fromInt(this.depositStatus)); @@ -394,19 +411,49 @@ public class DepositAccount extends AbstractAuditableCustom { 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 (maturesOnDate().isBefore(new LocalDate())) { - - final String errorMessage = "The date on which a deposit is rejected cannot be before its submittal date: " + 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("reject", - "cannot.be.before.submittal.date", errorMessage, + throw new InvalidDepositStateTransitionException("matured", + "cannot.be.before.mature.date", errorMessage, maturedOnDate, new LocalDate()); } + + 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() { @@ -416,4 +463,10 @@ public class DepositAccount extends AbstractAuditableCustom { } return date; } + + public void updateAccount(DepositAccount account) { + + this.renewdAccount = account; + + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccountEvent.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccountEvent.java index 0fed22102..363c95305 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccountEvent.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccountEvent.java @@ -2,6 +2,6 @@ package org.mifosng.platform.saving.domain; public enum DepositAccountEvent { - DEPOSIT_CREATED, DEPOSIT_REJECTED, DEPOSIT_APPROVED, DEPOSIT_APPROVAL_UNDO, DEPOSIT_WITHDRAWN, DEPOSIT_MATURED; + DEPOSIT_CREATED, DEPOSIT_REJECTED, DEPOSIT_APPROVED, DEPOSIT_APPROVAL_UNDO, DEPOSIT_WITHDRAWN, DEPOSIT_MATURED, DEPOSIT_CLOSED; } 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 e79c5881b..f4e480eae 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 @@ -69,6 +69,17 @@ public class DepositLifecycleStateMachineImpl implements throw new InvalidDepositStateTransitionException("mature", "failed", errorMessage,from); } break; + case DEPOSIT_CLOSED: + if (from.hasStateOf(DepositAccountStatus.MATURED) ){ + newState = stateOf(DepositAccountStatus.CLOSED, allowedDepositStatuses); + } + else{ + final String errorMessage = "The DepositApplication can not be closed from "+from.getCode() ; + throw new InvalidDepositStateTransitionException("closed", "failed", errorMessage,from); + } + break; + default: + break; } return newState; 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 17226c3ab..ed6f0699a 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 @@ -109,4 +109,42 @@ public class DepositAccountAssembler { List allowedDepositStatuses = Arrays.asList(DepositAccountStatus.values()); return new DepositLifecycleStateMachineImpl(allowedDepositStatuses); } + + public DepositAccount assembleFrom(DepositAccount account) { + + Client client = account.client(); + + DepositProduct product = account.product(); + + Money deposit = Money.of(product.getCurrency(), account.getDeposit().getAmount()); + + Integer tenureInMonths = product.getTenureInMonths(); + + BigDecimal maturityInterestRate = product.getMaturityDefaultInterestRate(); + + BigDecimal preClosureInterestRate = product.getPreClosureInterestRate(); + + if(product.getMaturityMinInterestRate().compareTo(preClosureInterestRate)==-1){ + throw new DepositAccounDataValidationtException(preClosureInterestRate, product.getMaturityMinInterestRate()); + } + + Integer compoundingInterestEvery = product.getInterestCompoundedEvery(); + + PeriodFrequencyType compoundingInterestFrequency = product.getInterestCompoundedEveryPeriodType(); + + boolean renewalAllowed = product.isRenewalAllowed(); + + boolean preClosureAllowed = product.isPreClosureAllowed(); + + DepositAccount newAccount =new DepositAccount().openNew(client, product, null, + deposit, + maturityInterestRate, preClosureInterestRate, + tenureInMonths, compoundingInterestEvery, compoundingInterestFrequency, + account.maturesOnDate(), + renewalAllowed, preClosureAllowed, this.fixedTermDepositInterestCalculator, defaultDepositLifecycleStateMachine()); + + newAccount.updateAccount(account); + + return newAccount; + } } \ No newline at end of file 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 98de99761..cc8277a06 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 @@ -257,6 +257,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo account.matureDepositApplication(eventDate, defaultDepositLifecycleStateMachine()); this.depositAccountRepository.save(account); + if(account.isRenewalAllowed()){ + final DepositAccount renewedAccount = this.depositAccountAssembler.assembleFrom(account); + 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);