From 2ab7097b85219a5dbd791493cd5b3b8200a11a8c Mon Sep 17 00:00:00 2001 From: Madhukar Date: Wed, 19 Dec 2012 16:20:32 +0530 Subject: [PATCH] Change declarations for enums and spell checks Adding Saving account & product creation from UI Add state transitions for Saving accounts Approve saving account state patch partially done Approval of saving account Soft delete of saving account Add intereset calculation method for saving account Interest Posting functionality not completed yet Saving Interest Posting without interest calculation method --- .../bk_mifostenant_default.sql | 2 +- .../bk_mifostenant_default_new_demo.sql | 2 +- ..._deposit_freq_col_for_m_saving_account.sql | 2 + ...patch-17_add_note_for_m_saving_account.sql | 4 + ...reate_table_saving_account_transaction.sql | 21 + ...ing,i_p_frequency_for_m_saving_account.sql | 3 + ...rest_posting_cols_for_m_saving_account.sql | 4 + ...GsonPortfolioApiJsonSerializerService.java | 5 +- .../PortfolioApiDataConversionService.java | 12 + ...PortfolioApiDataConversionServiceImpl.java | 108 ++++- .../portfolio/client/domain/Note.java | 21 +- .../client/domain/NoteEnumerations.java | 4 + .../api/SavingsAccountApiResource.java | 113 ++++- .../CalculateSavingScheduleCommand.java | 23 +- .../command/SavingAccountApprovalCommand.java | 104 +++++ ...SavingAccountApprovalCommandValidator.java | 38 ++ .../command/SavingAccountCommand.java | 44 +- .../SavingAccountCommandValidator.java | 2 +- .../command/SavingAccountDepositCommand.java | 37 ++ .../SavingAccountDepositCommandValidator.java | 31 ++ ...ccountStateTransitionCommandValidator.java | 30 ++ .../SavingAccountWithdrawalCommand.java | 36 ++ .../SavingStateTransitionsCommand.java | 28 ++ .../data/SavingAccountData.java | 337 ++++++++++++++- .../data/SavingAccountForLookup.java | 15 + .../data/SavingPermissionData.java | 57 +++ .../ReccuringDepositInterestCalculator.java | 132 +++--- .../savingsaccount/domain/SavingAccount.java | 408 ++++++++++++++++-- .../domain/SavingAccountTransaction.java | 89 ++++ .../domain/SavingScheduleInstallments.java | 41 +- .../service/CalculateSavingScheduleImpl.java | 2 +- .../service/SavingAccountAssembler.java | 127 +++++- .../SavingAccountReadPlatformService.java | 13 + .../SavingAccountReadPlatformServiceImpl.java | 108 ++++- .../SavingAccountWritePlatformService.java | 32 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 191 +++++++- .../api/SavingProductsApiResource.java | 2 +- .../data/SavingProductData.java | 2 +- .../SavingInterestCalculationMethod.java | 8 + .../domain/SavingProductRelatedDetail.java | 48 +-- .../domain/SavingProductType.java | 6 +- .../service/SavingProductEnumerations.java | 8 +- ...WritePlatformServiceJpaRepositoryImpl.java | 13 +- .../domain/DepositAccountTransactionType.java | 8 + 44 files changed, 2121 insertions(+), 200 deletions(-) create mode 100644 mifosng-db/patches/patch-10_add_deposit_freq_col_for_m_saving_account.sql create mode 100644 mifosng-db/patches/patch-17_add_note_for_m_saving_account.sql create mode 100644 mifosng-db/patches/patch-18_create_table_saving_account_transaction.sql create mode 100644 mifosng-db/patches/patch-19_add_interest_posting,i_p_frequency_for_m_saving_account.sql create mode 100644 mifosng-db/patches/patch-20_add_interest_posting_cols_for_m_saving_account.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommand.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommandValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommand.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommandValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountStateTransitionCommandValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountWithdrawalCommand.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingStateTransitionsCommand.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountForLookup.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingPermissionData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccountTransaction.java diff --git a/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default.sql b/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default.sql index 00f4e42e2..0bc985944 100644 --- a/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default.sql +++ b/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default.sql @@ -1507,7 +1507,7 @@ CREATE TABLE `m_saving_account` ( `lastmodified_date` datetime DEFAULT NULL, `lastmodifiedby_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `deposit_acc_external_id` (`external_id`), + UNIQUE KEY `saving_acc_external_id` (`external_id`), KEY `FKSA0000000000001` (`client_id`), KEY `FKSA0000000000002` (`product_id`), CONSTRAINT `FKSA0000000000001` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`), diff --git a/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default_new_demo.sql b/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default_new_demo.sql index a8192eba5..8dd4aa079 100644 --- a/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default_new_demo.sql +++ b/mifosng-db/multi-tenant-demo-backups/bk_mifostenant_default_new_demo.sql @@ -1320,7 +1320,7 @@ CREATE TABLE `m_saving_account` ( `lastmodified_date` datetime DEFAULT NULL, `lastmodifiedby_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `deposit_acc_external_id` (`external_id`), + UNIQUE KEY `saving_acc_external_id` (`external_id`), KEY `FKSA0000000000001` (`client_id`), KEY `FKSA0000000000002` (`product_id`), CONSTRAINT `FKSA0000000000001` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`), diff --git a/mifosng-db/patches/patch-10_add_deposit_freq_col_for_m_saving_account.sql b/mifosng-db/patches/patch-10_add_deposit_freq_col_for_m_saving_account.sql new file mode 100644 index 000000000..b9dfbb2d8 --- /dev/null +++ b/mifosng-db/patches/patch-10_add_deposit_freq_col_for_m_saving_account.sql @@ -0,0 +1,2 @@ +ALTER TABLE m_saving_account + ADD COLUMN `deposit_every` bigint(20) DEFAULT NULL AFTER `tenure_type`; \ No newline at end of file diff --git a/mifosng-db/patches/patch-17_add_note_for_m_saving_account.sql b/mifosng-db/patches/patch-17_add_note_for_m_saving_account.sql new file mode 100644 index 000000000..5b422803f --- /dev/null +++ b/mifosng-db/patches/patch-17_add_note_for_m_saving_account.sql @@ -0,0 +1,4 @@ +ALTER TABLE m_note + ADD COLUMN `saving_account_id` bigint(20) DEFAULT NULL AFTER `deposit_account_id`, + ADD KEY `FK_m_note_m_saving_account` (`saving_account_id`), + ADD CONSTRAINT `FK_m_note_m_saving_account` FOREIGN KEY (`saving_account_id`) REFERENCES `m_saving_account` (`id`); \ No newline at end of file diff --git a/mifosng-db/patches/patch-18_create_table_saving_account_transaction.sql b/mifosng-db/patches/patch-18_create_table_saving_account_transaction.sql new file mode 100644 index 000000000..9e10a7a47 --- /dev/null +++ b/mifosng-db/patches/patch-18_create_table_saving_account_transaction.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS `m_saving_account_transaction`; +CREATE TABLE `m_saving_account_transaction` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `saving_account_id` bigint(20) NOT NULL, + `transaction_type_enum` smallint(5) NOT NULL, + `contra_id` bigint(20) DEFAULT NULL, + `transaction_date` date NOT NULL, + `amount` decimal(19,6) NOT NULL, + `createdby_id` bigint(20) DEFAULT NULL, + `created_date` datetime DEFAULT NULL, + `lastmodified_date` datetime DEFAULT NULL, + `lastmodifiedby_id` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKSAT0000000001` (`saving_account_id`), + KEY `FKSAT0000000002` (`contra_id`), + CONSTRAINT `FKSAT0000000001` FOREIGN KEY (`saving_account_id`) REFERENCES `m_saving_account` (`id`), + CONSTRAINT `FKSAT0000000002` FOREIGN KEY (`contra_id`) REFERENCES `m_saving_account_transaction` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `m_saving_account` + ADD COLUMN `outstanding_amount` decimal(19,6) NOT NULL AFTER `pre_closure_interest_rate`; diff --git a/mifosng-db/patches/patch-19_add_interest_posting,i_p_frequency_for_m_saving_account.sql b/mifosng-db/patches/patch-19_add_interest_posting,i_p_frequency_for_m_saving_account.sql new file mode 100644 index 000000000..89a97af27 --- /dev/null +++ b/mifosng-db/patches/patch-19_add_interest_posting,i_p_frequency_for_m_saving_account.sql @@ -0,0 +1,3 @@ +Alter TABLE `m_saving_account` + add column `interest_posting_every` int(11) DEFAULT NULL AFTER `frequency`, + add column `interest_posting_frequency` int(11) DEFAULT NULL AFTER `interest_posting_every`; \ No newline at end of file diff --git a/mifosng-db/patches/patch-20_add_interest_posting_cols_for_m_saving_account.sql b/mifosng-db/patches/patch-20_add_interest_posting_cols_for_m_saving_account.sql new file mode 100644 index 000000000..06f459be4 --- /dev/null +++ b/mifosng-db/patches/patch-20_add_interest_posting_cols_for_m_saving_account.sql @@ -0,0 +1,4 @@ +ALTER TABLE `m_saving_account` + ADD COLUMN `interest_posted_amount` decimal(19,6) DEFAULT '0.000000' AFTER `outstanding_amount`, + ADD COLUMN `last_interest_posted_date` date DEFAULT NULL AFTER `interest_posted_amount`, + ADD COLUMN `next_interest_posting_date` date DEFAULT NULL AFTER `last_interest_posted_date`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java index 9ae82027f..ba5b6621a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java @@ -51,10 +51,11 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi "clientId", "clientName", "productId", "productName", "productType", "currencyData", "savingsDepostiAmountPerPeriod", "savingsFrequencyType", "totalDepositAmount", "reccuringInterestRate", "savingInterestRate", "interestType", "interestCalculationMethod", "tenure", "tenureType", "projectedCommencementDate", "actualCommencementDate", "maturesOnDate", - "projectedInterestAccuredOnMaturity", "actualInterestAccured", "projectedMaturityAmount", "actualMaturityAmount", + "projectedInterestAccuredOnMaturity", "actualInterestAccured", "projectedMaturityAmount", "actualMaturityAmount","outstandingAmount", "preClosureAllowed", "preClosureInterestRate", "withdrawnonDate", "rejectedonDate", "closedonDate", "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType", "productOptions", "currencyOptions", "savingsProductTypeOptions", "tenureTypeOptions", - "savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions")); + "savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions","permissions","savingScheduleDatas", + "dueAmount", "savingScheduleData")); // guarantors private static final Set GUARANTOR_DATA_PARAMETERS = new HashSet(Arrays.asList("externalGuarantor", "existingClientId", diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java index 9a0ff591c..4407f9bed 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java @@ -3,7 +3,11 @@ package org.mifosplatform.infrastructure.core.api; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.group.command.GroupCommand; import org.mifosplatform.portfolio.loanaccount.gaurantor.command.GuarantorCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountApprovalCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountWithdrawalCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingStateTransitionsCommand; import org.mifosplatform.portfolio.savingsaccountproduct.command.SavingProductCommand; import org.mifosplatform.portfolio.savingsdepositaccount.command.DepositAccountCommand; import org.mifosplatform.portfolio.savingsdepositaccount.command.DepositAccountWithdrawInterestCommand; @@ -35,4 +39,12 @@ public interface PortfolioApiDataConversionService { SavingAccountCommand convertJsonToSavingAccountCommand(Long resourceIdentifier, String json); GuarantorCommand convertJsonToGuarantorCommand(Long resourceIdentifier, Long loanId, String json); + + SavingStateTransitionsCommand convertJsonToSavingStateTransitionCommand(Long accountId, String json); + + SavingAccountApprovalCommand convertJsonToSavingApprovalCommand(Long accountId, String json); + + SavingAccountDepositCommand convertJsonToSavingAccountDepositCommand(Long accountId, String json); + + SavingAccountWithdrawalCommand convertJsonToSavingAccountWithdrawalCommand(Long accountId, String jsonRequestBody); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java index 38ef4331c..c5bf97e6d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java @@ -26,7 +26,11 @@ import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.serialization.ClientCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.group.command.GroupCommand; import org.mifosplatform.portfolio.loanaccount.gaurantor.command.GuarantorCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountApprovalCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountWithdrawalCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingStateTransitionsCommand; import org.mifosplatform.portfolio.savingsaccountproduct.command.SavingProductCommand; import org.mifosplatform.portfolio.savingsdepositaccount.command.DepositAccountCommand; import org.mifosplatform.portfolio.savingsdepositaccount.command.DepositAccountWithdrawInterestCommand; @@ -813,8 +817,8 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo Set supportedParams = new HashSet(Arrays.asList("clientId", "productId", "externalId", "currencyCode", "digitsAfterDecimal", "savingsDepositAmountPerPeriod", "recurringInterestRate", "savingInterestRate", "tenure", "commencementDate", "locale", "dateFormat", "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType", - "savingProductType", "tenureType", "frequency", "interestType", "interestCalculationMethod", "minimumBalanceForWithdrawal", - "isPartialDepositAllowed", "payEvery")); + "savingProductType", "tenureType", "depositfrequency", "interestType", "interestCalculationMethod", "minimumBalanceForWithdrawal", + "isPartialDepositAllowed", "depositEvery","interestPostEvery","interestPostFrequency")); checkForUnsupportedParameters(requestMap, supportedParams); Set modifiedParameters = new HashSet(); @@ -835,17 +839,20 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo LocalDate commencementDate = extractLocalDateParameter("commencementDate", requestMap, modifiedParameters); Integer savingProductType = extractIntegerParameter("savingProductType", requestMap, modifiedParameters); Integer tenureType = extractIntegerParameter("tenureType", requestMap, modifiedParameters); - Integer frequency = extractIntegerParameter("frequency", requestMap, modifiedParameters); + Integer depositfrequency = extractIntegerParameter("depositfrequency", requestMap, modifiedParameters); Integer interestType = extractIntegerParameter("interestType", requestMap, modifiedParameters); Integer interestCalculationMethod = extractIntegerParameter("interestCalculationMethod", requestMap, modifiedParameters); BigDecimal minimumBalanceForWithdrawal = extractBigDecimalParameter("minimumBalanceForWithdrawal", requestMap, modifiedParameters); boolean isPartialDepositAllowed = extractBooleanParameter("isPartialDepositAllowed", requestMap, modifiedParameters); - Integer payEvery = extractIntegerParameter("payEvery", requestMap, modifiedParameters); + Integer depositEvery = extractIntegerParameter("depositEvery", requestMap, modifiedParameters); + + Integer interestPostEvery = extractIntegerParameter("interestPostEvery", requestMap, modifiedParameters); + Integer interestPostFrequency = extractIntegerParameter("interestPostFrequency", requestMap, modifiedParameters); return new SavingAccountCommand(modifiedParameters, resourceIdentifier, clientId, productId, externalId, currencyCode, digitsAfterDecimalValue, savingsDepositAmount, recurringInterestRate, savingInterestRate, tenure, commencementDate, - savingProductType, tenureType, frequency, interestType, minimumBalanceForWithdrawal, interestCalculationMethod, - isLockinPeriodAllowed, isPartialDepositAllowed, lockinPeriod, lockinPeriodType, payEvery); + savingProductType, tenureType, depositfrequency, interestType, minimumBalanceForWithdrawal, interestCalculationMethod, + isLockinPeriodAllowed, isPartialDepositAllowed, lockinPeriod, lockinPeriodType, depositEvery, interestPostEvery, interestPostFrequency); } @Override @@ -889,4 +896,93 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo command.setLocale(locale); return command; } + + @Override + public SavingStateTransitionsCommand convertJsonToSavingStateTransitionCommand( Long accountId, String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + Type typeOfMap = new TypeToken>() {}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + Set supportedParams = new HashSet(Arrays.asList("eventDate", "locale", "dateFormat", "note")); + + checkForUnsupportedParameters(requestMap, supportedParams); + + Set modifiedParameters = new HashSet(); + + LocalDate eventDate = extractLocalDateParameter("eventDate", requestMap, modifiedParameters); + String note = extractStringParameter("note", requestMap, modifiedParameters); + + return new SavingStateTransitionsCommand(accountId, eventDate, note); + + } + + @Override + public SavingAccountApprovalCommand convertJsonToSavingApprovalCommand( Long accountId, String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + Type typeOfMap = new TypeToken>() {}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + Set supportedParams = new HashSet(Arrays.asList("locale", "dateFormat", "commencementDate", "savingsDepositAmountPerPeriod", + "minimumBalanceForWithdrawal", "recurringInterestRate", "savingInterestRate", "interestType", "tenure", "tenureType", "frequency", "payEvery", "note", + "interestPostEvery", "interestPostFrequency")); + + checkForUnsupportedParameters(requestMap, supportedParams); + Set modifiedParameters = new HashSet(); + + LocalDate commencementDate = extractLocalDateParameter("commencementDate", requestMap, modifiedParameters); + BigDecimal savingsDepositAmountPerPeriod = extractBigDecimalParameter("savingsDepositAmountPerPeriod", requestMap, modifiedParameters); + BigDecimal minimumBalanceForWithdrawal = extractBigDecimalParameter("minimumBalanceForWithdrawal", requestMap, modifiedParameters); + BigDecimal recurringInterestRate = extractBigDecimalParameter("recurringInterestRate", requestMap, modifiedParameters); + BigDecimal savingInterestRate = extractBigDecimalParameter("savingInterestRate", requestMap, modifiedParameters); + Integer interestType = extractIntegerParameter("interestType", requestMap, modifiedParameters); + Integer tenure = extractIntegerParameter("tenure", requestMap, modifiedParameters); + Integer tenureType = extractIntegerParameter("tenureType", requestMap, modifiedParameters); + Integer frequency = extractIntegerParameter("frequency", requestMap, modifiedParameters); + Integer payEvery = extractIntegerParameter("payEvery", requestMap, modifiedParameters); + String note = extractStringParameter("note", requestMap, modifiedParameters); + Integer interestPostEvery = extractIntegerParameter("interestPostEvery", requestMap, modifiedParameters); + Integer interestPostFrequency = extractIntegerParameter("interestPostFrequency", requestMap, modifiedParameters); + return new SavingAccountApprovalCommand(accountId, commencementDate, savingsDepositAmountPerPeriod, minimumBalanceForWithdrawal, recurringInterestRate, + savingInterestRate, interestType, tenure, tenureType, frequency, payEvery, note, interestPostEvery, interestPostFrequency); + } + + @Override + public SavingAccountDepositCommand convertJsonToSavingAccountDepositCommand(Long accountId, String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + Type typeOfMap = new TypeToken>() {}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + Set supportedParams = new HashSet(Arrays.asList("locale", "dateFormat", "depositDate", "savingsDepositAmountPerPeriod", "note")); + + checkForUnsupportedParameters(requestMap, supportedParams); + Set modifiedParameters = new HashSet(); + + LocalDate depositDate = extractLocalDateParameter("depositDate", requestMap, modifiedParameters); + BigDecimal savingsDepositAmountPerPeriod = extractBigDecimalParameter("savingsDepositAmountPerPeriod", requestMap, modifiedParameters); + String note = extractStringParameter("note", requestMap, modifiedParameters); + return new SavingAccountDepositCommand(accountId, savingsDepositAmountPerPeriod, depositDate, note); + } + + @Override + public SavingAccountWithdrawalCommand convertJsonToSavingAccountWithdrawalCommand(Long accountId, String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + Type typeOfMap = new TypeToken>() {}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + Set supportedParams = new HashSet(Arrays.asList("locale", "dateFormat", "transactionDate", "amount", "note")); + + checkForUnsupportedParameters(requestMap, supportedParams); + Set modifiedParameters = new HashSet(); + + LocalDate transactionDate = extractLocalDateParameter("transactionDate", requestMap, modifiedParameters); + BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters); + String note = extractStringParameter("note", requestMap, modifiedParameters); + return new SavingAccountWithdrawalCommand(accountId, transactionDate, amount, note); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Note.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Note.java index ab0ae9544..db78236d1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Note.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Note.java @@ -14,6 +14,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.savingsaccount.domain.SavingAccount; import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositAccount; import org.mifosplatform.useradministration.domain.AppUser; @@ -46,10 +47,15 @@ public class Note extends AbstractAuditableCustom { @ManyToOne @JoinColumn(name = "deposit_account_id", nullable = true) private DepositAccount depositAccount; + + @SuppressWarnings("unused") + @ManyToOne + @JoinColumn(name = "saving_account_id", nullable = true) + private SavingAccount savingAccount; public enum NoteType { CLIENT(100, "noteType.client"), LOAN(200, "noteType.loan"), LOAN_TRANSACTION(300, "noteType.loan.transaction"), DEPOSIT(400, - "noteType.deposit"); + "noteType.deposit"), SAVING(500,"noteType.saving"); private Integer value; private String code; @@ -132,7 +138,14 @@ public class Note extends AbstractAuditableCustom { this.noteTypeId = NoteType.DEPOSIT.getValue(); } - public Map update(final JsonCommand command) { + public Note(SavingAccount account, String note) { + this.savingAccount = account; + this.client = account.getClient(); + this.note = note; + this.noteTypeId = NoteType.SAVING.getValue(); + } + + public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap(7); final String noteParamName = "note"; @@ -147,4 +160,8 @@ public class Note extends AbstractAuditableCustom { public boolean isNotAgainstClientWithIdOf(Long clientId) { return !this.client.identifiedBy(clientId); } + + public static Note savingNote(final SavingAccount account, final String noteText) { + return new Note(account, noteText); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/NoteEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/NoteEnumerations.java index 8fff84b5e..7f37d431d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/NoteEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/NoteEnumerations.java @@ -24,8 +24,12 @@ public class NoteEnumerations { case DEPOSIT: optionData = new EnumOptionData(type.getValue().longValue(), type.getCode(), "Deposit transaction note"); break; + case SAVING: + optionData = new EnumOptionData(type.getValue().longValue(), type.getCode(), "Saving transaction note"); + break; default: break; + } return optionData; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/api/SavingsAccountApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/api/SavingsAccountApiResource.java index 853569995..1ad97cd33 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/api/SavingsAccountApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/api/SavingsAccountApiResource.java @@ -1,5 +1,6 @@ package org.mifosplatform.portfolio.savingsaccount.api; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -7,6 +8,7 @@ import java.util.List; import java.util.Set; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -25,13 +27,22 @@ import org.mifosplatform.infrastructure.core.api.PortfolioApiDataConversionServi import org.mifosplatform.infrastructure.core.api.PortfolioApiJsonSerializerService; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService; +import org.mifosplatform.portfolio.loanaccount.command.UndoStateTransitionCommand; import org.mifosplatform.portfolio.savingsaccount.command.CalculateSavingScheduleCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountApprovalCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountWithdrawalCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingStateTransitionsCommand; import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData; +import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup; +import org.mifosplatform.portfolio.savingsaccount.data.SavingPermissionData; import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData; import org.mifosplatform.portfolio.savingsaccount.service.CalculateSavingSchedule; import org.mifosplatform.portfolio.savingsaccount.service.SavingAccountReadPlatformService; @@ -82,6 +93,9 @@ public class SavingsAccountApiResource { @Autowired private ToApiJsonSerializer toApiJsonSerializer; + + @Autowired + private FromJsonHelper fromJsonHelper; private static final Set typicalResponseParameters = new HashSet(Arrays.asList("id", "status", "externalId", "clientId", "clientName", "productId", "productName", "productType", "currencyData", "savingsDepostiAmountPerPeriod", @@ -89,7 +103,7 @@ public class SavingsAccountApiResource { "interestCalculationMethod", "tenure", "tenureType", "projectedCommencementDate", "actualCommencementDate", "maturesOnDate", "projectedInterestAccuredOnMaturity", "actualInterestAccured", "projectedMaturityAmount", "actualMaturityAmount", "preClosureAllowed", "preClosureInterestRate", "withdrawnonDate", "rejectedonDate", "closedonDate", "isLockinPeriodAllowed", - "lockinPeriod", "lockinPeriodType")); + "lockinPeriod", "lockinPeriodType","outstandingAmount","savingScheduleData")); @POST @Consumes({ MediaType.APPLICATION_JSON }) @@ -156,18 +170,42 @@ public class SavingsAccountApiResource { public String retrieveDepositAccount(@PathParam("accountId") final Long accountId, @Context final UriInfo uriInfo) { context.authenticatedUser().validateHasReadPermission(entityType); + + SavingPermissionData permissions = null; + SavingScheduleData savingScheduleData = null; Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); - if (responseParameters.isEmpty()) { responseParameters.addAll(typicalResponseParameters); } - boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); SavingAccountData account = this.savingAccountReadPlatformService.retrieveSavingsAccount(accountId); + boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); + if (template) { + account = handleTemplateRelatedData(responseParameters, account); + } + + Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + if (!associationParameters.isEmpty()) { + if (associationParameters.contains("all")) { + associationParameters.add("savingScheduleData"); + responseParameters.addAll(Arrays.asList("permissions","savingScheduleData")); + } else { + responseParameters.addAll(associationParameters); + } + + if (associationParameters.contains("savingScheduleData")) { + CurrencyData currencyData = account.getCurrencyData(); + savingScheduleData = this.savingAccountReadPlatformService.retrieveSavingsAccountSchedule(accountId,currencyData); + account = new SavingAccountData(account,savingScheduleData); + } + permissions = this.savingAccountReadPlatformService.retrieveSavingAccountPermissions(account); + account = new SavingAccountData(account, permissions); + } + return this.apiJsonSerializerService.serializeSavingAccountsDataToJson(prettyPrint, responseParameters, account); } @@ -196,14 +234,14 @@ public class SavingsAccountApiResource { private SavingAccountData handleTemplateRelatedData(Set responseParameters, SavingAccountData account) { responseParameters.addAll(Arrays.asList("productOptions", "currencyOptions", "savingsProductTypeOptions", "tenureTypeOptions", - "savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions")); + "savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions", "dueAmount")); Collection productOptions = savingProductReadPlatformService.retrieveAllSavingProductsForLookup(); List currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); - EnumOptionData reccuring = SavingProductEnumerations.savingProductType(SavingProductType.RECCURING); + EnumOptionData recurring = SavingProductEnumerations.savingProductType(SavingProductType.RECURRING); EnumOptionData regular = SavingProductEnumerations.savingProductType(SavingProductType.REGULAR); - List savingsProductTypeOptions = Arrays.asList(reccuring, regular); + List savingsProductTypeOptions = Arrays.asList(recurring, regular); EnumOptionData fixed = SavingProductEnumerations.tenureTypeEnum(TenureTypeEnum.FIXED_PERIOD); EnumOptionData perpetual = SavingProductEnumerations.tenureTypeEnum(TenureTypeEnum.PERPETUAL); @@ -224,12 +262,73 @@ public class SavingsAccountApiResource { EnumOptionData monthlyCollection = SavingProductEnumerations .savingInterestCalculationMethod(SavingInterestCalculationMethod.MONTHLYCOLLECTION); List interestCalculationOptions = Arrays.asList(averagebal, minbal, monthlyCollection); + BigDecimal dueAmount = this.savingAccountReadPlatformService.deriveSavingDueAmount(account); return new SavingAccountData(account, productOptions, currencyOptions, savingsProductTypeOptions,tenureTypeOptions,savingFrequencyOptions, - savingsInterestTypeOptions,lockinPeriodTypeOptions,interestCalculationOptions); + savingsInterestTypeOptions,lockinPeriodTypeOptions,interestCalculationOptions, dueAmount); } private boolean is(final String commandParam, final String commandValue) { return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); } + + @POST + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response savingStateTransitions(@PathParam("accountId") final Long accountId, @QueryParam("command") final String commandParam, final String jsonRequestBody) { + Response response = null; + if (is(commandParam, "approve")) { + SavingAccountApprovalCommand command = this.apiDataConversionService.convertJsonToSavingApprovalCommand(accountId, jsonRequestBody); + CommandProcessingResult identifier = this.savingAccountWritePlatformService.approveSavingAccount(command); + response = Response.ok().entity(identifier).build(); + } else if (is(commandParam, "depositmoney")) { + SavingAccountDepositCommand command = this.apiDataConversionService.convertJsonToSavingAccountDepositCommand(accountId, jsonRequestBody); + CommandProcessingResult identifier = this.savingAccountWritePlatformService.depositMoney(command); + response = Response.ok().entity(identifier).build(); + } else if (is(commandParam, "withdraw")) { + SavingAccountWithdrawalCommand command = this.apiDataConversionService.convertJsonToSavingAccountWithdrawalCommand(accountId, jsonRequestBody); + CommandProcessingResult identifier = this.savingAccountWritePlatformService.withdrawSavingAmount(command); + response = Response.ok().entity(identifier).build(); + } else { + SavingStateTransitionsCommand command = this.apiDataConversionService.convertJsonToSavingStateTransitionCommand(accountId, jsonRequestBody); + if (is(commandParam, "reject")) { + CommandProcessingResult identifier = this.savingAccountWritePlatformService.rejectSavingApplication(command); + response = Response.ok().entity(identifier).build(); + } else if (is(commandParam, "withdrewbyclient")) { + CommandProcessingResult identifier = this.savingAccountWritePlatformService.withdrawSavingApplication(command); + response = Response.ok().entity(identifier).build(); + } + UndoStateTransitionCommand undoCommand = new UndoStateTransitionCommand(accountId, command.getNote()); + if (is(commandParam, "undoapproval")) { + CommandProcessingResult identifier = this.savingAccountWritePlatformService.undoSavingAccountApproval(undoCommand); + response = Response.ok().entity(identifier).build(); + } + } + if (response == null) { throw new UnrecognizedQueryParamException("command", commandParam); } + return response; + } + + @DELETE + @Path("{accountId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response deleteSavingAccount(@PathParam("accountId") final Long accountId) { + + this.savingAccountWritePlatformService.deleteSavingAccount(accountId); + + return Response.ok(new CommandProcessingResult(accountId)).build(); + } + + @POST + @Path("postinterest") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response postInterest() { + + Collection savingAccounts = this.savingAccountReadPlatformService.retrieveSavingAccountsForLookUp(); + CommandProcessingResult entityIdentifier = this.savingAccountWritePlatformService.postInterest(savingAccounts); + return Response.ok().entity(entityIdentifier).build(); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/CalculateSavingScheduleCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/CalculateSavingScheduleCommand.java index 82975bfcf..67b36dcdb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/CalculateSavingScheduleCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/CalculateSavingScheduleCommand.java @@ -8,23 +8,27 @@ public class CalculateSavingScheduleCommand { private final Long productId; private final BigDecimal deposit; - private final Integer payEvery; + private final Integer depositEvery; private final Integer paymentFrequencyType; private final BigDecimal interestRate; private final LocalDate paymentsStartingFromDate; private final Integer tenure; + private final Integer interestPostEvery; + private final Integer interestPostFrequency; - public CalculateSavingScheduleCommand(final Long productId, final BigDecimal deposit, final Integer payEvery, + public CalculateSavingScheduleCommand(final Long productId, final BigDecimal deposit, final Integer depositEvery, final Integer paymentFrequencyType, final BigDecimal interestRate, final LocalDate paymentsStartingFromDate, - final Integer tenure) { + final Integer tenure,final Integer interestPostEvery, final Integer interestPostFrequency) { this.productId = productId; this.deposit = deposit; - this.payEvery = payEvery; + this.depositEvery = depositEvery; this.paymentFrequencyType = paymentFrequencyType; this.interestRate = interestRate; this.paymentsStartingFromDate = paymentsStartingFromDate; this.tenure = tenure; + this.interestPostEvery = interestPostEvery; + this.interestPostFrequency = interestPostFrequency; } @@ -36,8 +40,8 @@ public class CalculateSavingScheduleCommand { return this.deposit; } - public Integer getPayEvery() { - return this.payEvery; + public Integer getDepositEvery() { + return this.depositEvery; } public Integer getPaymentFrequencyType() { @@ -56,5 +60,12 @@ public class CalculateSavingScheduleCommand { return this.tenure; } + public Integer getInterestPostEvery() { + return this.interestPostEvery; + } + + public Integer getInterestPostFrequency() { + return this.interestPostFrequency; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommand.java new file mode 100644 index 000000000..4fa1b4b17 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommand.java @@ -0,0 +1,104 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; + +public class SavingAccountApprovalCommand { + + private final Long accountId; + private final LocalDate approvalDate; + private final BigDecimal depositAmountPerPeriod; + private final BigDecimal minimumBalanceForWithdrawal; + private final BigDecimal recurringInterestRate; + private final BigDecimal savingInterestRate; + private final Integer interestType; + private final Integer tenure; + private final Integer tenureType; + private final Integer depositFrequencyType; + private final Integer depositEvery; + + private final Integer interestPostEvery; + private final Integer interestPostFrequency; + + private final String note; + + public SavingAccountApprovalCommand(final Long accountId, final LocalDate approvalDate, + final BigDecimal depositAmountPerPeriod, final BigDecimal minimumBalanceForWithdrawal, final BigDecimal recurringInterestRate, + final BigDecimal savingInterestRate, final Integer interestType, final Integer tenure, final Integer tenureType, + final Integer depositFrequencyType, final Integer depositEvery, final String note, + final Integer interestPostEvery, final Integer interestPostFrequency) { + this.accountId = accountId; + this.approvalDate = approvalDate; + this.depositAmountPerPeriod = depositAmountPerPeriod; + this.minimumBalanceForWithdrawal = minimumBalanceForWithdrawal; + this.recurringInterestRate = recurringInterestRate; + this.savingInterestRate = savingInterestRate; + this.interestType = interestType; + this.tenure = tenure; + this.tenureType = tenureType; + this.depositFrequencyType = depositFrequencyType; + this.depositEvery = depositEvery; + this.note = note; + + this.interestPostEvery = interestPostEvery; + this.interestPostFrequency = interestPostFrequency; + } + + public Long getAccountId() { + return this.accountId; + } + + public LocalDate getApprovalDate() { + return this.approvalDate; + } + + public BigDecimal getDepositAmountPerPeriod() { + return this.depositAmountPerPeriod; + } + + public BigDecimal getMinimumBalanceForWithdrawal() { + return this.minimumBalanceForWithdrawal; + } + + public BigDecimal getRecurringInterestRate() { + return this.recurringInterestRate; + } + + public BigDecimal getSavingInterestRate() { + return this.savingInterestRate; + } + + public Integer getInterestType() { + return this.interestType; + } + + public Integer getTenure() { + return this.tenure; + } + + public Integer getTenureType() { + return this.tenureType; + } + + public Integer getDepositFrequencyType() { + return this.depositFrequencyType; + } + + public Integer getDepositEvery() { + return this.depositEvery; + } + + public String getNote() { + return this.note; + } + + public Integer getInterestPostEvery() { + return this.interestPostEvery; + } + + public Integer getInterestPostFrequency() { + return this.interestPostFrequency; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommandValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommandValidator.java new file mode 100644 index 000000000..2abe4da50 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountApprovalCommandValidator.java @@ -0,0 +1,38 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; + +public class SavingAccountApprovalCommandValidator { + + private final SavingAccountApprovalCommand command; + + public SavingAccountApprovalCommandValidator(final SavingAccountApprovalCommand command) { + this.command = command ; + } + + public void validate() { + List dataValidationErrors = new ArrayList(); + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("saving.account.transition.approve"); + baseDataValidator.reset().parameter("accountId").value(command.getAccountId()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("commencementDate").value(command.getApprovalDate()).notNull(); + baseDataValidator.reset().parameter("savingsDepositAmountPerPeriod").value(command.getDepositAmountPerPeriod()).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("minimumBalanceForWithdrawal").value(command.getMinimumBalanceForWithdrawal()).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("recurringInterestRate").value(command.getRecurringInterestRate()).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("savingInterestRate").value(command.getSavingInterestRate()).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("interestType").value(command.getInterestType()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("tenure").value(command.getTenure()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("tenureType").value(command.getTenureType()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("frequency").value(command.getDepositFrequencyType()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("payEvery").value(command.getDepositEvery()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("note").value(command.getNote()).notNull(); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommand.java index 3add3362a..3e2b5049f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommand.java @@ -32,7 +32,10 @@ public class SavingAccountCommand { private final Integer interestCalculationMethod; private final BigDecimal minimumBalanceForWithdrawal; private final boolean isPartialDepositAllowed; - private final Integer payEvery; + private final Integer depositEvery; + + private final Integer interestPostEvery; + private final Integer interestPostFrequency; private final Set modifiedParameters; @@ -42,7 +45,7 @@ public class SavingAccountCommand { final LocalDate commencementDate, final Integer savingProductType, final Integer tenureType, final Integer frequency, final Integer interestType, final BigDecimal minimumBalanceForWithdrawal, final Integer interestCalculationMethod, final boolean isLockinPeriodAllowed, final boolean isPartialDepositAllowed, final Integer lockInPeriod, - final Integer lockinPeriodType, final Integer payEvery) { + final Integer lockinPeriodType, final Integer depositEvery,final Integer interestPostEvery,final Integer interestPostFrequency) { this.id = id; this.clientId = clientId; this.productId = productId; @@ -69,7 +72,10 @@ public class SavingAccountCommand { this.lockinPeriodType = lockinPeriodType; this.minimumBalanceForWithdrawal = minimumBalanceForWithdrawal; this.interestCalculationMethod = interestCalculationMethod; - this.payEvery = payEvery; + this.depositEvery = depositEvery; + + this.interestPostEvery = interestPostEvery; + this.interestPostFrequency = interestPostFrequency; } public Long getId() { @@ -156,11 +162,19 @@ public class SavingAccountCommand { return isPartialDepositAllowed; } - public Integer getPayEvery() { - return this.payEvery; + public Integer getDepositEvery() { + return this.depositEvery; } - public boolean isNoFieldChanged() { + public Integer getInterestPostEvery() { + return this.interestPostEvery; + } + + public Integer getInterestPostFrequency() { + return this.interestPostFrequency; + } + + public boolean isNoFieldChanged() { return this.modifiedParameters.isEmpty(); } @@ -231,13 +245,23 @@ public class SavingAccountCommand { public boolean isPartialDepositAllowedChanged() { return this.modifiedParameters.contains("isPartialDepositAllowed"); } - public boolean isPayEveryChanged() { - return this.modifiedParameters.contains("payEvery"); + + public boolean isDepositEveryChanged() { + return this.modifiedParameters.contains("depositEvery"); } + + public boolean isInterestPostedEveryChanged(){ + return this.modifiedParameters.contains(interestPostEvery); + } + + public boolean isInterestPostingChanged(){ + return this.modifiedParameters.contains(interestPostFrequency); + } + public CalculateSavingScheduleCommand toCalculateSavingScheduleCommand() { - return new CalculateSavingScheduleCommand(productId, savingsDepositAmount, payEvery, frequency, recurringInterestRate, - commencementDate, tenure); + return new CalculateSavingScheduleCommand(productId, savingsDepositAmount, depositEvery, frequency, recurringInterestRate, + commencementDate, tenure, interestPostEvery, interestPostFrequency); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommandValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommandValidator.java index d6831abac..031d74cde 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountCommandValidator.java @@ -22,7 +22,7 @@ public class SavingAccountCommandValidator { baseDataValidator.reset().parameter("clientId").value(command.getClientId()).notNull(); baseDataValidator.reset().parameter("productId").value(command.getProductId()).notNull(); - baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100); + baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).notNull().notExceedingLengthOf(100); baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank(); baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimal()).notNull().inMinMaxRange(0, 6); baseDataValidator.reset().parameter("savingsDepositAmountPerPeriod").value(command.getSavingsDepositAmount()).notNull() diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommand.java new file mode 100644 index 000000000..68ce87e2d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommand.java @@ -0,0 +1,37 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; + +public class SavingAccountDepositCommand { + + private final Long accountId; + private final BigDecimal savingsDepostiAmountPerPeriod; + private final LocalDate depositDate; + private final String note; + + public SavingAccountDepositCommand(final Long accountId, final BigDecimal savingsDepostiAmountPerPeriod, final LocalDate depositDate, final String note) { + this.accountId = accountId; + this.savingsDepostiAmountPerPeriod = savingsDepostiAmountPerPeriod; + this.depositDate = depositDate; + this.note = note; + } + + public Long getAccountId() { + return this.accountId; + } + + public BigDecimal getSavingsDepostiAmountPerPeriod() { + return this.savingsDepostiAmountPerPeriod; + } + + public LocalDate getDepositDate() { + return this.depositDate; + } + + public String getNote() { + return this.note; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommandValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommandValidator.java new file mode 100644 index 000000000..1c2f767c7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountDepositCommandValidator.java @@ -0,0 +1,31 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; + +public class SavingAccountDepositCommandValidator { + + private final SavingAccountDepositCommand command; + + public SavingAccountDepositCommandValidator(final SavingAccountDepositCommand command) { + this.command = command; + } + + public void validate(){ + + List dataValidationErrors = new ArrayList(); + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("saving.account.transaction.deposit"); + baseDataValidator.reset().parameter("accountId").value(command.getAccountId()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("depositDate").value(command.getDepositDate()).notNull(); + baseDataValidator.reset().parameter("savingsDepositAmountPerPeriod").value(command.getSavingsDepostiAmountPerPeriod()).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("note").value(command.getNote()).notNull(); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountStateTransitionCommandValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountStateTransitionCommandValidator.java new file mode 100644 index 000000000..8a3fa5e0f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountStateTransitionCommandValidator.java @@ -0,0 +1,30 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; + +public class SavingAccountStateTransitionCommandValidator { + + private final SavingStateTransitionsCommand command; + + public SavingAccountStateTransitionCommandValidator(final SavingStateTransitionsCommand command) { + this.command = command; + } + + public void validate() { + + List dataValidationErrors = new ArrayList(); + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("saving.account.transition"); + baseDataValidator.reset().parameter("accountId").value(command.getAccountId()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("eventDate").value(command.getEventDate()).notNull(); + baseDataValidator.reset().parameter("note").value(command.getNote()).notNull(); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountWithdrawalCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountWithdrawalCommand.java new file mode 100644 index 000000000..a347e947f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingAccountWithdrawalCommand.java @@ -0,0 +1,36 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; + +public class SavingAccountWithdrawalCommand { + + private final Long accountId; + private final LocalDate transactionDate; + private final BigDecimal amount; + private final String note; + + public SavingAccountWithdrawalCommand(final Long accountId, final LocalDate transactionDate, final BigDecimal amount, final String note) { + this.accountId = accountId; + this.transactionDate = transactionDate; + this.amount = amount; + this.note = note; + } + + public Long getAccountId() { + return this.accountId; + } + + public LocalDate getTransactionDate() { + return this.transactionDate; + } + + public BigDecimal getAmount() { + return this.amount; + } + + public String getNote() { + return this.note; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingStateTransitionsCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingStateTransitionsCommand.java new file mode 100644 index 000000000..45ac5860e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/command/SavingStateTransitionsCommand.java @@ -0,0 +1,28 @@ +package org.mifosplatform.portfolio.savingsaccount.command; + +import org.joda.time.LocalDate; + +public class SavingStateTransitionsCommand { + + private final Long accountId; + private final LocalDate eventDate; + private final String note; + + public SavingStateTransitionsCommand( final Long accountId, final LocalDate eventDate, final String note) { + this.accountId = accountId; + this.eventDate = eventDate; + this.note = note; + } + + public Long getAccountId() { + return this.accountId; + } + + public LocalDate getEventDate() { + return this.eventDate; + } + + public String getNote() { + return this.note; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountData.java index 660aac33c..3c50ee16d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountData.java @@ -24,12 +24,10 @@ public class SavingAccountData { private final String productName; private final EnumOptionData productType; private final CurrencyData currencyData; - // FIXME - Madhukar - spelling mistake on word 'deposit' - directly affects api and how people use it. - private final BigDecimal savingsDepostiAmountPerPeriod; + private final BigDecimal savingsDepositAmountPerPeriod; private final EnumOptionData savingsFrequencyType; private final BigDecimal totalDepositAmount; - // FIXME - Madhukar - spelling mistake on work 'recurring' - directly affects api and how people use it. - private final BigDecimal reccuringInterestRate; + private final BigDecimal recurringInterestRate; private final BigDecimal savingInterestRate; private final EnumOptionData interestType; private final EnumOptionData interestCalculationMethod; @@ -69,19 +67,25 @@ public class SavingAccountData { @SuppressWarnings("unused") private final List interestCalculationOptions; + private final Collection savingScheduleDatas; + private final SavingPermissionData permissions; + private final BigDecimal outstandingAmount; + private final BigDecimal dueAmount; + private final SavingScheduleData savingScheduleData; + public static SavingAccountData createFrom(final Long clientId, final String clientDisplayName) { return new SavingAccountData(clientId, clientDisplayName); } public SavingAccountData(Long id, EnumOptionData status, String externalId, Long clientId, String clientName, Long productId, - String productName, EnumOptionData productType, CurrencyData currencyData, BigDecimal savingsDepostiAmountPerPeriod, - EnumOptionData savingsFrequencyType, BigDecimal totalDepositAmount, BigDecimal reccuringInterestRate, + String productName, EnumOptionData productType, CurrencyData currencyData, BigDecimal savingsDepositAmountPerPeriod, + EnumOptionData savingsFrequencyType, BigDecimal totalDepositAmount, BigDecimal recurringInterestRate, BigDecimal savingInterestRate, EnumOptionData interestType, EnumOptionData interestCalculationMethod, Integer tenure, EnumOptionData tenureType, LocalDate projectedCommencementDate, LocalDate actualCommencementDate, LocalDate maturesOnDate, BigDecimal projectedInterestAccuredOnMaturity, BigDecimal actualInterestAccured, BigDecimal projectedMaturityAmount, BigDecimal actualMaturityAmount, boolean preClosureAllowed, BigDecimal preClosureInterestRate, LocalDate withdrawnonDate, LocalDate rejectedonDate, LocalDate closedonDate, boolean isLockinPeriodAllowed, Integer lockinPeriod, - EnumOptionData lockinPeriodType, Integer depositEvery) { + EnumOptionData lockinPeriodType, Integer depositEvery, BigDecimal outstandingAmount) { this.id = id; this.status = status; this.externalId = externalId; @@ -91,10 +95,10 @@ public class SavingAccountData { this.productName = productName; this.productType = productType; this.currencyData = currencyData; - this.savingsDepostiAmountPerPeriod = savingsDepostiAmountPerPeriod; + this.savingsDepositAmountPerPeriod = savingsDepositAmountPerPeriod; this.savingsFrequencyType = savingsFrequencyType; this.totalDepositAmount = totalDepositAmount; - this.reccuringInterestRate = reccuringInterestRate; + this.recurringInterestRate = recurringInterestRate; this.savingInterestRate = savingInterestRate; this.interestType = interestType; this.interestCalculationMethod = interestCalculationMethod; @@ -124,7 +128,11 @@ public class SavingAccountData { this.savingsInterestTypeOptions = null; this.lockinPeriodTypeOptions = null; this.interestCalculationOptions = null; - + this.savingScheduleDatas = null; + this.permissions = null; + this.outstandingAmount = outstandingAmount; + this.dueAmount = BigDecimal.ZERO; + this.savingScheduleData = null; } public SavingAccountData(Long clientId, String clientName) { @@ -137,11 +145,10 @@ public class SavingAccountData { this.productName = null; this.productType = null; this.currencyData = new CurrencyData("USD", "US Dollar", 2, "$", "currency.USD"); - ; - this.savingsDepostiAmountPerPeriod = null; + this.savingsDepositAmountPerPeriod = null; this.savingsFrequencyType = null; this.totalDepositAmount = null; - this.reccuringInterestRate = null; + this.recurringInterestRate = null; this.savingInterestRate = null; this.interestType = null; this.interestCalculationMethod = null; @@ -171,12 +178,17 @@ public class SavingAccountData { this.savingsInterestTypeOptions = null; this.lockinPeriodTypeOptions = null; this.interestCalculationOptions = null; + this.savingScheduleDatas = null; + this.permissions = null; + this.outstandingAmount = BigDecimal.ZERO; + this.dueAmount =BigDecimal.ZERO; + this.savingScheduleData = null; } public SavingAccountData(SavingAccountData account, Collection productOptions, List currencyOptions, List savingsProductTypeOptions, List tenureTypeOptions, List savingFrequencyOptions, List savingsInterestTypeOptions, - List lockinPeriodTypeOptions, List interestCalculationOptions) { + List lockinPeriodTypeOptions, List interestCalculationOptions,BigDecimal dueAmount) { this.id = account.id; this.status = account.status; this.externalId = account.externalId; @@ -186,10 +198,10 @@ public class SavingAccountData { this.productName = account.productName; this.productType = account.productType; this.currencyData = account.currencyData; - this.savingsDepostiAmountPerPeriod = account.savingsDepostiAmountPerPeriod; + this.savingsDepositAmountPerPeriod = account.savingsDepositAmountPerPeriod; this.savingsFrequencyType = account.savingsFrequencyType; this.totalDepositAmount = account.totalDepositAmount; - this.reccuringInterestRate = account.reccuringInterestRate; + this.recurringInterestRate = account.recurringInterestRate; this.savingInterestRate = account.savingInterestRate; this.interestType = account.interestType; this.interestCalculationMethod = account.interestCalculationMethod; @@ -220,11 +232,17 @@ public class SavingAccountData { this.savingsInterestTypeOptions = savingsInterestTypeOptions; this.lockinPeriodTypeOptions = lockinPeriodTypeOptions; this.interestCalculationOptions = interestCalculationOptions; + this.dueAmount = dueAmount; + + this.savingScheduleDatas = account.savingScheduleDatas; + this.permissions = account.permissions; + this.outstandingAmount = account.outstandingAmount; + this.savingScheduleData = account.savingScheduleData; } // FIXME - Madhukar - unused variables been passed into construction - why? public SavingAccountData(Long clientId, String clientName, Long productId, String productName, CurrencyData currency, - BigDecimal interestRate, BigDecimal savingsDepostiAmountPerPeriod, EnumOptionData productType, EnumOptionData tenureType, + BigDecimal interestRate, BigDecimal savingsDepositAmountPerPeriod, EnumOptionData productType, EnumOptionData tenureType, Integer tenure, EnumOptionData savingsFrequencyType, EnumOptionData interestType, EnumOptionData interestCalculationMethod, BigDecimal minimumBalanceForWithdrawal, boolean partialDepositAllowed, boolean lockinPeriodAllowed, Integer lockinPeriod, EnumOptionData lockinPeriodType, Integer depositEvery) { @@ -238,10 +256,10 @@ public class SavingAccountData { this.productName = productName; this.productType = productType; this.currencyData = currency; - this.savingsDepostiAmountPerPeriod = savingsDepostiAmountPerPeriod; + this.savingsDepositAmountPerPeriod = savingsDepositAmountPerPeriod; this.savingsFrequencyType = savingsFrequencyType; this.totalDepositAmount = null; - this.reccuringInterestRate = interestRate; + this.recurringInterestRate = interestRate; this.savingInterestRate = interestRate; this.interestType = interestType; this.interestCalculationMethod = interestCalculationMethod; @@ -271,6 +289,285 @@ public class SavingAccountData { this.savingsInterestTypeOptions = null; this.lockinPeriodTypeOptions = null; this.interestCalculationOptions = null; - + this.savingScheduleDatas = null; + this.permissions = null; + this.outstandingAmount = BigDecimal.ZERO; + this.dueAmount = BigDecimal.ZERO; + this.savingScheduleData = null; } + + public SavingAccountData(SavingAccountData account, SavingPermissionData permissions) { + this.id = account.id; + this.status = account.status; + this.externalId = account.externalId; + this.clientId = account.clientId; + this.clientName = account.clientName; + this.productId = account.productId; + this.productName = account.productName; + this.productType = account.productType; + this.currencyData = account.currencyData; + this.savingsDepositAmountPerPeriod = account.savingsDepositAmountPerPeriod; + this.savingsFrequencyType = account.savingsFrequencyType; + this.totalDepositAmount = account.totalDepositAmount; + this.recurringInterestRate = account.recurringInterestRate; + this.savingInterestRate = account.savingInterestRate; + this.interestType = account.interestType; + this.interestCalculationMethod = account.interestCalculationMethod; + this.tenure = account.tenure; + this.tenureType = account.tenureType; + this.projectedCommencementDate = account.projectedCommencementDate; + this.actualCommencementDate = account.actualCommencementDate; + this.maturesOnDate = account.maturesOnDate; + this.projectedInterestAccuredOnMaturity = account.projectedInterestAccuredOnMaturity; + this.actualInterestAccured = account.actualInterestAccured; + this.projectedMaturityAmount = account.projectedMaturityAmount; + this.actualMaturityAmount = account.actualMaturityAmount; + this.preClosureAllowed = account.preClosureAllowed; + this.preClosureInterestRate = account.preClosureInterestRate; + this.withdrawnonDate = account.withdrawnonDate; + this.rejectedonDate = account.rejectedonDate; + this.closedonDate = account.closedonDate; + this.isLockinPeriodAllowed = account.isLockinPeriodAllowed; + this.lockinPeriod = account.lockinPeriod; + this.lockinPeriodType = account.lockinPeriodType; + this.depositEvery = account.depositEvery; + + this.productOptions = account.productOptions; + this.currencyOptions = account.currencyOptions; + this.savingsProductTypeOptions = account.savingsProductTypeOptions; + this.tenureTypeOptions = account.tenureTypeOptions; + this.savingFrequencyOptions = account.savingFrequencyOptions; + this.savingsInterestTypeOptions = account.savingsInterestTypeOptions; + this.lockinPeriodTypeOptions = account.lockinPeriodTypeOptions; + this.interestCalculationOptions = account.interestCalculationOptions; + this.savingScheduleDatas = account.savingScheduleDatas; + this.permissions = permissions; + this.outstandingAmount = account.outstandingAmount; + this.dueAmount = account.dueAmount; + this.savingScheduleData = account.savingScheduleData; + } + + public SavingAccountData(SavingAccountData account, SavingScheduleData savingScheduleData) { + this.id = account.id; + this.status = account.status; + this.externalId = account.externalId; + this.clientId = account.clientId; + this.clientName = account.clientName; + this.productId = account.productId; + this.productName = account.productName; + this.productType = account.productType; + this.currencyData = account.currencyData; + this.savingsDepositAmountPerPeriod = account.savingsDepositAmountPerPeriod; + this.savingsFrequencyType = account.savingsFrequencyType; + this.totalDepositAmount = account.totalDepositAmount; + this.recurringInterestRate = account.recurringInterestRate; + this.savingInterestRate = account.savingInterestRate; + this.interestType = account.interestType; + this.interestCalculationMethod = account.interestCalculationMethod; + this.tenure = account.tenure; + this.tenureType = account.tenureType; + this.projectedCommencementDate = account.projectedCommencementDate; + this.actualCommencementDate = account.actualCommencementDate; + this.maturesOnDate = account.maturesOnDate; + this.projectedInterestAccuredOnMaturity = account.projectedInterestAccuredOnMaturity; + this.actualInterestAccured = account.actualInterestAccured; + this.projectedMaturityAmount = account.projectedMaturityAmount; + this.actualMaturityAmount = account.actualMaturityAmount; + this.preClosureAllowed = account.preClosureAllowed; + this.preClosureInterestRate = account.preClosureInterestRate; + this.withdrawnonDate = account.withdrawnonDate; + this.rejectedonDate = account.rejectedonDate; + this.closedonDate = account.closedonDate; + this.isLockinPeriodAllowed = account.isLockinPeriodAllowed; + this.lockinPeriod = account.lockinPeriod; + this.lockinPeriodType = account.lockinPeriodType; + this.depositEvery = account.depositEvery; + + this.productOptions = account.productOptions; + this.currencyOptions = account.currencyOptions; + this.savingsProductTypeOptions = account.savingsProductTypeOptions; + this.tenureTypeOptions = account.tenureTypeOptions; + this.savingFrequencyOptions = account.savingFrequencyOptions; + this.savingsInterestTypeOptions = account.savingsInterestTypeOptions; + this.lockinPeriodTypeOptions = account.lockinPeriodTypeOptions; + this.interestCalculationOptions = account.interestCalculationOptions; + this.savingScheduleDatas = account.savingScheduleDatas; + this.permissions = account.permissions; + this.outstandingAmount = account.outstandingAmount; + this.dueAmount = account.dueAmount; + this.savingScheduleData = savingScheduleData; + } + + public Long getId() { + return this.id; + } + + public EnumOptionData getStatus() { + return this.status; + } + + public String getExternalId() { + return this.externalId; + } + + public Long getClientId() { + return this.clientId; + } + + public String getClientName() { + return this.clientName; + } + + public Long getProductId() { + return this.productId; + } + + public String getProductName() { + return this.productName; + } + + public EnumOptionData getProductType() { + return this.productType; + } + + public CurrencyData getCurrencyData() { + return this.currencyData; + } + + public BigDecimal getSavingsDepostiAmountPerPeriod() { + return this.savingsDepositAmountPerPeriod; + } + + public EnumOptionData getSavingsFrequencyType() { + return this.savingsFrequencyType; + } + + public BigDecimal getTotalDepositAmount() { + return this.totalDepositAmount; + } + + public BigDecimal getReccuringInterestRate() { + return this.recurringInterestRate; + } + + public BigDecimal getSavingInterestRate() { + return this.savingInterestRate; + } + + public EnumOptionData getInterestType() { + return this.interestType; + } + + public EnumOptionData getInterestCalculationMethod() { + return this.interestCalculationMethod; + } + + public Integer getTenure() { + return this.tenure; + } + + public EnumOptionData getTenureType() { + return this.tenureType; + } + + public LocalDate getProjectedCommencementDate() { + return this.projectedCommencementDate; + } + + public LocalDate getActualCommencementDate() { + return this.actualCommencementDate; + } + + public LocalDate getMaturesOnDate() { + return this.maturesOnDate; + } + + public BigDecimal getProjectedInterestAccuredOnMaturity() { + return this.projectedInterestAccuredOnMaturity; + } + + public BigDecimal getActualInterestAccured() { + return this.actualInterestAccured; + } + + public BigDecimal getProjectedMaturityAmount() { + return this.projectedMaturityAmount; + } + + public BigDecimal getActualMaturityAmount() { + return this.actualMaturityAmount; + } + + public boolean isPreClosureAllowed() { + return this.preClosureAllowed; + } + + public BigDecimal getPreClosureInterestRate() { + return this.preClosureInterestRate; + } + + public LocalDate getWithdrawnonDate() { + return this.withdrawnonDate; + } + + public LocalDate getRejectedonDate() { + return this.rejectedonDate; + } + + public LocalDate getClosedonDate() { + return this.closedonDate; + } + + public boolean isLockinPeriodAllowed() { + return this.isLockinPeriodAllowed; + } + + public Integer getLockinPeriod() { + return this.lockinPeriod; + } + + public EnumOptionData getLockinPeriodType() { + return this.lockinPeriodType; + } + + public Integer getDepositEvery() { + return this.depositEvery; + } + + public List getProductOptions() { + return this.productOptions; + } + + public List getCurrencyOptions() { + return this.currencyOptions; + } + + public List getSavingsProductTypeOptions() { + return this.savingsProductTypeOptions; + } + + public List getTenureTypeOptions() { + return this.tenureTypeOptions; + } + + public List getSavingFrequencyOptions() { + return this.savingFrequencyOptions; + } + + public List getSavingsInterestTypeOptions() { + return this.savingsInterestTypeOptions; + } + + public List getLockinPeriodTypeOptions() { + return this.lockinPeriodTypeOptions; + } + + public List getInterestCalculationOptions() { + return this.interestCalculationOptions; + } + + public BigDecimal getOutstandingAmount() { + return this.outstandingAmount; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountForLookup.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountForLookup.java new file mode 100644 index 000000000..a6f76568a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingAccountForLookup.java @@ -0,0 +1,15 @@ +package org.mifosplatform.portfolio.savingsaccount.data; + +public class SavingAccountForLookup { + + private final Long id; + + public SavingAccountForLookup(final Long id) { + this.id = id; + } + + public Long getId() { + return this.id; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingPermissionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingPermissionData.java new file mode 100644 index 000000000..dbe4136e1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/data/SavingPermissionData.java @@ -0,0 +1,57 @@ +package org.mifosplatform.portfolio.savingsaccount.data; + +public class SavingPermissionData { + + + + private final boolean rejectAllowed; + private final boolean withdrawnByApplicantAllowed; + private final boolean undoApprovalAllowed; + private final boolean pendingApproval; + private final boolean renewelAllowed; + private final boolean isMaturedDepositAccount; + + private final boolean anyActionOnAccountAllowed; + + public SavingPermissionData(final boolean rejectAllowed, final boolean withdrawnByApplicantAllowed, final boolean undoApprovalAllowed, + final boolean pendingApproval, final boolean renewelAllowed, final boolean isMaturedDepositAccount) { + this.rejectAllowed = rejectAllowed; + this.withdrawnByApplicantAllowed = withdrawnByApplicantAllowed; + this.undoApprovalAllowed = undoApprovalAllowed; + this.pendingApproval = pendingApproval; + this.renewelAllowed = renewelAllowed; + this.isMaturedDepositAccount = isMaturedDepositAccount; + this.anyActionOnAccountAllowed = isRejectAllowed() || isPendingApproval() || isUndoApprovalAllowed() + || isWithdrawnByApplicantAllowed() || isRenewelAllowed() || isMaturedDepositAccount(); + } + + public boolean isRejectAllowed() { + return rejectAllowed; + } + + public boolean isWithdrawnByApplicantAllowed() { + return withdrawnByApplicantAllowed; + } + + public boolean isUndoApprovalAllowed() { + return undoApprovalAllowed; + } + + public boolean isPendingApproval() { + return pendingApproval; + } + + public boolean isAnyActionOnAccountAllowed() { + return anyActionOnAccountAllowed; + } + + public boolean isRenewelAllowed() { + return renewelAllowed; + } + + public boolean isMaturedDepositAccount() { + return isMaturedDepositAccount; + } + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/ReccuringDepositInterestCalculator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/ReccuringDepositInterestCalculator.java index 1784f2de6..d81110e25 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/ReccuringDepositInterestCalculator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/ReccuringDepositInterestCalculator.java @@ -13,62 +13,98 @@ import org.springframework.stereotype.Service; @Service public class ReccuringDepositInterestCalculator { + + private final Integer monthsInYear = 12; // FIXME - MADHUKAR - Are the unused field here needed? public Money calculateInterestOnMaturityFor(Money savingsDepositPerPeriod, Integer tenure, BigDecimal reccuringInterestRate, LocalDate commencementDate, TenureTypeEnum tenureTypeEnum, SavingFrequencyType savingFrequencyType, - SavingInterestCalculationMethod savingInterestCalculationMethod) { - - /* - * // Assuming tenure in months // see - * http://www.onemint.com/2012/04/03/ - * how-to-calculate-interest-on-recurring-deposits/ - * - * A=P(1+r/n)^nt - * - * A = final amount P = principal amount (initial investment) r = annual - * nominal interest rate (as a decimal, not in percentage) n = number of - * times the interest is compounded per year t = number of years - */ - MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); - Integer monthsInYear = 12; + SavingInterestCalculationMethod savingInterestCalculationMethod, Integer depositEvery) { + + BigDecimal finalAmount = BigDecimal.ZERO; + MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); + BigDecimal interestRateAsFraction = reccuringInterestRate.divide(BigDecimal.valueOf(100), mc); - Integer noofTimesInterestCompoundPerYear = Integer.valueOf(0); - switch (savingFrequencyType) { - case MONTHLY: - noofTimesInterestCompoundPerYear = monthsInYear; - break; - case QUATERLY: - noofTimesInterestCompoundPerYear = monthsInYear / 3; - break; - case HALFYEARLY: - noofTimesInterestCompoundPerYear = monthsInYear / 6; - break; - case YEARLY: - noofTimesInterestCompoundPerYear = monthsInYear / 12; - break; - default: - throw new RuntimeException("The specified frequency not supported"); - - } - + Integer noofTimesInterestCompoundPerYear = determineIntValue(savingFrequencyType); + BigDecimal interestRatePerPeriod = BigDecimal.valueOf(interestRateAsFraction.doubleValue() / noofTimesInterestCompoundPerYear.doubleValue()); - BigDecimal onePlusInterestRatePerPeriod = BigDecimal.ONE.add(interestRatePerPeriod); - BigDecimal monthsExpressedInYears; - BigDecimal timeforOneCalculationPeriod; - BigDecimal amountPerPeriod; - BigDecimal finalAmount = BigDecimal.ZERO; - while (tenure > 0) { - monthsExpressedInYears = BigDecimal.valueOf(tenure.doubleValue() / monthsInYear.doubleValue()); - timeforOneCalculationPeriod = BigDecimal.valueOf(noofTimesInterestCompoundPerYear * monthsExpressedInYears.doubleValue()); - amountPerPeriod = savingsDepositPerPeriod.getAmount().multiply( - BigDecimal.valueOf(Math.pow(onePlusInterestRatePerPeriod.doubleValue(), timeforOneCalculationPeriod.doubleValue()))); - finalAmount = finalAmount.add(amountPerPeriod); - tenure--; - } + Integer noOfTimestobeCalculated = tenure/depositEvery; + + if(savingInterestCalculationMethod.isAverageBalance()){ + + BigDecimal totalAmount = BigDecimal.ZERO; + BigDecimal termAmount = BigDecimal.ZERO; + BigDecimal totalInterest = BigDecimal.ZERO; + BigDecimal amountPerPeriod = savingsDepositPerPeriod.getAmount(); + Integer depositEveryOriginalValue = depositEvery; + BigDecimal amountPermonth = amountPerPeriod.divide(BigDecimal.valueOf(depositEvery.doubleValue())); + while (noOfTimestobeCalculated > 0) { + while (depositEvery > 0) { + termAmount = termAmount.add(amountPermonth); + totalAmount = totalAmount.add(termAmount); + depositEvery--; + } + BigDecimal averageAmount = BigDecimal.valueOf(totalAmount.doubleValue()/depositEveryOriginalValue.doubleValue()); + BigDecimal interestRatePerTerm = BigDecimal.valueOf(interestRateAsFraction.doubleValue()/monthsInYear.doubleValue()*depositEveryOriginalValue.doubleValue()); + BigDecimal interestperTerm = averageAmount.multiply(interestRatePerTerm); + totalInterest = totalInterest.add(interestperTerm); + depositEvery = depositEveryOriginalValue; + totalAmount = BigDecimal.ZERO; + noOfTimestobeCalculated--; + } + finalAmount = termAmount.add(totalInterest); + + } else if(savingInterestCalculationMethod.isMonthlyCollection()){ + /* + * // Assuming tenure in months // see + * http://www.onemint.com/2012/04/03/ + * how-to-calculate-interest-on-recurring-deposits/ + * + * A=P(1+r/n)^nt + * + * A = final amount P = principal amount (initial investment) r = annual + * nominal interest rate (as a decimal, not in percentage) n = number of + * times the interest is compounded per year t = number of years + */ + + BigDecimal onePlusInterestRatePerPeriod = BigDecimal.ONE.add(interestRatePerPeriod); + BigDecimal monthsExpressedInYears; + BigDecimal timeforOneCalculationPeriod; + BigDecimal amountPerPeriod; + + while (noOfTimestobeCalculated > 0) { + monthsExpressedInYears = BigDecimal.valueOf(tenure.doubleValue() / monthsInYear.doubleValue()); + timeforOneCalculationPeriod = BigDecimal.valueOf(noofTimesInterestCompoundPerYear * monthsExpressedInYears.doubleValue()); + amountPerPeriod = savingsDepositPerPeriod.getAmount().multiply( + BigDecimal.valueOf(Math.pow(onePlusInterestRatePerPeriod.doubleValue(), timeforOneCalculationPeriod.doubleValue()))); + finalAmount = finalAmount.add(amountPerPeriod); + noOfTimestobeCalculated--; + } + } + return Money.of(savingsDepositPerPeriod.getCurrency(), finalAmount); + } + + private Integer determineIntValue(SavingFrequencyType frequencyType){ + Integer noofTimesInterestCompoundPerYear = Integer.valueOf(0); + switch (frequencyType) { + case MONTHLY: + noofTimesInterestCompoundPerYear = monthsInYear; + break; + case QUATERLY: + noofTimesInterestCompoundPerYear = monthsInYear / 3; + break; + case HALFYEARLY: + noofTimesInterestCompoundPerYear = monthsInYear / 6; + break; + case YEARLY: + noofTimesInterestCompoundPerYear = monthsInYear / 12; + break; + default: + throw new RuntimeException("The specified frequency not supported"); - return Money.of(savingsDepositPerPeriod.getCurrency(), finalAmount); + } + return noofTimesInterestCompoundPerYear; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccount.java index 73e394f23..071a4511a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccount.java @@ -1,6 +1,8 @@ package org.mifosplatform.portfolio.savingsaccount.domain; import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -12,6 +14,7 @@ import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -20,11 +23,13 @@ import javax.persistence.UniqueConstraint; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; import org.joda.time.LocalDate; +import org.joda.time.Months; import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommand; import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingFrequencyType; import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingInterestCalculationMethod; import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingProduct; @@ -33,6 +38,7 @@ import org.mifosplatform.portfolio.savingsaccountproduct.domain.SavingsInterestT import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositAccountEvent; import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositAccountStatus; import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositLifecycleStateMachine; +import org.mifosplatform.portfolio.savingsdepositaccount.exception.InvalidDepositStateTransitionException; import org.mifosplatform.portfolio.savingsdepositproduct.domain.TenureTypeEnum; import org.mifosplatform.useradministration.domain.AppUser; @@ -48,10 +54,8 @@ public class SavingAccount extends AbstractAuditableCustom { @JoinColumn(name = "product_id") private SavingProduct product; - // FIXME - MADHUKAR - Do not use the actual enum type here but instead use - // Integer e.g. private Integer savingProductType; @Column(name = "savings_product_type", nullable = false) - private SavingProductType savingProductType; + private Integer savingProductType; @Column(name = "external_id") private String externalId; @@ -76,24 +80,17 @@ public class SavingAccount extends AbstractAuditableCustom { @Column(name = "tenure", nullable = false) private Integer tenure; - // FIXME - MADHUKAR - Do not use the actual enum type here use integer representation @Column(name = "tenure_type", nullable = false) - private TenureTypeEnum tenureType; + private Integer tenureType; - // FIXME - MADHUKAR - Do not use the actual enum type here but instead use - // Integer e.g. private Integer savingProductType; @Column(name = "frequency", nullable = false) - private SavingFrequencyType frequency; + private Integer frequency; - // FIXME - MADHUKAR - Do not use the actual enum type here but instead use - // Integer e.g. private Integer savingProductType; @Column(name = "interest_type", nullable = false) - private SavingsInterestType interestType; + private Integer interestType; - // FIXME - MADHUKAR - Do not use the actual enum type here but instead use - // Integer e.g. private Integer savingProductType; @Column(name = "interest_calculation_method") - private SavingInterestCalculationMethod interestCalculationMethod; + private Integer interestCalculationMethod; @Temporal(TemporalType.DATE) @Column(name = "projected_commencement_date") @@ -127,6 +124,9 @@ public class SavingAccount extends AbstractAuditableCustom { @Column(name = "pre_closure_interest_rate", scale = 6, precision = 19, nullable = false) private BigDecimal preClosureInterestRate; + + @Column(name = "outstanding_amount", scale = 6, precision = 19, nullable = false) + private BigDecimal outstandingAmount; @Column(name = "is_preclosure_allowed", nullable = false) private boolean preClosureAllowed = false; @@ -158,16 +158,42 @@ public class SavingAccount extends AbstractAuditableCustom { private Integer lockinPeriod; @Column(name = "lock_in_period_type", nullable = false) - private PeriodFrequencyType lockinPeriodType; + private Integer lockinPeriodType; @Column(name = "deposit_every", nullable = false) private Integer payEvery; + + @Column(name = "interest_posting_every", nullable = false) + private Integer interestPostEvery; + + @Column(name = "interest_posting_frequency", nullable = false) + private Integer interestPostFrequency; + + @Column(name = "interest_posted_amount", scale = 6, precision = 19) + private BigDecimal interestPostedAmount; + + @Temporal(TemporalType.DATE) + @Column(name = "last_interest_posted_date") + private Date lastInterestPostedDate; + + @Temporal(TemporalType.DATE) + @Column(name = "next_interest_posting_date") + private Date nextInterestPostingDate; // see // http://stackoverflow.com/questions/4334970/hibernate-cannot-simultaneously-fetch-multiple-bags @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "savingAccount", orphanRemoval = true) private final List savingScheduleInstallments = new ArrayList(); + + @OrderBy(value = "id") + @LazyCollection(LazyCollectionOption.FALSE) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "savingAccount", orphanRemoval = true) + private final List savingAccountTransactions = new ArrayList(); + + public SavingAccount() { + // TODO Auto-generated constructor stub + } public static SavingAccount openNew(Client client, SavingProduct product, String externalId, Money savingsDeposit, BigDecimal reccuringInterestRate, BigDecimal savingInterestRate, Integer tenure, LocalDate commencementDate, @@ -175,27 +201,28 @@ public class SavingAccount extends AbstractAuditableCustom { SavingsInterestType interestType, SavingInterestCalculationMethod savingInterestCalculationMethod, boolean isLockinPeriodAllowed, Integer lockinPeriod, PeriodFrequencyType lockinPeriodType, ReccuringDepositInterestCalculator reccuringDepositInterestCalculator, final DepositLifecycleStateMachine lifecycleStateMachine, - Integer payEvery) { + Integer depositEvery, Integer interestPostEvery, Integer interestPostFrequency) { Money futureValueOnMaturity = null; if (savingProductType.isReccuring()) { futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure, - reccuringInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod); + reccuringInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,depositEvery); } else if (savingProductType.isRegular()) { futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure, - savingInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod); + savingInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,depositEvery); } return new SavingAccount(client, externalId, product, savingsDeposit, reccuringInterestRate, savingInterestRate, tenure, commencementDate, tenureTypeEnum, savingProductType, savingFrequencyType, interestType, savingInterestCalculationMethod, - isLockinPeriodAllowed, lockinPeriod, lockinPeriodType, futureValueOnMaturity, lifecycleStateMachine,payEvery); + isLockinPeriodAllowed, lockinPeriod, lockinPeriodType, futureValueOnMaturity, lifecycleStateMachine,depositEvery,interestPostEvery, interestPostFrequency); } public SavingAccount(Client client, String externalId, SavingProduct product, Money savingsDeposit, BigDecimal reccuringInterestRate, BigDecimal savingInterestRate, Integer tenure, LocalDate commencementDate, TenureTypeEnum tenureTypeEnum, SavingProductType savingProductType, SavingFrequencyType savingFrequencyType, SavingsInterestType interestType, SavingInterestCalculationMethod savingInterestCalculationMethod, boolean isLockinPeriodAllowed, Integer lockinPeriod, - PeriodFrequencyType lockinPeriodType, Money futureValueOnMaturity, final DepositLifecycleStateMachine lifecycleStateMachine,Integer payEvery) { + PeriodFrequencyType lockinPeriodType, Money futureValueOnMaturity, final DepositLifecycleStateMachine lifecycleStateMachine,Integer payEvery, + Integer interestPostEvery, Integer interestPostFrequency) { DepositAccountStatus from = null; if (accountStatus != null) { @@ -219,18 +246,21 @@ public class SavingAccount extends AbstractAuditableCustom { this.savingInterestRate = savingInterestRate; this.tenure = tenure; this.projectedCommencementDate = commencementDate.toDate(); - this.tenureType = tenureTypeEnum; - this.savingProductType = savingProductType; - this.frequency = savingFrequencyType; - this.interestType = interestType; - this.interestCalculationMethod = savingInterestCalculationMethod; + this.tenureType = tenureTypeEnum.getValue(); + this.savingProductType = savingProductType.getValue(); + this.frequency = savingFrequencyType.getValue(); + this.interestType = interestType.getValue(); + this.interestCalculationMethod = savingInterestCalculationMethod.getValue(); this.isLockinPeriodAllowed = isLockinPeriodAllowed; this.lockinPeriod = lockinPeriod; - this.lockinPeriodType = lockinPeriodType; + this.lockinPeriodType = lockinPeriodType.getValue(); this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount(); this.projectedInterestAccruedOnMaturity = futureValueOnMaturity.getAmount().subtract( - BigDecimal.valueOf(savingsDeposit.getAmount().doubleValue() * tenure.doubleValue())); + BigDecimal.valueOf(savingsDeposit.getAmount().doubleValue() * tenure.doubleValue() / payEvery)); //presently only for months this.payEvery = payEvery; + this.outstandingAmount = BigDecimal.ZERO; + this.interestPostEvery = interestPostEvery; + this.interestPostFrequency=interestPostFrequency; } public void modifyAccount(SavingProduct product, String externalId, Money savingsDeposit, BigDecimal reccuringInterestRate, @@ -242,10 +272,10 @@ public class SavingAccount extends AbstractAuditableCustom { Money futureValueOnMaturity = null; if (savingProductType.isReccuring()) { futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure, - reccuringInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod); + reccuringInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,payEvery); } else if (savingProductType.isRegular()) { futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsDeposit, tenure, - savingInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod); + savingInterestRate, commencementDate, tenureTypeEnum, savingFrequencyType, savingInterestCalculationMethod,payEvery); } this.product = product; @@ -255,14 +285,15 @@ public class SavingAccount extends AbstractAuditableCustom { this.savingInterestRate = savingInterestRate; this.tenure = tenure; this.projectedCommencementDate = commencementDate.toDate(); - this.tenureType = tenureTypeEnum; - this.savingProductType = savingProductType; - this.frequency = savingFrequencyType; - this.interestCalculationMethod = savingInterestCalculationMethod; + this.tenureType = tenureTypeEnum.getValue(); + this.savingProductType = savingProductType.getValue(); + this.frequency = savingFrequencyType.getValue(); + this.interestCalculationMethod = savingInterestCalculationMethod.getValue(); this.isLockinPeriodAllowed = isLockinPeriodAllowed; this.lockinPeriod = lockinPeriod; - this.lockinPeriodType = lockinPeriodType; + this.lockinPeriodType = lockinPeriodType.getValue(); this.payEvery = payEvery; + this.outstandingAmount = BigDecimal.ZERO; // FIXME - MADHUKAR - futureValueOnMaturity is possibly null this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount(); @@ -286,7 +317,7 @@ public class SavingAccount extends AbstractAuditableCustom { return product; } - public SavingProductType getSavingProductType() { + public Integer getSavingProductType() { return savingProductType; } @@ -310,19 +341,19 @@ public class SavingAccount extends AbstractAuditableCustom { return tenure; } - public TenureTypeEnum getTenureType() { + public Integer getTenureType() { return tenureType; } - public SavingFrequencyType getFrequency() { + public Integer getFrequency() { return frequency; } - public SavingsInterestType getInterestType() { + public Integer getInterestType() { return interestType; } - public SavingInterestCalculationMethod getInterestCalculationMethod() { + public Integer getInterestCalculationMethod() { return interestCalculationMethod; } @@ -350,7 +381,7 @@ public class SavingAccount extends AbstractAuditableCustom { return lockinPeriod; } - public PeriodFrequencyType getLockinPeriodType() { + public Integer getLockinPeriodType() { return lockinPeriodType; } @@ -358,9 +389,306 @@ public class SavingAccount extends AbstractAuditableCustom { return this.payEvery; } + public BigDecimal getOutstandingAmount() { + return this.outstandingAmount; + } + + public Integer getInterestPostEvery() { + return this.interestPostEvery; + } + + public Integer getInterestPostFrequency() { + return this.interestPostFrequency; + } + + public List getSavingAccountTransactions() { + return this.savingAccountTransactions; + } + public void addSavingScheduleInstallment(final SavingScheduleInstallments installment) { installment.updateAccount(this); this.savingScheduleInstallments.add(installment); } + public void reject(LocalDate rejectedOn, DepositLifecycleStateMachine depositLifecycleStateMachine) { + + + DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_REJECTED, + DepositAccountStatus.fromInt(this.accountStatus)); + this.accountStatus = statusEnum.getValue(); + + this.maturesOnDate = null; + this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate(); + this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate(); + + if (rejectedOn.isBefore(projectedCommencementDate())) { + + final String errorMessage = "The date on which a saving account is rejected cannot be before its submittal date: " + + projectedCommencementDate().toString(); + throw new InvalidDepositStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, rejectedOn, + projectedCommencementDate()); + + } + if (rejectedOn.isAfter(new LocalDate())) { + + final String errorMessage = "The date on which a saving account is rejected cannot be in the future."; + throw new InvalidDepositStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, rejectedOn); + + } + + + } + + public LocalDate projectedCommencementDate() { + LocalDate date = null; + if (this.projectedCommencementDate != null) { + date = new LocalDate(this.projectedCommencementDate); + } + return date; + } + + public void withdrawnByApplicant(LocalDate withdrawnOn, DepositLifecycleStateMachine depositLifecycleStateMachine) { + + DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_WITHDRAWN, + DepositAccountStatus.fromInt(this.accountStatus)); + this.accountStatus = statusEnum.getValue(); + + this.maturesOnDate = null; + this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate(); + this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate(); + + if (withdrawnOn.isBefore(projectedCommencementDate())) { + + final String errorMessage = "The date on which a deposit is rejected cannot be before its submittal date: " + + projectedCommencementDate().toString(); + throw new InvalidDepositStateTransitionException("withdrawnbyclient", "cannot.be.before.submittal.date", errorMessage, withdrawnOn, + projectedCommencementDate()); + + } + + if (withdrawnOn.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a deposit is rejected cannot be in the future."; + throw new InvalidDepositStateTransitionException("withdrawnbyclient", "cannot.be.a.future.date", errorMessage, withdrawnOn); + } + } + + public void undoSavingAccountApproval(DepositLifecycleStateMachine depositLifecycleStateMachine) { + DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_APPROVAL_UNDO, + DepositAccountStatus.fromInt(this.accountStatus)); + this.accountStatus = statusEnum.getValue(); + this.savingScheduleInstallments.clear(); + this.closedOnDate = new Date(); + } + + @SuppressWarnings("null") + public void approveSavingAccount(LocalDate approvalDate, BigDecimal savingsDepositAmountPerPeriod, BigDecimal recurringInterestRate, + BigDecimal savingInterestRate, Integer interestType, Integer tenure, Integer tenureType, Integer frequency, Integer payEvery, + DepositLifecycleStateMachine depositLifecycleStateMachine, ReccuringDepositInterestCalculator reccuringDepositInterestCalculator, + Integer interestPostEvery, Integer interestPostFrequency) { + + DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_APPROVED, + DepositAccountStatus.fromInt(this.accountStatus)); + this.accountStatus = statusEnum.getValue(); + + Money savingsAmountPerPeriod = Money.of(currency, savingsDepositAmountPerPeriod); + TenureTypeEnum tenureTypeEnum = TenureTypeEnum.fromInt(tenureType); + SavingFrequencyType savingFrequencyType = SavingFrequencyType.fromInt(frequency); + SavingInterestCalculationMethod interestCalculationMethod = SavingInterestCalculationMethod.fromInt(this.interestCalculationMethod); + + Money futureValueOnMaturity = null; + if (SavingProductType.fromInt(product.getSavingProductRelatedDetail().getSavingProductType()).isReccuring()) { + futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsAmountPerPeriod, tenure, + recurringInterestRate, approvalDate, tenureTypeEnum, savingFrequencyType, interestCalculationMethod,payEvery); + } else if (SavingProductType.fromInt(product.getSavingProductRelatedDetail().getSavingProductType()).isRegular()) { + futureValueOnMaturity = reccuringDepositInterestCalculator.calculateInterestOnMaturityFor(savingsAmountPerPeriod, tenure, + savingInterestRate, approvalDate, tenureTypeEnum, savingFrequencyType, interestCalculationMethod,payEvery); + } + + this.actualCommencementDate = approvalDate.toDate(); + this.savingsDepositAmountPerPeriod = savingsDepositAmountPerPeriod; + this.reccuringInterestRate = recurringInterestRate; + this.savingInterestRate = savingInterestRate; + this.interestType = interestType; + this.tenure = tenure; + this.tenureType = tenureType; + this.frequency = frequency; + this.payEvery = payEvery; + this.savingScheduleInstallments.clear(); + this.total=futureValueOnMaturity.getAmount(); + this.interestAccrued = futureValueOnMaturity.getAmount().subtract(BigDecimal.valueOf(savingsDepositAmountPerPeriod.doubleValue() * tenure/payEvery.doubleValue())); + this.interestPostEvery = interestPostEvery; + this.interestPostFrequency = interestPostFrequency; + + // for interest posting initializing + this.lastInterestPostedDate = this.actualCommencementDate; + this.nextInterestPostingDate = new LocalDate(this.lastInterestPostedDate).plusMonths(this.interestPostEvery).toDate(); + this.interestPostedAmount = BigDecimal.ZERO; + } + + public void depositMoney(SavingAccountDepositCommand command) { + + BigDecimal depositAmount = BigDecimal.ZERO; + BigDecimal remainAmountToBePaid = BigDecimal.ZERO; + BigDecimal commandRemainDepositAmount = command.getSavingsDepostiAmountPerPeriod(); + + SavingAccountTransaction savingAccountTransaction = SavingAccountTransaction.deposit(command.getSavingsDepostiAmountPerPeriod(),command.getDepositDate()); + savingAccountTransaction.updateAccount(this); + this.savingAccountTransactions.add(savingAccountTransaction); + this.outstandingAmount = this.outstandingAmount.add(command.getSavingsDepostiAmountPerPeriod()); + this.totalSavingsAmount = this.totalSavingsAmount.add(command.getSavingsDepostiAmountPerPeriod()); + + for(SavingScheduleInstallments savingScheduleInstallment : this.savingScheduleInstallments){ + if(!savingScheduleInstallment.isCompleted()){ + depositAmount = savingScheduleInstallment.getDeposit(); + remainAmountToBePaid = depositAmount.subtract(savingScheduleInstallment.getDepositPaid()); + if (remainAmountToBePaid.doubleValue() > 0) { + if (commandRemainDepositAmount.doubleValue() > 0) { + if (commandRemainDepositAmount.doubleValue() >= remainAmountToBePaid.doubleValue()) { + savingScheduleInstallment.setDepositPaid(remainAmountToBePaid.add(savingScheduleInstallment.getDepositPaid())); + savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate()); + savingScheduleInstallment.setCompleted(true); + commandRemainDepositAmount = commandRemainDepositAmount.subtract(remainAmountToBePaid); + } else if(commandRemainDepositAmount.doubleValue() < remainAmountToBePaid.doubleValue()){ + savingScheduleInstallment.setDepositPaid(commandRemainDepositAmount.add(savingScheduleInstallment.getDepositPaid())); + savingScheduleInstallment.setPaymentDate(command.getDepositDate().toDate()); + commandRemainDepositAmount = BigDecimal.ZERO; + } + } + } + } + } + } + + public boolean isActive() { + return DepositAccountStatus.fromInt(this.accountStatus).isActive(); + } + + public void withdrawAmount(BigDecimal amount, LocalDate transactionDate) { + this.outstandingAmount = this.outstandingAmount.subtract(amount); + SavingAccountTransaction savingAccountTransaction = SavingAccountTransaction.withdraw(amount,transactionDate); + savingAccountTransaction.updateAccount(this); + this.savingAccountTransactions.add(savingAccountTransaction); + } + + + /** + * Delete is a soft delete. Updates flag on account so it wont appear + * in query/report results. + * + * Any fields with unique constraints and prepended with id of record. + */ + public void delete() { + this.deleted = true; + this.externalId = this.getId() + "_" + this.externalId; + } + + public void postInterest(ReccuringDepositInterestCalculator reccuringDepositInterestCalculator) { + + LocalDate lastInterestPostedDate = getLastInterestPostedDate(); + @SuppressWarnings("unused") + LocalDate nextInterestPostingDate = getNextInterestPostedDate(); + @SuppressWarnings("unused") + SavingInterestCalculationMethod savingInterestCalculationMethod = SavingInterestCalculationMethod.fromInt(this.interestCalculationMethod); + + Integer monthsForInterestCalculation = Months.monthsBetween(lastInterestPostedDate, new LocalDate()).getMonths(); + Integer postInterestItereations = monthsForInterestCalculation / this.interestPostEvery; + MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); + BigDecimal interestRateAsFraction = reccuringInterestRate.divide(BigDecimal.valueOf(100), mc); + final Integer monthsInYear = 12; + + BigDecimal totalDepositedAmount = BigDecimal.ZERO; + BigDecimal totalWithdrawableAmount = BigDecimal.ZERO; + BigDecimal totalOutstandingAmount = BigDecimal.ZERO; + boolean isOutstandingAmountCalculated = false; + + while (postInterestItereations > 0 ) { + lastInterestPostedDate = getLastInterestPostedDate(); + nextInterestPostingDate = getNextInterestPostedDate(); + LocalDate transactionStartDate = lastInterestPostedDate; + + BigDecimal termTotalAmount = BigDecimal.ZERO; + BigDecimal averageBalanceForTerm = BigDecimal.ZERO; + + for (int i=0; i< interestPostEvery; i++){ + //default for months here + LocalDate transactionEndDate = transactionStartDate.plusMonths(1); + BigDecimal depositedAmount = BigDecimal.ZERO; + BigDecimal withdrawableAmount = BigDecimal.ZERO; + BigDecimal outstandingAmount = BigDecimal.ZERO; + + for(SavingAccountTransaction savingAccountTransaction : this.savingAccountTransactions){ + LocalDate actualTransactionDate = savingAccountTransaction.getTransactionDate(); + if (actualTransactionDate != null && (actualTransactionDate.isAfter(transactionStartDate) || actualTransactionDate.isEqual(transactionStartDate))) { + if (actualTransactionDate.isBefore(transactionEndDate)) { + if(savingAccountTransaction.getTypeOf().isDeposit()){ + depositedAmount = depositedAmount.add(savingAccountTransaction.getAmount()); + } else if (savingAccountTransaction.getTypeOf().isWithdraw()) { + withdrawableAmount = withdrawableAmount.add(savingAccountTransaction.getAmount()); + } + } + } else if (actualTransactionDate != null && (actualTransactionDate.isBefore(transactionStartDate))) { + if(!isOutstandingAmountCalculated){ + if(savingAccountTransaction.getTypeOf().isDeposit()){ + depositedAmount = depositedAmount.add(savingAccountTransaction.getAmount()); + } else if (savingAccountTransaction.getTypeOf().isWithdraw()) { + withdrawableAmount = withdrawableAmount.add(savingAccountTransaction.getAmount()); + } + } + } + } + outstandingAmount = depositedAmount.subtract(withdrawableAmount); + totalDepositedAmount = totalDepositedAmount.add(depositedAmount); + totalWithdrawableAmount = totalWithdrawableAmount.add(withdrawableAmount); + totalOutstandingAmount = totalOutstandingAmount.add(outstandingAmount); + termTotalAmount = termTotalAmount.add(totalOutstandingAmount); + transactionStartDate = transactionEndDate; + isOutstandingAmountCalculated = true; + } + // FIXME-MADHUKAR calculate interest on calculation method + + // if (savingInterestCalculationMethod.isAverageBalance()) { + averageBalanceForTerm =BigDecimal.valueOf(termTotalAmount.doubleValue()/this.interestPostEvery); + BigDecimal interestRatePerTerm = BigDecimal.valueOf(interestRateAsFraction.doubleValue()/monthsInYear.doubleValue()*this.interestPostEvery.doubleValue()); + BigDecimal interestPerTerm = averageBalanceForTerm.multiply(interestRatePerTerm); + this.interestPostedAmount = this.interestPostedAmount.add(interestPerTerm); + totalOutstandingAmount = totalOutstandingAmount.add(interestPerTerm); + // } else if(savingInterestCalculationMethod.isMonthlyCollection()){ + + //} + this.outstandingAmount = this.outstandingAmount . add(interestPerTerm); + this.lastInterestPostedDate = getNextInterestPostedDate().toDate(); + this.nextInterestPostingDate = getNextInterestPostedDate().plusMonths(this.interestPostEvery).toDate(); + postInterestItereations--; + } + } + + private final LocalDate getLastInterestPostedDate() { + LocalDate lastInterestPostedDate; + if (this.lastInterestPostedDate == null) { + lastInterestPostedDate = new LocalDate(this.actualCommencementDate); + } else { + lastInterestPostedDate = new LocalDate(this.lastInterestPostedDate); + } + return lastInterestPostedDate; + } + + private final LocalDate getNextInterestPostedDate() { + LocalDate nextInterestPostingDate; + if (this.nextInterestPostingDate == null) { + nextInterestPostingDate = new LocalDate(this.nextInterestPostingDate).plusMonths(this.interestPostEvery); + } else { + nextInterestPostingDate = new LocalDate(this.nextInterestPostingDate); + } + return nextInterestPostingDate; + } + + private final LocalDate getMaturityDate(){ + LocalDate commencementDate; + if (this.actualCommencementDate == null) { + commencementDate = new LocalDate(this.projectedCommencementDate).plusMonths(this.tenure); + } else { + commencementDate = new LocalDate(this.actualCommencementDate).plusMonths(this.tenure); + } + return commencementDate; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccountTransaction.java new file mode 100644 index 000000000..bcf6c6add --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingAccountTransaction.java @@ -0,0 +1,89 @@ +package org.mifosplatform.portfolio.savingsaccount.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.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; +import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositAccountTransactionType; +import org.mifosplatform.useradministration.domain.AppUser; + +@Entity +@Table(name = "m_saving_account_transaction") +public class SavingAccountTransaction extends AbstractAuditableCustom { + + @ManyToOne(optional = false) + @JoinColumn(name = "saving_account_id", nullable = false) + private SavingAccount savingAccount; + + // @Enumerated(EnumType.ORDINAL) + @Column(name = "transaction_type_enum", nullable = false) + private Integer typeOf; + + @SuppressWarnings("unused") + @OneToOne(optional = true, cascade = { CascadeType.PERSIST }) + @JoinColumn(name = "contra_id") + private SavingAccountTransaction contra; + + @Temporal(TemporalType.DATE) + @Column(name = "transaction_date", nullable = false) + private final Date transactionDate; + + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private final BigDecimal amount; + + public SavingAccountTransaction() { + this.transactionDate = null; + this.amount = BigDecimal.ZERO; + } + + public SavingAccountTransaction(DepositAccountTransactionType transactionType, BigDecimal amount, LocalDate paymentDate) { + this.typeOf = transactionType.getValue(); + this.amount = amount; + this.transactionDate =paymentDate.toDate(); + } + + public static SavingAccountTransaction deposit(BigDecimal amount, LocalDate paymentDate) { + return new SavingAccountTransaction(DepositAccountTransactionType.DEPOSIT, amount == null ? new BigDecimal(0) : amount, paymentDate); + } + + public static SavingAccountTransaction withdraw(BigDecimal amount, LocalDate paymentDate) { + return new SavingAccountTransaction(DepositAccountTransactionType.WITHDRAW, amount == null ? new BigDecimal(0) : amount, paymentDate); + } + + public static SavingAccountTransaction postInterest(BigDecimal amount, LocalDate paymentDate) { + return new SavingAccountTransaction(DepositAccountTransactionType.INTEREST_POSTING, amount == null ? new BigDecimal(0) : amount, paymentDate); + } + + public void updateAccount(SavingAccount savingAccount) { + this.savingAccount = savingAccount; + } + + public DepositAccountTransactionType getTypeOf() { + return DepositAccountTransactionType.fromInt(this.typeOf); + } + + public BigDecimal getAmount() { + return this.amount; + } + + public LocalDate getTransactionDate() { + LocalDate transactionDate = null; + if(this.transactionDate != null){ + transactionDate = new LocalDate(this.transactionDate); + } + return transactionDate; + } + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingScheduleInstallments.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingScheduleInstallments.java index e0cdb59a5..7fe316c00 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingScheduleInstallments.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/domain/SavingScheduleInstallments.java @@ -39,7 +39,7 @@ public class SavingScheduleInstallments extends AbstractAuditableCustom account.getOutstandingAmount().doubleValue()) { + throw new DepositAccountTransactionsException("deposit.transaction.interest.withdrawal.exceed", "You can Withdraw " + + account.getOutstandingAmount() + " only"); + } + + account.withdrawAmount(command.getAmount(),command.getTransactionDate()); + + } + + public void postInterest(SavingAccount account) { + account.postInterest(this.reccuringDepositInterestCalculator); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformService.java index 951f9a3a2..eb96c5a96 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformService.java @@ -1,8 +1,13 @@ package org.mifosplatform.portfolio.savingsaccount.service; +import java.math.BigDecimal; import java.util.Collection; +import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData; +import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup; +import org.mifosplatform.portfolio.savingsaccount.data.SavingPermissionData; +import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData; public interface SavingAccountReadPlatformService { @@ -11,4 +16,12 @@ public interface SavingAccountReadPlatformService { SavingAccountData retrieveSavingsAccount(Long accountId); SavingAccountData retrieveNewSavingsAccountDetails(Long clientId, Long productId); + + SavingPermissionData retrieveSavingAccountPermissions(SavingAccountData data); + + BigDecimal deriveSavingDueAmount(SavingAccountData account); + + SavingScheduleData retrieveSavingsAccountSchedule(Long accountId, CurrencyData currencyData); + + Collection retrieveSavingAccountsForLookUp(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformServiceImpl.java index 2903a6124..e63269cf0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountReadPlatformServiceImpl.java @@ -4,8 +4,10 @@ import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; +import java.util.List; import org.joda.time.LocalDate; +import org.joda.time.Months; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; @@ -14,11 +16,16 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData; +import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup; +import org.mifosplatform.portfolio.savingsaccount.data.SavingPermissionData; +import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData; +import org.mifosplatform.portfolio.savingsaccount.data.SavingSchedulePeriodData; import org.mifosplatform.portfolio.savingsaccountproduct.data.SavingProductData; import org.mifosplatform.portfolio.savingsaccountproduct.data.SavingProductLookup; import org.mifosplatform.portfolio.savingsaccountproduct.service.SavingProductEnumerations; import org.mifosplatform.portfolio.savingsaccountproduct.service.SavingProductReadPlatformService; import org.mifosplatform.portfolio.savingsdepositaccount.service.DepositAccountEnumerations; +import org.mifosplatform.portfolio.savingsdepositproduct.domain.TenureTypeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -58,6 +65,7 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl SavingAccountMapper mapper = new SavingAccountMapper(); String sql = "select " + mapper.schema() + " where sa.id = ? and sa.is_deleted=0"; SavingAccountData savingAccountData = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { accountId }); + return savingAccountData; } @@ -73,7 +81,7 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl + " sa.projected_commencement_date AS projectedCommencementDate, sa.actual_commencement_date AS actualCommencementDate," + " sa.matures_on_date AS maturesOnDate, sa.projected_interest_accrued_on_maturity AS projectedInterestAccuredOnMaturity," + " sa.actual_interest_accrued AS actualInterestAccured, sa.projected_total_maturity_amount AS projectedTotalMaturityAmount," - + " sa.actual_total_amount AS actualTotalAmount, sa.is_preclosure_allowed AS isPreclosureAllowed," + + " sa.actual_total_amount AS actualTotalAmount, sa.is_preclosure_allowed AS isPreclosureAllowed, sa.outstanding_amount as outstandingAmount, " + " sa.pre_closure_interest_rate AS preClosureInterestRate, sa.is_lock_in_period_allowed AS isLockinPeriodAllowed," + " sa.lock_in_period AS lockinPeriod, sa.lock_in_period_type AS lockinPeriodType, sa.withdrawnon_date AS withdrawnonDate," + " sa.rejectedon_date AS rejectedonDate, sa.closedon_date AS closedonDate, ps.name AS productName, sa.deposit_every as depositEvery, " @@ -131,6 +139,7 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl boolean preClosureAllowed = rs.getBoolean("isPreclosureAllowed"); BigDecimal preClosureInterestRate = rs.getBigDecimal("preClosureInterestRate"); + BigDecimal outstandingAmount = rs.getBigDecimal("outstandingAmount"); LocalDate withdrawnonDate = JdbcSupport.getLocalDate(rs, "withdrawnonDate"); LocalDate rejectedonDate = JdbcSupport.getLocalDate(rs, "rejectedonDate"); @@ -147,7 +156,7 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl interestType, interestCalculationMethod, tenure, tenureType, projectedCommencementDate, actualCommencementDate, maturesOnDate, projectedInterestAccuredOnMaturity, actualInterestAccured, projectedMaturityAmount, actualMaturityAmount, preClosureAllowed, preClosureInterestRate, withdrawnonDate, rejectedonDate, closedonDate, - isLockinPeriodAllowed, lockinPeriod, lockinPeriodType,depositEvery); + isLockinPeriodAllowed, lockinPeriod, lockinPeriodType,depositEvery,outstandingAmount); } } @@ -188,4 +197,99 @@ public class SavingAccountReadPlatformServiceImpl implements SavingAccountReadPl return match; } + @Override + public SavingPermissionData retrieveSavingAccountPermissions(SavingAccountData savingAccountData) { + boolean pendingApproval = (savingAccountData.getStatus().getId().equals(100L)); + boolean undoApprovalAllowed = (savingAccountData.getStatus().getId().equals(300L)); + boolean renewelAllowed = false; + if (savingAccountData.getMaturesOnDate() != null && savingAccountData.getTenureType().equals(TenureTypeEnum.FIXED_PERIOD)) { + if (new LocalDate().isAfter(savingAccountData.getMaturesOnDate())) { + renewelAllowed = true; + } + } + boolean rejectAllowed = pendingApproval; + boolean withdrawnByApplicantAllowed = pendingApproval; + boolean isMaturedDepositAccount = (savingAccountData.getStatus().getId().equals(700L)); + return new SavingPermissionData(rejectAllowed, withdrawnByApplicantAllowed, undoApprovalAllowed, pendingApproval, renewelAllowed, isMaturedDepositAccount); + } + + @SuppressWarnings("unused") + @Override + public BigDecimal deriveSavingDueAmount(SavingAccountData account) { + + BigDecimal dueAmount = BigDecimal.ZERO; + EnumOptionData status = account.getStatus(); + LocalDate date = account.getActualCommencementDate(); + + if (account.getStatus() != null && account.getActualCommencementDate() != null) { + BigDecimal amountPerPeriod = account.getSavingsDepostiAmountPerPeriod(); + Integer tenure = account.getTenure(); + Integer depositEvery = account.getDepositEvery(); + Integer noOfPeriods = tenure/depositEvery ; + Integer noOfMonths = Months.monthsBetween(date, new LocalDate()).getMonths(); + Integer noOfPeriodsExistedTillDate = noOfMonths/depositEvery; + BigDecimal totalAmountToBePaidTillDate = BigDecimal.valueOf(noOfPeriodsExistedTillDate.doubleValue()*amountPerPeriod.doubleValue()); + dueAmount = dueAmount.add(BigDecimal.valueOf(totalAmountToBePaidTillDate.doubleValue() - account.getTotalDepositAmount().doubleValue())); + + } + + return dueAmount; + } + + @Override + public SavingScheduleData retrieveSavingsAccountSchedule(Long accountId, CurrencyData currency) { + this.context.authenticatedUser(); + SavingAccountScheduleMapper mapper = new SavingAccountScheduleMapper(); + String sql = "Select " + mapper.savingScheduleSchema() +" where ss.saving_account_id =? order by ss.installment"; + List periods = this.jdbcTemplate.query(sql, mapper, new Object[]{accountId}); + BigDecimal cumulativeDepositDue = BigDecimal.ZERO; + for(SavingSchedulePeriodData data : periods){ + cumulativeDepositDue = cumulativeDepositDue.add(data.getDepositDue()).subtract(data.getDepositPaid()); + } + SavingScheduleData scheduleData = new SavingScheduleData(currency, cumulativeDepositDue, periods); + return scheduleData; + } + + public static final class SavingAccountScheduleMapper implements RowMapper { + + public String savingScheduleSchema(){ + return "ss.id as id, ss.saving_account_id as savingAccountId, ss.duedate as dueDate, ss.installment as installment, " + + " ss.deposit as deposit, ss.payment_date as paymentDate, ss.deposit_paid as depositPaid, " + + " ss.completed_derived as completedDerived FROM m_saving_schedule ss"; + } + + @Override + public SavingSchedulePeriodData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + + LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); + Integer installment = JdbcSupport.getInteger(rs, "installment"); + BigDecimal depositDue = rs.getBigDecimal("deposit"); + BigDecimal depositPaid = rs.getBigDecimal("depositPaid"); + + return new SavingSchedulePeriodData(installment, dueDate, depositDue, depositPaid); + } + + } + + @Override + public Collection retrieveSavingAccountsForLookUp() { + SavingAccountLookupMapper mapper = new SavingAccountLookupMapper(); + String sql = "select "+ mapper.schema(); + return this.jdbcTemplate.query(sql, mapper, new Object[] {}); + } + + private static final class SavingAccountLookupMapper implements RowMapper { + + public String schema(){ + return " sa.id as id from m_saving_account sa where sa.is_deleted=0 and sa.status_enum=300"; + } + + @Override + public SavingAccountForLookup mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + Long id = rs.getLong("id"); + return new SavingAccountForLookup(id); + } + + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformService.java index 1f5b46209..8c205360b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformService.java @@ -1,7 +1,15 @@ package org.mifosplatform.portfolio.savingsaccount.service; +import java.util.Collection; + import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.loanaccount.command.UndoStateTransitionCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountApprovalCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountWithdrawalCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingStateTransitionsCommand; +import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup; import org.springframework.security.access.prepost.PreAuthorize; public interface SavingAccountWritePlatformService { @@ -11,4 +19,28 @@ public interface SavingAccountWritePlatformService { @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'UPDATE_SAVINGSACCOUNT')") CommandProcessingResult updateSavingAccount(SavingAccountCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult rejectSavingApplication(SavingStateTransitionsCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult withdrawSavingApplication(SavingStateTransitionsCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult undoSavingAccountApproval(UndoStateTransitionCommand undoCommand); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult approveSavingAccount(SavingAccountApprovalCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult depositMoney(SavingAccountDepositCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult withdrawSavingAmount(SavingAccountWithdrawalCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult deleteSavingAccount(Long accountId); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER')") + CommandProcessingResult postInterest(Collection savingAccounts); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformServiceJpaRepositoryImpl.java index f9ab2915a..15a71011c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccount/service/SavingAccountWritePlatformServiceJpaRepositoryImpl.java @@ -1,14 +1,34 @@ package org.mifosplatform.portfolio.savingsaccount.service; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.bouncycastle.crypto.RuntimeCryptoException; +import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.client.domain.Note; import org.mifosplatform.portfolio.client.domain.NoteRepository; +import org.mifosplatform.portfolio.loanaccount.command.UndoStateTransitionCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountApprovalCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountApprovalCommandValidator; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommandValidator; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountDepositCommandValidator; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountStateTransitionCommandValidator; +import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountWithdrawalCommand; +import org.mifosplatform.portfolio.savingsaccount.command.SavingStateTransitionsCommand; +import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountForLookup; import org.mifosplatform.portfolio.savingsaccount.domain.SavingAccount; import org.mifosplatform.portfolio.savingsaccount.domain.SavingAccountRepository; import org.mifosplatform.portfolio.savingsaccount.exception.SavingAccountNotFoundException; +import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositAccountStatus; +import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositLifecycleStateMachine; +import org.mifosplatform.portfolio.savingsdepositaccount.domain.DepositLifecycleStateMachineImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -24,7 +44,6 @@ public class SavingAccountWritePlatformServiceJpaRepositoryImpl implements Savin private final PlatformSecurityContext context; private final SavingAccountRepository savingAccountRepository; private final SavingAccountAssembler savingAccountAssembler; - @SuppressWarnings("unused") private final NoteRepository noteRepository; @Autowired @@ -91,4 +110,174 @@ public class SavingAccountWritePlatformServiceJpaRepositoryImpl implements Savin throw new PlatformDataIntegrityException("error.msg.saving.account.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); } + + @Override + public CommandProcessingResult rejectSavingApplication(SavingStateTransitionsCommand command) { + + this.context.authenticatedUser(); + + SavingAccountStateTransitionCommandValidator validator = new SavingAccountStateTransitionCommandValidator(command); + validator.validate(); + + SavingAccount account = this.savingAccountRepository.findOne(command.getAccountId()); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(command.getAccountId()); + } + LocalDate eventDate = command.getEventDate(); + account.reject(eventDate, defaultDepositLifecycleStateMachine()); + this.savingAccountRepository.save(account); + + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.savingNote(account, noteText); + this.noteRepository.save(note); + } + + return new CommandProcessingResult(account.getId()); + } + + private DepositLifecycleStateMachine defaultDepositLifecycleStateMachine() { + List allowedDepositStatuses = Arrays.asList(DepositAccountStatus.values()); + return new DepositLifecycleStateMachineImpl(allowedDepositStatuses); + } + + @Override + public CommandProcessingResult withdrawSavingApplication(SavingStateTransitionsCommand command) { + this.context.authenticatedUser(); + + SavingAccountStateTransitionCommandValidator validator = new SavingAccountStateTransitionCommandValidator(command); + validator.validate(); + + SavingAccount account = this.savingAccountRepository.findOne(command.getAccountId()); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(command.getAccountId()); + } + LocalDate eventDate = command.getEventDate(); + account.withdrawnByApplicant(eventDate, defaultDepositLifecycleStateMachine()); + this.savingAccountRepository.save(account); + + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.savingNote(account, noteText); + this.noteRepository.save(note); + } + + return new CommandProcessingResult(account.getId()); + } + + @Override + public CommandProcessingResult undoSavingAccountApproval(UndoStateTransitionCommand command) { + + this.context.authenticatedUser(); + SavingAccount account = this.savingAccountRepository.findOne(command.getLoanId()); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(command.getLoanId()); + } + account.undoSavingAccountApproval(defaultDepositLifecycleStateMachine()); + this.savingAccountRepository.save(account); + + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.savingNote(account, noteText); + this.noteRepository.save(note); + } + + return new CommandProcessingResult(account.getId()); + } + + @Override + public CommandProcessingResult approveSavingAccount(SavingAccountApprovalCommand command) { + + this.context.authenticatedUser(); + SavingAccountApprovalCommandValidator validator = new SavingAccountApprovalCommandValidator(command); + validator.validate(); + SavingAccount account = this.savingAccountRepository.findOne(command.getAccountId()); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(command.getAccountId()); + } + LocalDate approvalDate = command.getApprovalDate(); + if (approvalDate.isBefore(account.projectedCommencementDate())) { + throw new RuntimeCryptoException("Date of approval cannot before application submission date"); + } + this.savingAccountAssembler.approveSavingAccount(command,account); + this.savingAccountRepository.save(account); + + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.savingNote(account, noteText); + this.noteRepository.save(note); + } + + return new CommandProcessingResult(account.getId()); + } + + @Override + public CommandProcessingResult depositMoney(SavingAccountDepositCommand command) { + + this.context.authenticatedUser(); + SavingAccountDepositCommandValidator validator = new SavingAccountDepositCommandValidator(command); + validator.validate(); + + SavingAccount account = this.savingAccountRepository.findOne(command.getAccountId()); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(command.getAccountId()); + } + + if (account.isActive()) + account.depositMoney(command); + this.savingAccountRepository.save(account); + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.savingNote(account, noteText); + this.noteRepository.save(note); + } + return new CommandProcessingResult(account.getId()); + } + + @Override + public CommandProcessingResult withdrawSavingAmount(SavingAccountWithdrawalCommand command) { + + this.context.authenticatedUser(); + SavingAccount account = this.savingAccountRepository.findOne(command.getAccountId()); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(command.getAccountId()); + } + if (account.isActive()) + this.savingAccountAssembler.withdrawSavingAccountMoney(command,account); + this.savingAccountRepository.save(account); + String noteText = command.getNote(); + if (StringUtils.isNotBlank(noteText)) { + Note note = Note.savingNote(account, noteText); + this.noteRepository.save(note); + } + return new CommandProcessingResult(account.getId()); + } + + @Override + public CommandProcessingResult deleteSavingAccount(Long accountId) { + this.context.authenticatedUser(); + SavingAccount account = this.savingAccountRepository.findOne(accountId); + if (account == null || account.isDeleted()) { + throw new SavingAccountNotFoundException(accountId); + } + account.delete(); + this.savingAccountRepository.save(account); + return new CommandProcessingResult(accountId); + } + + @Override + public CommandProcessingResult postInterest(Collection savingAccounts) { + this.context.authenticatedUser(); + try { + for(SavingAccountForLookup accountForLookup : savingAccounts ){ + SavingAccount account = this.savingAccountRepository.findOne(accountForLookup.getId()); + if (account == null || account.isDeleted()) { throw new SavingAccountNotFoundException(accountForLookup.getId()); } + this.savingAccountAssembler.postInterest(account); + this.savingAccountRepository.save(account); + } + return new CommandProcessingResult(new Long(savingAccounts.size())); + } catch (Exception e) { + return new CommandProcessingResult(Long.valueOf(-1)); + } + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/api/SavingProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/api/SavingProductsApiResource.java index 57fd0e770..074c35f45 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/api/SavingProductsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/api/SavingProductsApiResource.java @@ -173,7 +173,7 @@ public class SavingProductsApiResource { "savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions")); List currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); - EnumOptionData reccuring = SavingProductEnumerations.savingProductType(SavingProductType.RECCURING); + EnumOptionData reccuring = SavingProductEnumerations.savingProductType(SavingProductType.RECURRING); EnumOptionData regular = SavingProductEnumerations.savingProductType(SavingProductType.REGULAR); List savingsProductTypeOptions = Arrays.asList(reccuring, regular); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/data/SavingProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/data/SavingProductData.java index 26a6eebc8..2d2f8698e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/data/SavingProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/data/SavingProductData.java @@ -64,7 +64,7 @@ public class SavingProductData implements Serializable { this.currency = new CurrencyData("USD", "US Dollar", 2, "$", "currency.USD"); this.digitsAfterDecimal = Integer.valueOf(2); this.savingsDepositAmount = BigDecimal.ZERO; - this.savingProductType = SavingProductEnumerations.savingProductType(SavingProductType.RECCURING); + this.savingProductType = SavingProductEnumerations.savingProductType(SavingProductType.RECURRING); this.tenureType = SavingProductEnumerations.tenureTypeEnum(TenureTypeEnum.FIXED_PERIOD); this.tenure = Integer.valueOf(12); this.savingFrequencyType = SavingProductEnumerations.interestFrequencyType(SavingFrequencyType.MONTHLY); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingInterestCalculationMethod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingInterestCalculationMethod.java index 00fda71f0..406a72ce2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingInterestCalculationMethod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingInterestCalculationMethod.java @@ -40,4 +40,12 @@ public enum SavingInterestCalculationMethod { } return savingInterestCalculationMethod; } + + public boolean isAverageBalance() { + return this.value.equals(SavingInterestCalculationMethod.AVERAGEBAL.value); + } + + public boolean isMonthlyCollection() { + return this.value.equals(SavingInterestCalculationMethod.MONTHLYCOLLECTION.value); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductRelatedDetail.java index 243775d84..f257c3104 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductRelatedDetail.java @@ -35,22 +35,22 @@ public class SavingProductRelatedDetail { private Integer depositEvery; @Column(name = "savings_product_type", nullable = false) - private SavingProductType savingProductType; + private Integer savingProductType; @Column(name = "tenure_type", nullable = false) - private TenureTypeEnum tenureType; + private Integer tenureType; @Column(name = "tenure", nullable = false) private Integer tenure; @Column(name = "frequency", nullable = false) - private SavingFrequencyType frequency; + private Integer frequency; @Column(name = "interest_type", nullable = false) - private SavingsInterestType interestType; + private Integer interestType; @Column(name = "interest_calculation_method") - private SavingInterestCalculationMethod interestCalculationMethod; + private Integer interestCalculationMethod; @Column(name = "min_bal_for_withdrawal", scale = 6, precision = 19, nullable = false) private BigDecimal minimumBalanceForWithdrawal; @@ -65,7 +65,7 @@ public class SavingProductRelatedDetail { private Integer lockinPeriod; @Column(name = "lock_in_period_type", nullable = false) - private PeriodFrequencyType lockinPeriodType; + private Integer lockinPeriodType; public SavingProductRelatedDetail() { this.interestRate = null; @@ -84,17 +84,17 @@ public class SavingProductRelatedDetail { this.maxInterestRate = maxInterestRate; this.savingsDepositAmount = savingsDepositAmount; this.depositEvery=depositEvery; - this.savingProductType = savingProductType; - this.tenureType = tenureType; + this.savingProductType = savingProductType.getValue(); + this.tenureType = tenureType.getValue(); this.tenure = tenure; - this.frequency = frequency; - this.interestType = interestType; - this.interestCalculationMethod = interestCalculationMethod; + this.frequency = frequency.getValue(); + this.interestType = interestType.getValue(); + this.interestCalculationMethod = interestCalculationMethod.getValue(); this.minimumBalanceForWithdrawal = minimumBalanceForWithdrawal; this.isPartialDepositAllowed = isPartialDepositAllowed; this.isLockinPeriodAllowed = isLockinPeriodAllowed; this.lockinPeriod = lockinPeriod; - this.lockinPeriodType = lockinPeriodType; + this.lockinPeriodType = lockinPeriodType.getValue(); } public MonetaryCurrency getCurrency() { @@ -137,11 +137,11 @@ public class SavingProductRelatedDetail { } if (command.isSavingProductTypeChanged()) { - this.savingProductType = SavingProductType.fromInt(command.getSavingProductType()); + this.savingProductType = SavingProductType.fromInt(command.getSavingProductType()).getValue(); } if (command.isTenureTypeChanged()) { - this.tenureType = TenureTypeEnum.fromInt(command.getTenureType()); + this.tenureType = TenureTypeEnum.fromInt(command.getTenureType()).getValue(); } if (command.isTenureChanged()) { @@ -149,15 +149,15 @@ public class SavingProductRelatedDetail { } if (command.isFrequencyChanged()) { - this.frequency = SavingFrequencyType.fromInt(command.getFrequency()); + this.frequency = SavingFrequencyType.fromInt(command.getFrequency()).getValue(); } if (command.isInterestTypeChanged()) { - this.interestType = SavingsInterestType.fromInt(command.getInterestType()); + this.interestType = SavingsInterestType.fromInt(command.getInterestType()).getValue(); } if (command.isInterestCalculationMethodChanged()) { - this.interestCalculationMethod = SavingInterestCalculationMethod.fromInt(command.getInterestCalculationMethod()); + this.interestCalculationMethod = SavingInterestCalculationMethod.fromInt(command.getInterestCalculationMethod()).getValue(); } if (command.isMinimumBalanceForWithdrawalChanged()) { @@ -177,7 +177,7 @@ public class SavingProductRelatedDetail { } if (command.isLockinPeriodTypeChanged()) { - this.lockinPeriodType = PeriodFrequencyType.fromInt(command.getLockinPeriodType()); + this.lockinPeriodType = PeriodFrequencyType.fromInt(command.getLockinPeriodType()).getValue(); } } @@ -189,11 +189,11 @@ public class SavingProductRelatedDetail { return this.depositEvery; } - public SavingProductType getSavingProductType() { + public Integer getSavingProductType() { return savingProductType; } - public TenureTypeEnum getTenureType() { + public Integer getTenureType() { return tenureType; } @@ -201,15 +201,15 @@ public class SavingProductRelatedDetail { return tenure; } - public SavingFrequencyType getFrequency() { + public Integer getFrequency() { return frequency; } - public SavingsInterestType getInterestType() { + public Integer getInterestType() { return interestType; } - public SavingInterestCalculationMethod getInterestCalculationMethod() { + public Integer getInterestCalculationMethod() { return interestCalculationMethod; } @@ -229,7 +229,7 @@ public class SavingProductRelatedDetail { return lockinPeriod; } - public PeriodFrequencyType getLockinPeriodType() { + public Integer getLockinPeriodType() { return lockinPeriodType; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductType.java index 22832f16a..7c57b5a3f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/domain/SavingProductType.java @@ -3,7 +3,7 @@ package org.mifosplatform.portfolio.savingsaccountproduct.domain; public enum SavingProductType { INVALID(0, "savingProductType.invalid"), // - RECCURING(1, "savingProductType.reccuring"), // + RECURRING(1, "savingProductType.reccuring"), // REGULAR(2, "savingProductType.regular"); private final Integer value; @@ -27,7 +27,7 @@ public enum SavingProductType { SavingProductType savingProductType = SavingProductType.INVALID; switch (frequency) { case 1: - savingProductType = SavingProductType.RECCURING; + savingProductType = SavingProductType.RECURRING; break; case 2: savingProductType = SavingProductType.REGULAR; @@ -40,7 +40,7 @@ public enum SavingProductType { } public boolean isReccuring() { - return this.value.equals(SavingProductType.RECCURING.getValue()); + return this.value.equals(SavingProductType.RECURRING.getValue()); } public boolean isRegular() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductEnumerations.java index 3daa0bb71..97c473f8c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductEnumerations.java @@ -167,9 +167,9 @@ public class SavingProductEnumerations { EnumOptionData optionData = null; switch (type) { // FIXME - Madhukar - spelling mistake on 'Recurring' - case RECCURING: - optionData = new EnumOptionData(SavingProductType.RECCURING.getValue().longValue(), codePrefix - + SavingProductType.RECCURING.getCode(), "Reccuring"); + case RECURRING: + optionData = new EnumOptionData(SavingProductType.RECURRING.getValue().longValue(), codePrefix + + SavingProductType.RECURRING.getCode(), "Recurring"); break; case REGULAR: optionData = new EnumOptionData(SavingProductType.REGULAR.getValue().longValue(), codePrefix @@ -186,4 +186,4 @@ public class SavingProductEnumerations { } return optionData; } -} \ No newline at end of file +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductWritePlatformServiceJpaRepositoryImpl.java index 3460d2d3a..f435d2403 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsaccountproduct/service/SavingProductWritePlatformServiceJpaRepositoryImpl.java @@ -44,9 +44,18 @@ public class SavingProductWritePlatformServiceJpaRepositoryImpl implements Savin SavingFrequencyType savingFrequencyType = SavingFrequencyType.fromInt(command.getFrequency()); SavingsInterestType interestType = SavingsInterestType.fromInt(command.getInterestType()); PeriodFrequencyType lockinPeriodType = PeriodFrequencyType.fromInt(command.getLockinPeriodType()); - SavingInterestCalculationMethod savingInterestCalculationMethod = SavingInterestCalculationMethod.fromInt(command - .getInterestCalculationMethod()); + SavingInterestCalculationMethod savingInterestCalculationMethod = SavingInterestCalculationMethod.fromInt(command.getInterestCalculationMethod()); MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal()); + + if ( savingProductType.equals(SavingProductType.INVALID) || + tenureType.equals(TenureTypeEnum.INVALID) || + savingFrequencyType.equals(SavingFrequencyType.INVALID) || + interestType.equals(SavingsInterestType.INVALID) || + lockinPeriodType.equals(PeriodFrequencyType.INVALID)|| + savingInterestCalculationMethod.equals(SavingInterestCalculationMethod.INVALID) + ) { + throw new RuntimeException("Please select a valid types"); + } SavingProduct product = new SavingProduct(command.getName(), command.getDescription(), currency, command.getInterestRate(), command.getMinInterestRate(), command.getMaxInterestRate(), command.getSavingsDepositAmount(),command.getDepositEvery(), savingProductType, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccountTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccountTransactionType.java index 0b09e76b5..ec05c5574 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccountTransactionType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccountTransactionType.java @@ -58,4 +58,12 @@ public enum DepositAccountTransactionType { return depositTransactionType; } + public boolean isDeposit() { + return this.value.equals(DepositAccountTransactionType.DEPOSIT.value); + } + + public boolean isWithdraw() { + return this.value.equals(DepositAccountTransactionType.WITHDRAW.value); + } + }