diff --git a/mifosng-db/patches/patch-0001-add-group_id-to-m_loan.sql b/mifosng-db/patches/patch-0001-add-group_id-to-m_loan.sql new file mode 100644 index 000000000..a1596be8d --- /dev/null +++ b/mifosng-db/patches/patch-0001-add-group_id-to-m_loan.sql @@ -0,0 +1,3 @@ +alter table m_loan add column group_id bigint(20) after client_id; +alter table m_loan modify client_id bigint(20); +alter table m_loan add constraint foreign key (`group_id`) references `m_group` (`id`); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/GroupsApiResource.java index db3e5b0f2..1cc31a1a5 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/GroupsApiResource.java @@ -20,8 +20,10 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.mifosng.platform.api.commands.GroupCommand; +import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData; import org.mifosng.platform.api.data.ClientLookup; import org.mifosng.platform.api.data.EntityIdentifier; +import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData; import org.mifosng.platform.api.data.GroupData; import org.mifosng.platform.api.data.OfficeLookup; import org.mifosng.platform.api.infrastructure.ApiDataConversionService; @@ -167,4 +169,28 @@ public class GroupsApiResource { return Response.ok().entity(entityIdentifier).build(); } + + @GET + @Path("{groupId}/loans") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveGroupAccount(@PathParam("groupId") final Long groupId, + @Context final UriInfo uriInfo) { + + Set typicalResponseParameters = new HashSet( + Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans", "openLoans", "closedLoans", + "anyLoanCount", "pendingApprovalLoanCount", "awaitingDisbursalLoanCount", + "activeLoanCount", "closedLoanCount") + ); + + Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); + if (responseParameters.isEmpty()) { + responseParameters.addAll(typicalResponseParameters); + } + boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + + GroupAccountSummaryCollectionData clientAccount = this.groupReadPlatformService.retrieveGroupAccountDetails(groupId); + + return this.apiJsonSerializerService.serializeGroupAccountSummaryCollectionDataToJson(prettyPrint, responseParameters, clientAccount); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java index 2998b8275..688733289 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java @@ -96,7 +96,7 @@ public class LoansApiResource { private StaffReadPlatformService staffReadPlatformService; private final static Set typicalResponseParameters = new HashSet( - Arrays.asList("id", "externalId", "clientId", "clientName", "fundId", "fundName", + Arrays.asList("id", "externalId", "clientId", "groupId", "clientName", "groupName", "fundId", "fundName", "loanProductId", "loanProductName", "loanProductDescription", "loanOfficerName", "loanOfficerId", "currency", "principal", @@ -118,6 +118,7 @@ public class LoansApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String retrieveDetailsForNewLoanApplicationStepOne( @QueryParam("clientId") final Long clientId, + @QueryParam("groupId") final Long groupId, @QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) { @@ -145,17 +146,26 @@ public class LoansApiResource { Collection fundOptions = this.fundReadPlatformService.retrieveAllFunds(); Collection repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies(); - Collection chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(); - ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate(); + Collection chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(); + ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate(); + + LoanBasicDetailsData loanBasicDetails; + Long officeId; + + if ( clientId != null ){ + loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId); + officeId = loanBasicDetails.getClientOfficeId(); + } else { + loanBasicDetails = this.loanReadPlatformService.retrieveGroupAndProductDetails(groupId, productId); + officeId = loanBasicDetails.getGroupOfficeId(); + } - LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId); - final boolean convenienceDataRequired = false; Collection charges = loanBasicDetails.getCharges(); - - Collection allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.getClientOfficeId()); - - final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges, + + Collection allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId); + + final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanApplicationCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanApplicationCommand.java index 7ccc9195f..81f3e4b3a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanApplicationCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanApplicationCommand.java @@ -12,6 +12,7 @@ public class LoanApplicationCommand { private final Long loanId; private final Long clientId; + private final Long groupId; private final Long productId; private final String externalId; @@ -48,7 +49,7 @@ public class LoanApplicationCommand { public LoanApplicationCommand( final Set modifiedParameters, final Long loanId, - final Long clientId, final Long productId, final String externalId, + final Long clientId, final Long groupId, final Long productId, final String externalId, final Long fundId, final Long transactionProcessingStrategyId, final LocalDate submittedOnDate, final String submittedOnNote, final LocalDate expectedDisbursementDate, @@ -66,6 +67,7 @@ public class LoanApplicationCommand { this.modifiedParameters = modifiedParameters; this.loanId = loanId; this.clientId = clientId; + this.groupId = groupId; this.productId = productId; this.externalId = externalId; this.fundId = fundId; @@ -112,6 +114,10 @@ public class LoanApplicationCommand { return clientId; } + public Long getGroupId() { + return groupId; + } + public Long getProductId() { return productId; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupAccountSummaryCollectionData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupAccountSummaryCollectionData.java new file mode 100644 index 000000000..22a901c41 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupAccountSummaryCollectionData.java @@ -0,0 +1,73 @@ +package org.mifosng.platform.api.data; + + +import java.util.List; + +public class GroupAccountSummaryCollectionData { + + private final int anyLoanCount; + private final int pendingApprovalLoanCount; + private final List pendingApprovalLoans; + private final int awaitingDisbursalLoanCount; + private final List awaitingDisbursalLoans; + private final int activeLoanCount; + private final List openLoans; + private final int closedLoanCount; + private final List closedLoans; + + + public GroupAccountSummaryCollectionData(List pendingApprovalLoans, + List awaitingDisbursalLoans, + List openLoans, + List closedLoans) { + this.pendingApprovalLoans = pendingApprovalLoans; + this.pendingApprovalLoanCount = pendingApprovalLoans.size(); + + this.awaitingDisbursalLoans = awaitingDisbursalLoans; + this.awaitingDisbursalLoanCount = awaitingDisbursalLoans.size(); + + this.openLoans = openLoans; + this.activeLoanCount = openLoans.size(); + + this.closedLoans = closedLoans; + this.closedLoanCount = closedLoans.size(); + + this.anyLoanCount = pendingApprovalLoanCount + awaitingDisbursalLoanCount + activeLoanCount + closedLoanCount; + } + + public int getAnyLoanCount() { + return anyLoanCount; + } + + public int getPendingApprovalLoanCount() { + return pendingApprovalLoanCount; + } + + public List getPendingApprovalLoans() { + return pendingApprovalLoans; + } + + public int getAwaitingDisbursalLoanCount() { + return awaitingDisbursalLoanCount; + } + + public List getAwaitingDisbursalLoans() { + return awaitingDisbursalLoans; + } + + public int getActiveLoanCount() { + return activeLoanCount; + } + + public List getOpenLoans() { + return openLoans; + } + + public int getClosedLoanCount() { + return closedLoanCount; + } + + public List getClosedLoans() { + return closedLoans; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupAccountSummaryData.java new file mode 100644 index 000000000..8382a81e5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupAccountSummaryData.java @@ -0,0 +1,44 @@ +package org.mifosng.platform.api.data; + +/** + * Immutable data object for group loan accounts. + */ +public class GroupAccountSummaryData { + + private final Long id; + private final String externalId; + private final Long productId; + private final String productName; + private final Integer accountStatusId; + + public GroupAccountSummaryData(Long id, String externalId, + Long productId, + String productName, + Integer accountStatusId) { + this.id = id; + this.externalId = externalId; + this.productId = productId; + this.productName = productName; + this.accountStatusId = accountStatusId; + } + + public Long getId() { + return id; + } + + public String getExternalId() { + return externalId; + } + + public Long getProductId() { + return productId; + } + + public String getProductName() { + return productName; + } + + public Integer getAccountStatusId() { + return accountStatusId; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupData.java index c4ed19e55..1cfb62b51 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GroupData.java @@ -42,7 +42,15 @@ public class GroupData { return name; } - public String getExternalId() { + public Long getOfficeId() { + return officeId; + } + + public String getOfficeName() { + return officeName; + } + + public String getExternalId() { return externalId; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java index c2c30453e..fd6e22237 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java @@ -17,6 +17,8 @@ public class LoanAccountData { private final String externalId; private final Long clientId; private final String clientName; + private final Long groupId; + private final String groupName; private final Long loanProductId; private final String loanProductName; private final String loanProductDescription; @@ -134,6 +136,8 @@ public class LoanAccountData { this.externalId = basicDetails.getExternalId(); this.clientId = basicDetails.getClientId(); this.clientName = basicDetails.getClientName(); + this.groupId = basicDetails.getGroupId(); + this.groupName = basicDetails.getGroupName(); this.loanProductId = basicDetails.getLoanProductId(); this.loanProductName = basicDetails.getLoanProductName(); this.loanProductDescription = basicDetails.getLoanProductDescription(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java index 81b67ef11..59dc12783 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java @@ -19,6 +19,9 @@ public class LoanBasicDetailsData { private final Long clientId; private final Long clientOfficeId; private final String clientName; + private final Long groupId; + private final Long groupOfficeId; + private final String groupName; private final Long loanProductId; private final String loanProductName; private final String loanProductDescription; @@ -57,11 +60,17 @@ public class LoanBasicDetailsData { private final BigDecimal totalDisbursementCharges; private final Collection charges; - public static LoanBasicDetailsData populateForNewLoanCreation(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate, - final Long clientOfficeId) { - return new LoanBasicDetailsData(clientId, clientName, expectedDisbursementDate, clientOfficeId); + public static LoanBasicDetailsData populateForNewIndividualClientLoanCreation(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate, + final Long clientOfficeId) { + return new LoanBasicDetailsData(clientId, clientName, clientOfficeId, null, null, null, expectedDisbursementDate); } - + + public static LoanBasicDetailsData populateForNewGroupLoanCreation(final Long groupId, final String groupName, + final LocalDate expectedDisbursementDate, + final Long groupOfficeId){ + return new LoanBasicDetailsData(null, null, null, groupId, groupName, groupOfficeId, expectedDisbursementDate); + } + public static LoanBasicDetailsData populateForNewLoanCreation(final LoanBasicDetailsData loanAccount, final LoanProductData product) { final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery(); @@ -75,7 +84,8 @@ public class LoanBasicDetailsData { return new LoanBasicDetailsData( loanAccount.clientId, loanAccount.clientName, loanAccount.clientOfficeId, - product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), + loanAccount.groupId, loanAccount.groupName, loanAccount.groupOfficeId, + product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), product.getCurrency(), product.getPrincipal(), product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(), product.getRepaymentEvery(), product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(), product.getAmortizationType(), @@ -85,6 +95,7 @@ public class LoanBasicDetailsData { private LoanBasicDetailsData( final Long clientId, final String clientName, final Long clientOfficeId, + final Long groupId, final String groupName, final Long groupOfficeId, final Long productId, final String productName, final String productDescription, final Long fundId, final String fundName, final CurrencyData currency, final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer termFrequency, @@ -104,6 +115,9 @@ public class LoanBasicDetailsData { this.clientId = clientId; this.clientName = clientName; this.clientOfficeId = clientOfficeId; + this.groupId = groupId; + this.groupName = groupName; + this.groupOfficeId = groupOfficeId; this.loanProductId = productId; this.loanProductName = productName; this.loanProductDescription= productDescription; @@ -151,12 +165,17 @@ public class LoanBasicDetailsData { this.totalDisbursementCharges = BigDecimal.ZERO; } - private LoanBasicDetailsData(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate, final Long clientOfficeId) { + private LoanBasicDetailsData(final Long clientId, final String clientName, final Long clientOfficeId, + final Long groupId, final String groupName, final Long groupOfficeId, + final LocalDate expectedDisbursementDate) { this.id = null; this.externalId = null; this.clientId = clientId; this.clientName = clientName; this.clientOfficeId = clientOfficeId; + this.groupId = groupId; + this.groupName = groupName; + this.groupOfficeId = groupOfficeId; this.loanProductId = null; this.loanProductName = null; this.loanProductDescription= null; @@ -201,7 +220,11 @@ public class LoanBasicDetailsData { final Long id, final String externalId, final Long clientId, final String clientName, - final Long clientOfficeId, final Long loanProductId, + final Long clientOfficeId, + final Long groupId, + final String groupName, + final Long groupOfficeId, + final Long loanProductId, final String loanProductName, final String loanProductDescription, final Long fundId, String fundName, @@ -238,6 +261,9 @@ public class LoanBasicDetailsData { this.clientId = clientId; this.clientName = clientName; this.clientOfficeId = clientOfficeId; + this.groupId = groupId; + this.groupName = groupName; + this.groupOfficeId = groupOfficeId; this.loanProductId = loanProductId; this.loanProductName = loanProductName; this.loanProductDescription = loanProductDescription; @@ -383,6 +409,18 @@ public class LoanBasicDetailsData { return clientOfficeId; } + public Long getGroupId() { + return groupId; + } + + public Long getGroupOfficeId() { + return groupOfficeId; + } + + public String getGroupName() { + return groupName; + } + public Long getFundId() { return fundId; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 1d3548d81..bc00b0d31 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -444,7 +444,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { Map requestMap = gsonConverter.fromJson(json, typeOfMap); Set supportedParams = new HashSet( - Arrays.asList("clientId", "productId", "externalId", "fundId", "transactionProcessingStrategyId", + Arrays.asList("clientId", "groupId", "productId", "externalId", "fundId", "transactionProcessingStrategyId", "principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", @@ -461,6 +461,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { JsonParserHelper helper = new JsonParserHelper(); final Long clientId = helper.extractLongNamed("clientId", element, modifiedParameters); + final Long groupId = helper.extractLongNamed("groupId", element, modifiedParameters); final Long productId = helper.extractLongNamed("productId", element, modifiedParameters); final Long fundId = helper.extractLongNamed("fundId", element, modifiedParameters); final Long loanOfficerId = helper.extractLongNamed("loanOfficerId", element, modifiedParameters); @@ -514,7 +515,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { } return new LoanApplicationCommand(modifiedParameters, - resourceIdentifier, clientId, productId, externalId, fundId, transactionProcessingStrategyId, + resourceIdentifier, clientId, groupId, productId, externalId, fundId, transactionProcessingStrategyId, submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate, principal, interestRatePerPeriod, interestRateFrequencyType, interestType, interestCalculationPeriodType, diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java index 09c226b9e..a3d5a15e3 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java @@ -17,6 +17,7 @@ import org.mifosng.platform.api.data.DepositProductData; import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.FundData; import org.mifosng.platform.api.data.GenericResultsetData; +import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData; import org.mifosng.platform.api.data.GroupData; import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanChargeData; @@ -86,6 +87,8 @@ public interface ApiJsonSerializerService { String serializeClientAccountSummaryCollectionDataToJson(boolean prettyPrint, Set responseParameters, ClientAccountSummaryCollectionData clientAccount); + String serializeGroupAccountSummaryCollectionDataToJson(boolean prettyPrint, Set responseParameters, GroupAccountSummaryCollectionData groupAccount); + String serializeGroupDataToJson(boolean prettyPrint, Set responseParameters, Collection groups); String serializeGroupDataToJson(boolean prettyPrint, Set responseParameters, GroupData group); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java index 1671a945f..3d4debd19 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java @@ -19,6 +19,7 @@ import org.mifosng.platform.api.data.DepositProductData; import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.FundData; import org.mifosng.platform.api.data.GenericResultsetData; +import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData; import org.mifosng.platform.api.data.GroupData; import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanChargeData; @@ -131,6 +132,12 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ "rejectedDepositAccountsCount","rejectedDepositAccounts", "preclosedDepositAccountsCount","preclosedDepositAccounts")); + private static final Set GROUP_ACCOUNTS_DATA_PARAMETERS = new HashSet( + Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans", + "openLoans", "closedLoans", "anyLoanCount", + "pendingApprovalLoanCount", "awaitingDisbursalLoanCount", + "activeLoanCount", "closedLoanCount")); + private static final Set GROUP_DATA_PARAMETERS = new HashSet( Arrays.asList("id", "officeId", "name", "externalId", "clientMembers", "allowedClients", "allowedOffices")); @@ -145,7 +152,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ Arrays.asList("periods", "cumulativePrincipalDisbursed")); private static final Set LOAN_DATA_PARAMETERS = new HashSet( - Arrays.asList("id", "externalId", "clientId", "clientName", "fundId", "fundName", + Arrays.asList("id", "externalId", "clientId","groupId", "clientName", "groupName", "fundId", "fundName", "loanProductId", "loanProductName", "loanProductDescription", "currency", "principal", "inArrearsTolerance", "numberOfRepayments", @@ -468,6 +475,17 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ return helper.serializedJsonFrom(gsonDeserializer, clientAccount); } + @Override + public String serializeGroupAccountSummaryCollectionDataToJson( + final boolean prettyPrint, final Set responseParameters, + final GroupAccountSummaryCollectionData groupAccount) { + final Gson gsonDeserializer = helper + .createGsonBuilderWithParameterExclusionSerializationStrategy( + GROUP_ACCOUNTS_DATA_PARAMETERS, prettyPrint, + responseParameters); + return helper.serializedJsonFrom(gsonDeserializer, groupAccount); + } + @Override public String serializeGroupDataToJson(final boolean prettyPrint, final Set responseParameters, diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformService.java index b866d6fe9..309bcd638 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformService.java @@ -2,6 +2,7 @@ package org.mifosng.platform.group.service; import java.util.Collection; +import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData; import org.mifosng.platform.api.data.ClientLookup; import org.mifosng.platform.api.data.GroupData; @@ -14,4 +15,6 @@ public interface GroupReadPlatformService { GroupData retrieveNewGroupDetails(); Collection retrieveClientMembers(Long groupId); + + GroupAccountSummaryCollectionData retrieveGroupAccountDetails(Long groupId); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformServiceImpl.java index c6600f119..c0113586f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/group/service/GroupReadPlatformServiceImpl.java @@ -8,10 +8,14 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.mifosng.platform.api.data.ClientLookup; +import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData; +import org.mifosng.platform.api.data.GroupAccountSummaryData; import org.mifosng.platform.api.data.GroupData; import org.mifosng.platform.api.data.OfficeLookup; import org.mifosng.platform.client.service.ClientReadPlatformService; +import org.mifosng.platform.client.service.LoanStatusMapper; import org.mifosng.platform.exceptions.GroupNotFoundException; +import org.mifosng.platform.infrastructure.JdbcSupport; import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource; import org.mifosng.platform.organisation.service.OfficeReadPlatformService; import org.mifosng.platform.security.PlatformSecurityContext; @@ -132,5 +136,75 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { } } - + + @Override + public GroupAccountSummaryCollectionData retrieveGroupAccountDetails(Long groupId) { + try { + this.context.authenticatedUser(); + + // Check if group exists + retrieveGroup(groupId); + + List pendingApprovalLoans = new ArrayList(); + List awaitingDisbursalLoans = new ArrayList(); + List openLoans = new ArrayList(); + List closedLoans = new ArrayList(); + + GroupLoanAccountSummaryDataMapper rm = new GroupLoanAccountSummaryDataMapper(); + + String sql = "select " + rm.loanAccountSummarySchema() + " where l.group_id = ?"; + + List results = this.jdbcTemplate.query(sql, rm, new Object[] {groupId}); + if (results != null) { + for (GroupAccountSummaryData row : results) { + + LoanStatusMapper statusMapper = new LoanStatusMapper(row.getAccountStatusId()); + + if (statusMapper.isOpen()) { + openLoans.add(row); + } else if (statusMapper.isAwaitingDisbursal()) { + awaitingDisbursalLoans.add(row); + } else if (statusMapper.isPendingApproval()) { + pendingApprovalLoans.add(row); + } else { + closedLoans.add(row); + } + } + } + + return new GroupAccountSummaryCollectionData(pendingApprovalLoans, + awaitingDisbursalLoans, openLoans, closedLoans); + } catch (EmptyResultDataAccessException e) { + throw new GroupNotFoundException(groupId); + } + } + + private static final class GroupLoanAccountSummaryDataMapper implements + RowMapper { + + public String loanAccountSummarySchema() { + + StringBuilder accountsSummary = new StringBuilder("l.id as id, l.external_id as externalId,"); + accountsSummary + .append("l.product_id as productId, lp.name as productName,") + .append("l.loan_status_id as statusId ") + .append("from m_loan l ") + .append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id "); + + return accountsSummary.toString(); + } + + @Override + public GroupAccountSummaryData mapRow(final ResultSet rs, + @SuppressWarnings("unused") final int rowNum) throws SQLException { + + Long id = JdbcSupport.getLong(rs, "id"); + String externalId = rs.getString("externalId"); + Long productId = JdbcSupport.getLong(rs, "productId"); + String loanProductName = rs.getString("productName"); + Integer loanStatusId = JdbcSupport.getInteger(rs, "statusId"); + + return new GroupAccountSummaryData(id, externalId, productId, loanProductName, loanStatusId); + } + } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index 4b45cc4ca..e9eb1ebac 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -36,6 +36,7 @@ import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.exceptions.InvalidLoanStateTransitionException; import org.mifosng.platform.exceptions.InvalidLoanTransactionTypeException; import org.mifosng.platform.fund.domain.Fund; +import org.mifosng.platform.group.domain.Group; import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.staff.domain.Staff; import org.mifosng.platform.user.domain.AppUser; @@ -44,10 +45,14 @@ import org.mifosng.platform.user.domain.AppUser; @Table(name = "m_loan", uniqueConstraints = @UniqueConstraint(columnNames = { "external_id" })) public class Loan extends AbstractAuditableCustom { - @ManyToOne - @JoinColumn(name = "client_id", nullable = false) + @ManyToOne(optional = true) + @JoinColumn(name = "client_id") private Client client; + @ManyToOne(optional = true) + @JoinColumn(name = "group_id") + private Group group; + @ManyToOne @JoinColumn(name = "product_id") private LoanProduct loanProduct; @@ -170,8 +175,15 @@ public class Loan extends AbstractAuditableCustom { return new Loan(client, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null, loanCharges); } + public static Loan createNew(final Fund fund,final Staff loanOfficer, final LoanTransactionProcessingStrategy transactionProcessingStrategy, + final LoanProduct loanProduct, final Group group, + final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges) { + return new Loan(group, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null, loanCharges); + } + protected Loan() { this.client = null; + this.group = null; this.loanProduct = null; this.loanRepaymentScheduleDetail = null; this.charges = null; @@ -203,6 +215,32 @@ public class Loan extends AbstractAuditableCustom { this.interestRebateOwed = BigDecimal.ZERO; } + private Loan( + final Group group, Fund fund, Staff loanOfficer, + final LoanTransactionProcessingStrategy transactionProcessingStrategy, + final LoanProduct loanProduct, + final LoanProductRelatedDetail loanRepaymentScheduleDetail, + final LoanStatus loanStatus, final Set loanCharges) { + this.group = group; + this.fund = fund; + this.loanofficer = loanOfficer; + this.transactionProcessingStrategy = transactionProcessingStrategy; + this.loanProduct = loanProduct; + this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail; + if (loanStatus != null) { + this.loanStatus = loanStatus.getValue(); + } else { + this.loanStatus = null; + } + if (loanCharges != null && !loanCharges.isEmpty()) { + this.charges = associateChargesWithThisLoan(loanCharges); + this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement(); + } else { + this.charges = null; + } + this.interestRebateOwed = BigDecimal.ZERO; + } + private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() { Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO); @@ -227,7 +265,11 @@ public class Loan extends AbstractAuditableCustom { return this.client; } - public LoanProduct loanProduct() { + public Group getGroup() { + return group; + } + + public LoanProduct loanProduct() { return this.loanProduct; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanApplicationCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanApplicationCommandValidator.java index eda57dfcf..da1160b4a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanApplicationCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanApplicationCommandValidator.java @@ -26,7 +26,15 @@ public class LoanApplicationCommandValidator { // "The parameter loanSchedule cannot be empty.", "loanSchedule"); // dataValidationErrors.add(error); // } - + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan"); + + if (command.getGroupId() != null){ + baseDataValidator.reset().parameter("clientId").value(command.getClientId()).mustBeBlankWhenParameterProvided("groupId", command.getGroupId()).longGreaterThanZero(); + } + if (command.getClientId() != null){ + baseDataValidator.reset().parameter("groupId").value(command.getGroupId()).mustBeBlankWhenParameterProvided("clientId", command.getClientId()).longGreaterThanZero(); + } + if (command.getSubmittedOnDate() == null) { ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank", "The parameter submittedOnDate cannot be empty.", "submittedOnDate"); @@ -41,7 +49,6 @@ public class LoanApplicationCommandValidator { } if (command.getTransactionProcessingStrategyId() == null) { - DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan"); baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(command.getTransactionProcessingStrategyId()).notNull().inMinMaxRange(2, 2); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java index 90b46ae42..cf2b2e227 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java @@ -13,12 +13,15 @@ import org.mifosng.platform.client.domain.ClientRepository; import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.exceptions.ClientNotFoundException; import org.mifosng.platform.exceptions.FundNotFoundException; +import org.mifosng.platform.exceptions.GroupNotFoundException; import org.mifosng.platform.exceptions.LoanProductNotFoundException; import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException; import org.mifosng.platform.exceptions.StaffNotFoundException; import org.mifosng.platform.exceptions.StaffRoleException; import org.mifosng.platform.fund.domain.Fund; import org.mifosng.platform.fund.domain.FundRepository; +import org.mifosng.platform.group.domain.Group; +import org.mifosng.platform.group.domain.GroupRepository; import org.mifosng.platform.loan.domain.AmortizationMethod; import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine; import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod; @@ -45,6 +48,7 @@ public class LoanAssembler { private final LoanProductRepository loanProductRepository; private final ClientRepository clientRepository; + private final GroupRepository groupRepository; private final AprCalculator aprCalculator = new AprCalculator(); private final FundRepository fundRepository; private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository; @@ -56,6 +60,7 @@ public class LoanAssembler { public LoanAssembler( final LoanProductRepository loanProductRepository, final ClientRepository clientRepository, + final GroupRepository groupRepository, final FundRepository fundRepository, final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository, final StaffRepository staffRepository, @@ -63,6 +68,7 @@ public class LoanAssembler { final LoanChargeAssembler loanChargeAssembler) { this.loanProductRepository = loanProductRepository; this.clientRepository = clientRepository; + this.groupRepository = groupRepository; this.fundRepository = fundRepository; this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository; this.staffRepository = staffRepository; @@ -77,11 +83,20 @@ public class LoanAssembler { throw new LoanProductNotFoundException(command.getProductId()); } - Client client = this.clientRepository.findOne(command.getClientId()); - if (client == null || client.isDeleted()) { - throw new ClientNotFoundException(command.getClientId()); + Client client = null; + Group group = null; + if (command.getClientId() != null) { + client = this.clientRepository.findOne(command.getClientId()); + if (client == null || client.isDeleted()) { + throw new ClientNotFoundException(command.getClientId()); + } + } else { + group = this.groupRepository.findOne(command.getGroupId()); + if (group == null || group.isDeleted()) { + throw new GroupNotFoundException(command.getGroupId()); + } } - + MonetaryCurrency currency = loanProduct.getCurrency(); final Integer loanTermFrequency = command.getLoanTermFrequency(); final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType()); @@ -97,8 +112,14 @@ public class LoanAssembler { // optionally, see if charges are associated with loan on creation (through loan product or by being directly added) final Set loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), command.getPrincipal()); - - Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges); + + Loan loan; + if (client != null) { + loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges); + } else { + loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, group, loanRepaymentScheduleDetail, loanCharges); + } + loan.setExternalId(command.getExternalId()); final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand()); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java index 11ebd2777..696cd7496 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java @@ -25,6 +25,8 @@ public interface LoanReadPlatformService { LoanBasicDetailsData retrieveClientAndProductDetails(Long clientId, Long productId); + LoanBasicDetailsData retrieveGroupAndProductDetails(Long groupId, Long productId); + LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId); LoanTransactionData retrieveNewLoanWaiverDetails(Long loanId); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java index 66d47c830..6b0bb433a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java @@ -12,6 +12,7 @@ import org.mifosng.platform.api.data.ClientData; import org.mifosng.platform.api.data.CurrencyData; import org.mifosng.platform.api.data.DisbursementData; import org.mifosng.platform.api.data.EnumOptionData; +import org.mifosng.platform.api.data.GroupData; import org.mifosng.platform.api.data.LoanBasicDetailsData; import org.mifosng.platform.api.data.LoanChargeData; import org.mifosng.platform.api.data.LoanPermissionData; @@ -27,6 +28,7 @@ import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.exceptions.CurrencyNotFoundException; import org.mifosng.platform.exceptions.LoanNotFoundException; import org.mifosng.platform.exceptions.LoanTransactionNotFoundException; +import org.mifosng.platform.group.service.GroupReadPlatformService; import org.mifosng.platform.infrastructure.JdbcSupport; import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource; import org.mifosng.platform.loan.domain.Loan; @@ -52,6 +54,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private final ApplicationCurrencyRepository applicationCurrencyRepository; private final LoanProductReadPlatformService loanProductReadPlatformService; private final ClientReadPlatformService clientReadPlatformService; + private final GroupReadPlatformService groupReadPlatformService; private final LoanTransactionRepository loanTransactionRepository; @Autowired @@ -62,6 +65,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final ApplicationCurrencyRepository applicationCurrencyRepository, final LoanProductReadPlatformService loanProductReadPlatformService, final ClientReadPlatformService clientReadPlatformService, + final GroupReadPlatformService groupReadPlatformService, final TenantAwareRoutingDataSource dataSource) { this.context = context; this.loanRepository = loanRepository; @@ -69,6 +73,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { this.applicationCurrencyRepository = applicationCurrencyRepository; this.loanProductReadPlatformService = loanProductReadPlatformService; this.clientReadPlatformService = clientReadPlatformService; + this.groupReadPlatformService = groupReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); } @@ -210,7 +215,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final LocalDate expectedDisbursementDate = new LocalDate(); final ClientData clientAccount = this.clientReadPlatformService.retrieveIndividualClient(clientId); - LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(clientAccount.getId(), clientAccount.getDisplayName(), expectedDisbursementDate, + LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewIndividualClientLoanCreation(clientAccount.getId(), clientAccount.getDisplayName(), expectedDisbursementDate, clientAccount.getOfficeId()); if (productId != null) { @@ -221,7 +226,26 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { return loanDetails; } - @Override + @Override + public LoanBasicDetailsData retrieveGroupAndProductDetails(Long groupId, Long productId) { + + context.authenticatedUser(); + + final LocalDate expectedDisbursementDate = new LocalDate(); + final GroupData groupAccount = this.groupReadPlatformService.retrieveGroup(groupId); + + LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewGroupLoanCreation(groupAccount.getId(), groupAccount.getName(), expectedDisbursementDate, + groupAccount.getOfficeId()); + + if (productId != null) { + LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); + loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(loanDetails, selectedProduct); + } + + return loanDetails; + } + + @Override public LoanTransactionData retrieveNewLoanRepaymentDetails(final Long loanId) { context.authenticatedUser(); @@ -348,7 +372,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { public String loanSchema() { return "l.id as id, l.external_id as externalId, l.fund_id as fundId, f.name as fundName, " + " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription, c.id as clientId, c.display_name as clientName, " - + " c.office_id as clientOfficeId,l.submittedon_date as submittedOnDate," + + " c.office_id as clientOfficeId, g.id as groupId, g.name as groupName, g.office_id as groupOfficeId," + + " l.submittedon_date as submittedOnDate," + " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges," + " l.approvedon_date as approvedOnDate, l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate," + " l.interest_calculated_from_date as interestChargedFromDate, l.closedon_date as closedOnDate, l.expected_maturedon_date as expectedMaturityDate, " @@ -361,7 +386,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName" + " from m_loan l" - + " join m_client c on c.id = l.client_id" + + " left join m_client c on c.id = l.client_id" + + " left join m_group g on g.id = l.group_id" + " join m_product_loan lp on lp.id = l.product_id" + " join m_currency rc on rc.`code` = l.currency_code" + " left join m_fund f on f.id = l.fund_id" @@ -383,9 +409,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { Long id = rs.getLong("id"); String externalId = rs.getString("externalId"); + Long clientId = JdbcSupport.getLong(rs, "clientId"); - Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId"); + Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId");; String clientName = rs.getString("clientName"); + + Long groupId = JdbcSupport.getLong(rs, "groupId"); + Long groupOfficeId = JdbcSupport.getLong(rs, "groupOfficeId");; + String groupName = rs.getString("groupName"); + Long fundId = JdbcSupport.getLong(rs, "fundId"); String fundName = rs.getString("fundName"); Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId"); @@ -455,7 +487,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } Collection charges = null; - return new LoanBasicDetailsData(id, externalId, clientId, clientName, clientOfficeId, + return new LoanBasicDetailsData(id, externalId, + clientId, clientName, clientOfficeId, + groupId, groupName, groupOfficeId, loanProductId, loanProductName, loanProductDescription, fundId, fundName, closedOnDate,