From 1fdf72679a11bb47501d321af3c4f91141fb7268 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 6 Aug 2013 12:13:52 +0530 Subject: [PATCH] added modifications for creating group and JLG savings account --- api-docs/apiLive.htm | 245 ++++++++++++++++++ .../V94__added_savings_accont type.sql | 2 + .../data/SavingsAccountSummaryData.java | 5 +- ...sReadPlatformServiceJpaRepositoryImpl.java | 8 +- .../group/api/GroupsApiResource.java | 2 +- .../portfolio/loanaccount/domain/Loan.java | 3 +- .../savings/domain/SavingsAccount.java | 37 ++- .../domain/SavingsAccountAssembler.java | 21 +- 8 files changed, 297 insertions(+), 26 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V94__added_savings_accont type.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index ef2dc2294..f982202d0 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -567,6 +567,15 @@ + + + groups/{groupId}/accounts + + Retrive Group accounts summary + + + + @@ -3888,6 +3897,233 @@ GET https://DomainName/mifosng-provider/api/v1/groups/1?associations=clientMembe + +   +
+
+

Retrieve Group accounts overview

+

+ An example of how a loan portfolio summary can be provided. This + is requested in a specific use case of the reference application.
It is quite reasonable to add resources like this to simplify User Interface development. +

+

Example Requests:

