diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java index afda6a0a4..bef3f6570 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java @@ -113,6 +113,22 @@ public enum SavingsAccountTransactionType { return this.value.equals(SavingsAccountTransactionType.WAIVE_CHARGES.getValue()); } + public boolean isTransferInitiation() { + return this.value.equals(SavingsAccountTransactionType.INITIATE_TRANSFER.getValue()); + } + + public boolean isTransferApproval() { + return this.value.equals(SavingsAccountTransactionType.APPROVE_TRANSFER.getValue()); + } + + public boolean isTransferRejection() { + return this.value.equals(SavingsAccountTransactionType.REJECT_TRANSFER.getValue()); + } + + public boolean isTransferWithdrawal() { + return this.value.equals(SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue()); + } + public boolean isDebit() { return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isCharge(); } 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 d91fed89c..fc3c949ad 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 @@ -79,6 +79,8 @@ import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod; import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException; +import org.mifosplatform.portfolio.savings.exception.SavingsActivityPriorToClientTransferException; +import org.mifosplatform.portfolio.savings.exception.SavingsTransferTransactionsCannotBeUndoneException; import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -605,6 +607,7 @@ public class SavingsAccount extends AbstractPersistable { throw new PlatformApiDataValidationException(dataValidationErrors); } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_DEPOSIT, transactionDTO.getTransactionDate()); transactionDTO.getExistingTransactionIds().addAll(findExistingTransactionIds()); transactionDTO.getExistingReversedTransactionIds().addAll(findExistingReversedTransactionIds()); @@ -679,6 +682,7 @@ public class SavingsAccount extends AbstractPersistable { throw new PlatformApiDataValidationException(dataValidationErrors); } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_WITHDRAWAL, transactionDTO.getTransactionDate()); transactionDTO.getExistingTransactionIds().addAll(findExistingTransactionIds()); transactionDTO.getExistingReversedTransactionIds().addAll(findExistingReversedTransactionIds()); @@ -823,6 +827,7 @@ public class SavingsAccount extends AbstractPersistable { throw new PlatformApiDataValidationException(dataValidationErrors); } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_PAY_FEE, annualFeeTransactionDate); existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); @@ -1416,6 +1421,7 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLICATION_APPROVED, approvedOn); // FIXME - kw - support field officer history for savings accounts // if (this.fieldOfficer != null) { @@ -1476,6 +1482,9 @@ public class SavingsAccount extends AbstractPersistable { // throw non found exception } else { + validateAttemptToUndoTransferRelatedTransactions(transactionToUndo); + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_UNDO_TRANSACTION, + transactionToUndo.transactionLocalDate()); transactionToUndo.reverse(); reversedTransactionIds.add(transactionId); @@ -1558,6 +1567,7 @@ public class SavingsAccount extends AbstractPersistable { actualChanges.put(SavingsApiConstants.closedOnDateParamName, rejectedOnAsString); final LocalDate submittalDate = getSubmittedOnLocalDate(); + if (rejectedOn.isBefore(submittalDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); @@ -1576,6 +1586,7 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLICATION_REJECTED, rejectedOn); return actualChanges; } @@ -1634,6 +1645,7 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLICATION_WITHDRAWAL_BY_CUSTOMER, withdrawnOn); return actualChanges; } @@ -1715,6 +1727,7 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_ACTIVATE, activationDate); // auto enter deposit for minimum required opening balance when // activating account. @@ -1778,6 +1791,7 @@ public class SavingsAccount extends AbstractPersistable { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("results.in.balance.not.zero"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_CLOSE_ACCOUNT, closedDate); this.status = SavingsAccountStatusType.CLOSED.getValue(); actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); actualChanges.put(SavingsApiConstants.localeParamName, command.locale()); @@ -1794,6 +1808,19 @@ public class SavingsAccount extends AbstractPersistable { return actualChanges; } + private void validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent event, LocalDate activityDate) { + if (this.client != null && this.client.getOfficeJoiningLocalDate() != null) { + LocalDate clientOfficeJoiningDate = client.getOfficeJoiningLocalDate(); + if (activityDate.isBefore(clientOfficeJoiningDate)) { throw new SavingsActivityPriorToClientTransferException(event.toString(), + clientOfficeJoiningDate); } + } + } + + private void validateAttemptToUndoTransferRelatedTransactions(SavingsAccountTransaction savingsAccountTransaction) { + if (savingsAccountTransaction.isTransferRelatedTransaction()) { throw new SavingsTransferTransactionsCannotBeUndoneException( + savingsAccountTransaction.getId()); } + } + private Date calculateDateAccountIsLockedUntil(final LocalDate activationLocalDate) { Date lockedInUntilLocalDate = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 0c3f0f219..283d39995 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -232,6 +232,26 @@ public final class SavingsAccountTransaction extends AbstractPersistable { return this.reversed; } + public boolean isTransferInitiation() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isTransferInitiation(); + } + + public boolean isTransferApproval() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isTransferApproval(); + } + + public boolean isTransferRejection() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isTransferRejection(); + } + + public boolean isTransferWithdrawal() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isTransferWithdrawal(); + } + + public boolean isTransferRelatedTransaction() { + return isTransferInitiation() || isTransferApproval() || isTransferRejection() || isTransferWithdrawal(); + } + public boolean occursOn(final LocalDate occursOnDate) { return getTransactionLocalDate().isEqual(occursOnDate); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsEvent.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsEvent.java new file mode 100644 index 000000000..00c938fc5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsEvent.java @@ -0,0 +1,41 @@ +/** + * 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; + +/** + * + */ +public enum SavingsEvent { + + SAVINGS_APPLICATION_REJECTED("application.rejected"), // + SAVINGS_APPLICATION_WITHDRAWAL_BY_CUSTOMER("application.withdrawal"), // + SAVINGS_APPLICATION_APPROVED("application.approval"), // + SAVINGS_APPLICATION_APPROVAL_UNDO("application.approval.undo"), // + SAVINGS_ACTIVATE("activate"), // + SAVINGS_DEPOSIT("deposit"), // + SAVINGS_WITHDRAWAL("withdraw"), // + SAVINGS_POST_INTEREST("interest.post"), // + SAVINGS_UNDO_TRANSACTION("transaction.undo"), // + SAVINGS_ADJUST_TRANSACTION("transaction.adjust"), // + SAVINGS_APPLY_FEE("fee.apply"), // + SAVINGS_PAY_FEE("fee.pay"), // + SAVINGS_CLOSE_ACCOUNT("account.close"); + + private final String value; + + private SavingsEvent(final String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + + @Override + public String toString() { + return getValue(); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsActivityPriorToClientTransferException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsActivityPriorToClientTransferException.java new file mode 100644 index 000000000..9eabc50e5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsActivityPriorToClientTransferException.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.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * {@link AbstractPlatformDomainRuleException} thrown an action to transition a + * loan from one state to another violates a domain rule. + */ +public class SavingsActivityPriorToClientTransferException extends AbstractPlatformDomainRuleException { + + public SavingsActivityPriorToClientTransferException(final String action, Object... defaultUserMessageArgs) { + super("error.msg.savings." + action + "." + "not.permitted.before.client.transfer.date", + "Transactions on savings account prior to the customer joining date are not permitted", defaultUserMessageArgs); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsTransferTransactionsCannotBeUndoneException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsTransferTransactionsCannotBeUndoneException.java new file mode 100644 index 000000000..c29a35fb9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsTransferTransactionsCannotBeUndoneException.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.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * {@link AbstractPlatformDomainRuleException} thrown an action to transition a + * loan from one state to another violates a domain rule. + */ +public class SavingsTransferTransactionsCannotBeUndoneException extends AbstractPlatformDomainRuleException { + + public SavingsTransferTransactionsCannotBeUndoneException(Long transactionId) { + super("error.msg.savings.transfer.transactions.cannot.be.undone", + "Transactions related to savings account transfers cannot be undone", transactionId); + } + +} \ No newline at end of file