+
groups/1/accounts
+
+
+
groups/1/accounts?fields=loanAccounts,savingsAccounts,memberLoanAccounts,
memberSavingsAccounts
+
+
+ +GET https://DomainName/api/v1/groups/{groupId}/accounts + + +{ + "loanAccounts": [ + { + "id": 3, + "accountNo": "000000003", + "productId": 3, + "productName": "daily product", + "status": { + "id": 100, + "code": "loanStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "pendingApproval": true, + "waitingForDisbursal": false, + "active": false, + "closedObligationsMet": false, + "closedWrittenOff": false, + "closedRescheduled": false, + "closed": false, + "overpaid": false + }, + "loanType": { + "id": 2, + "code": "accountType.group", + "value": "Group" + } + } + ], + "savingsAccounts": [ + { + "id": 9, + "accountNo": "000000009", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 100, + "code": "savingsAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 2, + "code": "accountType.group", + "value": "Group" + } + }, + { + "id": 4, + "accountNo": "000000004", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 2, + "code": "accountType.group", + "value": "Group" + } + } + ], + "memberLoanAccounts": [ + { + "id": 4, + "accountNo": "000000004", + "productId": 1, + "productName": "testLoan", + "status": { + "id": 200, + "code": "loanStatusType.approved", + "value": "Approved", + "pendingApproval": false, + "waitingForDisbursal": true, + "active": false, + "closedObligationsMet": false, + "closedWrittenOff": false, + "closedRescheduled": false, + "closed": false, + "overpaid": false + }, + "loanType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + }, + { + "id": 7, + "accountNo": "000000007", + "productId": 2, + "productName": "weekly product", + "status": { + "id": 100, + "code": "loanStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "pendingApproval": true, + "waitingForDisbursal": false, + "active": false, + "closedObligationsMet": false, + "closedWrittenOff": false, + "closedRescheduled": false, + "closed": false, + "overpaid": false + }, + "loanType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + } + ], + "memberSavingsAccounts": [ + { + "id": 3, + "accountNo": "000000003", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 100, + "code": "savingsAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + }, + { + "id": 10, + "accountNo": "000000010", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 100, + "code": "savingsAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + } + ] +} + +
+
+ +  
@@ -17110,6 +17346,15 @@ GET https://DomainName/api/v1/search?query=000000111 + + + groups/{groupId}/accounts + + Retrive Group accounts summary + + + + Loan Product loanproducts diff --git a/mifosng-db/migrations/core_db/V94__added_savings_accont type.sql b/mifosng-db/migrations/core_db/V94__added_savings_accont type.sql new file mode 100755 index 000000000..87985e710 --- /dev/null +++ b/mifosng-db/migrations/core_db/V94__added_savings_accont type.sql @@ -0,0 +1,2 @@ +ALTER TABLE `m_savings_account` + ADD COLUMN `account_type_enum` SMALLINT(5) NOT NULL DEFAULT '1' AFTER `status_enum`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java index dc5cde49b..ad8fee7fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java @@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.accountdetails.data; import java.math.BigDecimal; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; @@ -24,10 +25,11 @@ public class SavingsAccountSummaryData { private final SavingsAccountStatusEnumData status; private final CurrencyData currency; private final BigDecimal accountBalance; + private final EnumOptionData accountType; public SavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, final String productName, final SavingsAccountStatusEnumData status, final CurrencyData currency, - final BigDecimal accountBalance) { + final BigDecimal accountBalance, final EnumOptionData accountType) { this.id = id; this.accountNo = accountNo; this.externalId = externalId; @@ -36,5 +38,6 @@ public class SavingsAccountSummaryData { this.status = status; this.currency = currency; this.accountBalance = accountBalance; + this.accountType = accountType; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index 3e99e27a5..f707853be 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -63,7 +63,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou List memberLoanAccounts = retrieveLoanAccountDetails(loanWhereClauseForMembers, new Object[] { groupId }); List memberSavingsAccounts = retrieveAccountDetails(savingswhereClauseForMembers, new Object[] { groupId }); - return new AccountSummaryCollectionData(groupLoanAccounts, groupSavingsAccounts,memberLoanAccounts, memberSavingsAccounts); + return new AccountSummaryCollectionData(groupLoanAccounts, groupSavingsAccounts, memberLoanAccounts, memberSavingsAccounts); } @Override @@ -105,6 +105,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou public SavingsAccountSummaryDataMapper() { StringBuilder accountsSummary = new StringBuilder(); accountsSummary.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, sa.status_enum as statusEnum, "); + accountsSummary.append("sa.account_type_enum as accountType, "); accountsSummary.append("sa.account_balance_derived as accountBalance, "); accountsSummary.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); @@ -132,6 +133,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum"); final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accountBalance"); final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusId); + final Integer accountType = JdbcSupport.getInteger(rs, "accountType"); + final EnumOptionData accountTypeData = AccountEnumerations.loanType(accountType); final String currencyCode = rs.getString("currencyCode"); final String currencyName = rs.getString("currencyName"); @@ -141,7 +144,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, currencyNameCode); - return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance); + return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance, + accountTypeData); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 3e008d920..5ccd9f420 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -322,7 +322,7 @@ public class GroupsApiResource { @Path("{groupId}/accounts") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveLoans(@PathParam("groupId") final Long groupId, @Context final UriInfo uriInfo) { + public String retrieveAccounts(@PathParam("groupId") final Long groupId, @Context final UriInfo uriInfo) { this.context.authenticatedUser().validateHasReadPermission("GROUP"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 2664268a2..f37368d2d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1108,7 +1108,8 @@ public class Loan extends AbstractPersistable { if (client != null && client.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, submittedOn); - } else if (group != null && group.isActivatedAfter(submittedOn)) { + } + if (group != null && group.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 2691d21fc..3b4e39e2a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -58,6 +58,7 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType; @@ -105,6 +106,10 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "status_enum", nullable = false) private Integer status; + @SuppressWarnings("unused") + @Column(name = "account_type_enum", nullable = false) + private Integer accountType; + @Temporal(TemporalType.DATE) @Column(name = "submittedon_date", nullable = true) private Date submittedOnDate; @@ -254,8 +259,9 @@ public class SavingsAccount extends AbstractPersistable { } public static SavingsAccount createNewApplicationForSubmittal(final Client client, final Group group, final SavingsProduct product, - final Staff fieldOfficer, final String accountNo, final String externalId, final LocalDate submittedOnDate, - final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final Staff fieldOfficer, final String accountNo, final String externalId, final AccountType accountType, + final LocalDate submittedOnDate, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, @@ -263,15 +269,16 @@ public class SavingsAccount extends AbstractPersistable { final MonthDay annualFeeOnMonthDay) { final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; - return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, submittedOnDate, interestRate, - interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, - minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, - annualFeeAmount, annualFeeOnMonthDay); + return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, + interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, + withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay); } private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, - final String accountNo, final String externalId, final SavingsAccountStatusType status, final LocalDate submittedOnDate, - final BigDecimal nominalAnnualInterestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType, + final LocalDate submittedOnDate, final BigDecimal nominalAnnualInterestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, @@ -290,6 +297,7 @@ public class SavingsAccount extends AbstractPersistable { this.currency = product.currency(); this.externalId = externalId; this.status = status.getValue(); + this.accountType = accountType.getValue(); this.submittedOnDate = submittedOnDate.toDate(); this.nominalAnnualInterestRate = nominalAnnualInterestRate; this.interestCompoundingPeriodType = interestCompoundingPeriodType.getValue(); @@ -1612,14 +1620,19 @@ public class SavingsAccount extends AbstractPersistable { updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); } - if (this.client.isActivatedAfter(activationDate)) { - + if (this.client != null && this.client.isActivatedAfter(activationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); final String dateAsString = formatter.print(client.getActivationLocalDate()); - baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date"); - + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (this.group != null && this.group.isActivatedAfter(activationDate)) { + final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); + final String dateAsString = formatter.print(client.getActivationLocalDate()); + baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) + .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.group.activation.date"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index bc8ede232..0ede17f91 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -34,6 +34,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.group.domain.Group; @@ -67,8 +68,7 @@ public class SavingsAccountAssembler { public SavingsAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository, final StaffRepositoryWrapper staffRepository, final SavingsProductRepository savingProductRepository, - final SavingsAccountRepositoryWrapper savingsAccountRepository, - final FromJsonHelper fromApiJsonHelper) { + final SavingsAccountRepositoryWrapper savingsAccountRepository, final FromJsonHelper fromApiJsonHelper) { this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper; this.clientRepository = clientRepository; this.groupRepository = groupRepository; @@ -97,19 +97,22 @@ public class SavingsAccountAssembler { Client client = null; Group group = null; Staff fieldOfficer = null; - + AccountType accountType = AccountType.INVALID; final Long clientId = fromApiJsonHelper.extractLongNamed(clientIdParamName, element); if (clientId != null) { client = this.clientRepository.findOneWithNotFoundDetection(clientId); + accountType = AccountType.INDIVIDUAL; } final Long groupId = fromApiJsonHelper.extractLongNamed(groupIdParamName, element); if (groupId != null) { group = this.groupRepository.findOneWithNotFoundDetection(groupId); + accountType = AccountType.GROUP; } - - if(group != null && client != null){ + + if (group != null && client != null) { if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); } + accountType = AccountType.JLG; } final Long fieldOfficerId = fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element); @@ -215,10 +218,10 @@ public class SavingsAccountAssembler { monthDayOfAnnualFee = product.monthDayOfAnnualFee(); } - final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, externalId, - submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, - interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, - withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, monthDayOfAnnualFee); + final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, + externalId, accountType, submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, monthDayOfAnnualFee); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant());