From 2bd1332da763348adf2bbcd0b5b98642397989a6 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Fri, 16 Jan 2015 10:18:04 +0530 Subject: [PATCH] MIFOSX-1742 : added collection sheet for individuals --- api-docs/apiLive.htm | 161 +++++++++++- api-docs/apidocs.css | 2 +- .../commands/domain/CommandWrapper.java | 5 + .../service/CommandWrapperBuilder.java | 7 + .../SynchronousCommandProcessingService.java | 12 +- .../CollectionSheetConstants.java | 17 +- .../api/CollectionSheetApiResourse.java | 89 +++++++ .../command/SingleRepaymentCommand.java | 9 +- ...llectionSheetTransactionDataValidator.java | 60 ++++- .../data/IndividualClientData.java | 96 +++++++ .../data/IndividualCollectionSheetData.java | 38 +++ ...IndividualCollectionSheetLoanFlatData.java | 118 +++++++++ .../data/JLGCollectionSheetData.java | 14 +- ...dividualCollectionSheetCommandHandler.java | 33 +++ ...paymentCommandFromApiJsonDeserializer.java | 23 +- ...enerateCommandFromApiJsonDeserializer.java | 37 ++- .../CollectionSheetReadPlatformService.java | 3 + ...ollectionSheetReadPlatformServiceImpl.java | 234 +++++++++++++++++- .../CollectionSheetWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 64 ++++- ...WritePlatformServiceJpaRepositoryImpl.java | 54 ++-- .../paymentdetail/domain/PaymentDetail.java | 5 + .../domain/PaymentDetailAssembler.java | 43 ++++ ...WritePlatformServiceJpaRepositoryImpl.java | 1 - .../data/SavingsAccountTransactionDTO.java | 10 +- .../domain/DepositAccountAssembler.java | 18 +- ...WritePlatformServiceJpaRepositoryImpl.java | 8 +- ...ndividual_collection_sheet_permissions.sql | 2 + 28 files changed, 1080 insertions(+), 85 deletions(-) create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResourse.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualClientData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetLoanFlatData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/handler/SaveIndividualCollectionSheetCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetailAssembler.java create mode 100755 mifosng-provider/src/main/resources/sql/migrations/core_db/V236__individual_collection_sheet_permissions.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index d16f91eaa..f83d56d68 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -1183,7 +1183,7 @@
-

Group

+

Group & Collection Sheet

@@ -1437,6 +1437,24 @@ + + + + + + + + + + + + + + + + + +
Collection Sheetcollectionsheet?command=generateCollectionSheetGenerate Collection Sheet
collectionsheet?command=saveCollectionSheetSave Collection Sheet
@@ -5334,6 +5352,99 @@ Request Body:
+   +
+
+

Generate Individual Collection Sheet

+

This Api retrieves repayment details of all individual loans under a office as on a specified meeting date.

+
+
+ POST https://Domain Name/api/v1/collectionsheet?command=generateCollectionSheet + POST collectionsheet?command=generateCollectionSheet +Content-Type: application/json +Request Body: +{ + "dateFormat":"dd MMMM yyyy", + "locale":"en", + "transactionDate":"15 January 2015", + "officeId":3 +} + + +{ + "dueDate": [ + 2015, + 1, + 15 + ], + "clients": [ + { + "clientId": 74, + "clientName": "guarantee test", + "loans": [ + { + "loanId": 307, + "accountId": "000000307", + "accountStatusId": 300, + "productShortName": "pr", + "productId": 60, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "inMultiplesOf": 0, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "principalDue": 1126.23, + "principalPaid": 8873.77, + "interestDue": 0, + "interestPaid": 504.95, + "totalDue": 1126.23 + } + ], + "savings": [ + { + "savingsId": 213, + "accountId": "000000213", + "accountStatusId": 300, + "productName": "11", + "productId": 30, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "inMultiplesOf": 0, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "dueAmount": 2000 + } + ] + } + ], + "paymentTypeOptions": [ + { + "id": 19, + "name": "receipt", + "position": 1, + "description": "rec" + }, + { + "id": 20, + "name": "check", + "position": 2, + "description": "che" + } + ] +} + +
+
+ +  
@@ -5364,17 +5475,55 @@ Request Body: "changes": { "locale": "en", "dateFormat": "dd MMMM yyyy", - "bulkTransations": { - "loanId": 10, - "transactionAmount": 1221.36, - "transactionDate": [2014,5,4] - } + "loanTransactions": [10], + "SavingsTransactions": [] + } +} + +
+
+ +   +
+
+

Save Collection Sheet

+

This Api allows the loan officer to perform bulk repayments of individual loans and deposit of mandatory savings on a given meeting date.

+
+
+ POST https://Domain Name/api/v1/collectionsheet?command=saveCollectionSheet + POST collectionsheet?command=saveCollectionSheet +{ + "dateFormat": "dd MMMM yyyy", + "locale": "en", + "transactionDate": "04 May 2014", + "actualDisbursementDate": "04 May 2014", + "bulkDisbursementTransactions": [], + "bulkRepaymentTransactions": [{ + "loanId": 10, + "transactionAmount": 1221.36, + "paymentTypeId":19, + "receiptNumber":"1245356" + }], + "bulkSavingsDueTransactions":[] +} + + +{ + "groupId": 10, + "resourceId": 10, + "changes": { + "locale": "en", + "dateFormat": "dd MMMM yyyy", + "loanTransactions": [15], + "SavingsTransactions": [] + } }
+  
diff --git a/api-docs/apidocs.css b/api-docs/apidocs.css index 94a995115..837a7ff7b 100644 --- a/api-docs/apidocs.css +++ b/api-docs/apidocs.css @@ -192,7 +192,7 @@ body { .flybar-nav { display: block; float: left; - width: 90px; + min-width: 90px; position: relative; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 6f780412d..c13f419ff 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -697,6 +697,11 @@ public class CommandWrapper { public boolean isCollectionSheetResource() { return this.entityName.equals("COLLECTIONSHEET"); } + + public boolean isSaveIndividualCollectionSheet() { + return isCollectionSheetResource() && this.actionName.equalsIgnoreCase("SAVE"); + } + public boolean isCenterResource() { return this.entityName.equalsIgnoreCase("CENTER"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 9d48fda87..6fafeaf2e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -1425,6 +1425,13 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId + "?command=saveCollectionSheet"; return this; } + + public CommandWrapperBuilder saveIndividualCollectionSheet() { + this.actionName = "SAVE"; + this.entityName = "COLLECTIONSHEET"; + this.href = "/collectionsheet?command=saveCollectionSheet"; + return this; + } public CommandWrapperBuilder deleteGroup(final Long groupId) { this.actionName = "DELETE"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 8a36c5c06..58d182c13 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -280,15 +280,15 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else if (wrapper.isDelete()) { handler = this.applicationContext.getBean("deleteTellerCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isAllocateCashier()) { - handler = this.applicationContext.getBean("allocateCashierToTellerCommandHandler", NewCommandSourceHandler.class); + handler = this.applicationContext.getBean("allocateCashierToTellerCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdateCashierAllocation()) { - handler = this.applicationContext.getBean("updateCashierAllocationCommandHandler", NewCommandSourceHandler.class); + handler = this.applicationContext.getBean("updateCashierAllocationCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDeleteCashierAllocation()) { - handler = this.applicationContext.getBean("deleteCashierAllocationCommandHandler", NewCommandSourceHandler.class); + handler = this.applicationContext.getBean("deleteCashierAllocationCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isAllocateCashToCashier()) { - handler = this.applicationContext.getBean("allocateCashToCashierCommandHandler", NewCommandSourceHandler.class); + handler = this.applicationContext.getBean("allocateCashToCashierCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isSettleCashFromCashier()) { - handler = this.applicationContext.getBean("settleCashFromCashierCommandHandler", NewCommandSourceHandler.class); + handler = this.applicationContext.getBean("settleCashFromCashierCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } @@ -755,6 +755,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else if (wrapper.isCollectionSheetResource()) { if (wrapper.isUpdate()) { handler = this.applicationContext.getBean("updateCollectionSheetCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isSaveIndividualCollectionSheet()) { + handler = this.applicationContext.getBean("saveIndividualCollectionSheetCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/CollectionSheetConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/CollectionSheetConstants.java index 5dc4a3b4b..037eef161 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/CollectionSheetConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/CollectionSheetConstants.java @@ -9,6 +9,8 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; + public class CollectionSheetConstants { public static final String COLLECTIONSHEET_RESOURCE_NAME = "collectionsheet"; @@ -23,6 +25,8 @@ public class CollectionSheetConstants { public static final String bulkSavingsDueTransactionsParamName = "bulkSavingsDueTransactions"; public static final String noteParamName = "note"; public static final String calendarIdParamName = "calendarId"; + public static final String officeIdParamName = "officeId"; + public static final String staffIdParamName = "staffId"; // attendance parameters public static final String clientsAttendanceParamName = "clientsAttendance"; @@ -35,5 +39,16 @@ public class CollectionSheetConstants { public static final Set COLLECTIONSHEET_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, dateFormatParamName, transactionDateParamName, actualDisbursementDateParamName, bulkRepaymentTransactionsParamName, - bulkDisbursementTransactionsParamName, noteParamName, calendarIdParamName, clientsAttendanceParamName, bulkSavingsDueTransactionsParamName)); + bulkDisbursementTransactionsParamName, noteParamName, calendarIdParamName, clientsAttendanceParamName, + bulkSavingsDueTransactionsParamName, PaymentDetailConstants.paymentTypeParamName, + PaymentDetailConstants.accountNumberParamName, PaymentDetailConstants.checkNumberParamName, + PaymentDetailConstants.routingCodeParamName, PaymentDetailConstants.receiptNumberParamName, + PaymentDetailConstants.bankNumberParamName)); + + public static final Set INDIVIDUAL_COLLECTIONSHEET_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, + dateFormatParamName, transactionDateParamName, actualDisbursementDateParamName, bulkRepaymentTransactionsParamName, + bulkDisbursementTransactionsParamName, noteParamName, bulkSavingsDueTransactionsParamName)); + + public static final Set INDIVIDUAL_COLLECTIONSHEET_SUPPORTED_PARAMS = new HashSet<>(Arrays.asList(transactionDateParamName, + localeParamName, dateFormatParamName, officeIdParamName, staffIdParamName)); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResourse.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResourse.java new file mode 100755 index 000000000..c5bc63119 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResourse.java @@ -0,0 +1,89 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.collectionsheet.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.api.JsonQuery; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants; +import org.mifosplatform.portfolio.collectionsheet.data.IndividualCollectionSheetData; +import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; + +@Path("/collectionsheet") +@Component +@Scope("singleton") +public class CollectionSheetApiResourse { + + private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; + private final ToApiJsonSerializer toApiJsonSerializer; + private final FromJsonHelper fromJsonHelper; + private final ApiRequestParameterHelper apiRequestPrameterHelper; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final PlatformSecurityContext context; + + @Autowired + public CollectionSheetApiResourse(final CollectionSheetReadPlatformService collectionSheetReadPlatformService, + final ToApiJsonSerializer toApiJsonSerializer, final FromJsonHelper fromJsonHelper, + final ApiRequestParameterHelper apiRequestPrameterHelper, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final PlatformSecurityContext context) { + this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; + this.toApiJsonSerializer = toApiJsonSerializer; + this.fromJsonHelper = fromJsonHelper; + this.apiRequestPrameterHelper = apiRequestPrameterHelper; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.context = context; + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String generateCollectionSheet(@QueryParam("command") final String commandParam, final String apiRequestBodyAsJson, + @Context final UriInfo uriInfo) { + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + CommandProcessingResult result = null; + + if (is(commandParam, "generateCollectionSheet")) { + this.context.authenticatedUser().validateHasReadPermission(CollectionSheetConstants.COLLECTIONSHEET_RESOURCE_NAME); + final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); + final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); + final IndividualCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService + .generateIndividualCollectionSheet(query); + final ApiRequestJsonSerializationSettings settings = this.apiRequestPrameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, collectionSheet); + } else if (is(commandParam, "saveCollectionSheet")) { + final CommandWrapper commandRequest = builder.saveIndividualCollectionSheet().build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } + return null; + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/command/SingleRepaymentCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/command/SingleRepaymentCommand.java index aa5362299..03df43bbb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/command/SingleRepaymentCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/command/SingleRepaymentCommand.java @@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.collectionsheet.command; import java.math.BigDecimal; import org.joda.time.LocalDate; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; /** * Immutable command for Single loan repayment. @@ -17,11 +18,14 @@ public class SingleRepaymentCommand { private final Long loanId; private final BigDecimal transactionAmount; private final LocalDate transactionDate; + private final PaymentDetail paymentDetail; - public SingleRepaymentCommand(final Long loanId, final BigDecimal transactionAmount, final LocalDate transactionDate) { + public SingleRepaymentCommand(final Long loanId, final BigDecimal transactionAmount, final LocalDate transactionDate, + final PaymentDetail paymentDetail) { this.loanId = loanId; this.transactionAmount = transactionAmount; this.transactionDate = transactionDate; + this.paymentDetail = paymentDetail; } public Long getLoanId() { @@ -36,4 +40,7 @@ public class SingleRepaymentCommand { return this.transactionDate; } + public PaymentDetail getPaymentDetail() { + return this.paymentDetail; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/CollectionSheetTransactionDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/CollectionSheetTransactionDataValidator.java index 4dadb63e8..87567d372 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/CollectionSheetTransactionDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/CollectionSheetTransactionDataValidator.java @@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.collectionsheet.data; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.COLLECTIONSHEET_REQUEST_DATA_PARAMETERS; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.COLLECTIONSHEET_RESOURCE_NAME; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.INDIVIDUAL_COLLECTIONSHEET_REQUEST_DATA_PARAMETERS; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.attendanceTypeParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.bulkDisbursementTransactionsParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.bulkRepaymentTransactionsParamName; @@ -15,8 +16,8 @@ import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstan import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.clientIdParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.clientsAttendanceParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.loanIdParamName; -import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.savingsIdParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.noteParamName; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.savingsIdParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.transactionAmountParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.transactionDateParamName; @@ -35,6 +36,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -82,7 +84,39 @@ public class CollectionSheetTransactionDataValidator { validateDisbursementTransactions(element, baseDataValidator); validateRepaymentTransactions(element, baseDataValidator); - + + validateSavingsDueTransactions(element, baseDataValidator); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject()); + validatePaymentDetails(baseDataValidator, element, locale); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateIndividualCollectionSheet(final JsonCommand command) { + final String json = command.json(); + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INDIVIDUAL_COLLECTIONSHEET_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(COLLECTIONSHEET_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transactionDateParamName, element); + baseDataValidator.reset().parameter(transactionDateParamName).value(transactionDate).notNull(); + + final String note = this.fromApiJsonHelper.extractStringNamed(noteParamName, element); + if (StringUtils.isNotBlank(note)) { + baseDataValidator.reset().parameter(noteParamName).value(note).notExceedingLengthOf(1000); + } + + validateDisbursementTransactions(element, baseDataValidator); + + validateRepaymentTransactions(element, baseDataValidator); + validateSavingsDueTransactions(element, baseDataValidator); throwExceptionIfValidationWarningsExist(dataValidationErrors); @@ -147,11 +181,13 @@ public class CollectionSheetTransactionDataValidator { .integerGreaterThanZero(); baseDataValidator.reset().parameter("bulktransaction" + "[" + i + "].disbursement.amount").value(disbursementAmount) .notNull().zeroOrPositiveAmount(); + + validatePaymentDetails(baseDataValidator, loanTransactionElement, locale); } } } } - + private void validateSavingsDueTransactions(final JsonElement element, final DataValidatorBuilder baseDataValidator) { final JsonObject topLevelJsonElement = element.getAsJsonObject(); final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); @@ -168,13 +204,27 @@ public class CollectionSheetTransactionDataValidator { baseDataValidator.reset().parameter("bulktransaction" + "[" + i + "].savings.id").value(savingsId).notNull() .integerGreaterThanZero(); - baseDataValidator.reset().parameter("bulktransaction" + "[" + i + "].due.amount").value(dueAmount) - .notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("bulktransaction" + "[" + i + "].due.amount").value(dueAmount).notNull() + .zeroOrPositiveAmount(); + validatePaymentDetails(baseDataValidator, savingsTransactionElement, locale); } } } } + private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element, final Locale locale) { + // Validate all string payment detail fields for max length + final Integer paymentTypeId = this.fromApiJsonHelper.extractIntegerNamed(PaymentDetailConstants.paymentTypeParamName, element, + locale); + baseDataValidator.reset().parameter(PaymentDetailConstants.paymentTypeParamName).value(paymentTypeId).ignoreIfNull() + .integerGreaterThanZero(); + for (final String paymentDetailParameterName : PaymentDetailConstants.PAYMENT_CREATE_REQUEST_DATA_PARAMETERS) { + final String paymentDetailParameterValue = this.fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element); + baseDataValidator.reset().parameter(paymentDetailParameterName).value(paymentDetailParameterValue).ignoreIfNull() + .notExceedingLengthOf(50); + } + } + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualClientData.java new file mode 100755 index 000000000..5224a7342 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualClientData.java @@ -0,0 +1,96 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.collectionsheet.data; + +import java.util.Collection; + +/** + * Immutable data object for clients with loans due for disbursement or + * collection. + */ +public class IndividualClientData { + + private final Long clientId; + private final String clientName; + private Collection loans; + private Collection savings; + + public static IndividualClientData instance(final Long clientId, final String clientName) { + final Collection loans = null; + final Collection savings = null; + return new IndividualClientData(clientId, clientName, loans, savings); + } + + public static IndividualClientData withSavings(final IndividualClientData client, final Collection savings) { + + return new IndividualClientData(client.clientId, client.clientName, client.loans, savings); + } + + public static IndividualClientData withLoans(final IndividualClientData client, final Collection loans) { + + return new IndividualClientData(client.clientId, client.clientName, loans, client.savings); + } + + /** + * @param clientId + * @param clientName + * @param loans + * @param savings + */ + private IndividualClientData(Long clientId, String clientName, Collection loans, Collection savings) { + this.clientId = clientId; + this.clientName = clientName; + this.loans = loans; + this.savings = savings; + } + + public Long getClientId() { + return this.clientId; + } + + public String getClientName() { + return this.clientName; + } + + public Collection getLoans() { + return this.loans; + } + + public void setLoans(final Collection loans) { + this.loans = loans; + } + + public void addLoans(LoanDueData loans) { + if (this.loans != null) { + this.loans.add(loans); + } + } + + public Collection getSavings() { + return this.savings; + } + + public void setSavings(Collection savings) { + this.savings = savings; + } + + public void addSavings(SavingsDueData savings) { + if (this.savings != null) { + this.savings.add(savings); + } + } + + @Override + public boolean equals(final Object obj) { + final IndividualClientData clientData = (IndividualClientData) obj; + return clientData.clientId.compareTo(this.clientId) == 0; + } + + @Override + public int hashCode() { + return this.clientId.hashCode(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetData.java new file mode 100755 index 000000000..272232e9e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetData.java @@ -0,0 +1,38 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.collectionsheet.data; + +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; + +/** + * Immutable data object for collection sheet. + */ +public class IndividualCollectionSheetData { + + @SuppressWarnings("unused") + private final LocalDate dueDate; + @SuppressWarnings("unused") + private final Collection clients; + + @SuppressWarnings("unused") + private final Collection paymentTypeOptions; + + public static IndividualCollectionSheetData instance(final LocalDate date, final Collection clients, + final Collection paymentTypeOptions) { + return new IndividualCollectionSheetData(date, clients, paymentTypeOptions); + } + + private IndividualCollectionSheetData(final LocalDate dueDate, final Collection clients, + final Collection paymentTypeOptions) { + this.dueDate = dueDate; + this.clients = clients; + this.paymentTypeOptions = paymentTypeOptions; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetLoanFlatData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetLoanFlatData.java new file mode 100755 index 000000000..a50f54328 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/IndividualCollectionSheetLoanFlatData.java @@ -0,0 +1,118 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.collectionsheet.data; + +import java.math.BigDecimal; + +import org.mifosplatform.organisation.monetary.data.CurrencyData; + +/** + * Immutable data object for extracting flat data for joint liability group's + * collection sheet. + */ +public class IndividualCollectionSheetLoanFlatData { + + private final String clientName; + private final Long clientId; + private final Long loanId; + private final String accountId; + private final Integer accountStatusId; + private final String productShortName; + private final Long productId; + private final CurrencyData currency; + private BigDecimal disbursementAmount = BigDecimal.ZERO; + private BigDecimal principalDue = BigDecimal.ZERO; + private BigDecimal principalPaid = BigDecimal.ZERO; + private BigDecimal interestDue = BigDecimal.ZERO; + private BigDecimal interestPaid = BigDecimal.ZERO; + private BigDecimal chargesDue = BigDecimal.ZERO; + + public IndividualCollectionSheetLoanFlatData(final String clientName, final Long clientId, final Long loanId, final String accountId, + final Integer accountStatusId, final String productShortName, final Long productId, final CurrencyData currency, + final BigDecimal disbursementAmount, final BigDecimal principalDue, final BigDecimal principalPaid, + final BigDecimal interestDue, final BigDecimal interestPaid, final BigDecimal chargesDue) { + this.clientName = clientName; + this.clientId = clientId; + this.loanId = loanId; + this.accountId = accountId; + this.accountStatusId = accountStatusId; + this.productShortName = productShortName; + this.productId = productId; + this.currency = currency; + this.disbursementAmount = disbursementAmount; + this.principalDue = principalDue; + this.principalPaid = principalPaid; + this.interestDue = interestDue; + this.interestPaid = interestPaid; + this.chargesDue = chargesDue; + } + + public String getClientName() { + return this.clientName; + } + + public Long getClientId() { + return this.clientId; + } + + public Long getLoanId() { + return this.loanId; + } + + public String getAccountId() { + return this.accountId; + } + + public Integer getAccountStatusId() { + return this.accountStatusId; + } + + public String getProductShortName() { + return this.productShortName; + } + + public Long getProductId() { + return this.productId; + } + + public CurrencyData getCurrency() { + return this.currency; + } + + public BigDecimal getDisbursementAmount() { + return this.disbursementAmount; + } + + public BigDecimal getPrincipalDue() { + return this.principalDue; + } + + public BigDecimal getPrincipalPaid() { + return this.principalPaid; + } + + public BigDecimal getInterestDue() { + return this.interestDue; + } + + public BigDecimal getInterestPaid() { + return this.interestPaid; + } + + public BigDecimal getChargesDue() { + return this.chargesDue; + } + + public LoanDueData getLoanDueData() { + return new LoanDueData(this.loanId, this.accountId, this.accountStatusId, this.productShortName, this.productId, this.currency, + this.disbursementAmount, this.principalDue, this.principalPaid, this.interestDue, this.interestPaid, this.chargesDue); + } + + public IndividualClientData getClientData() { + return IndividualClientData.instance(this.clientId, this.clientName); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/JLGCollectionSheetData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/JLGCollectionSheetData.java index 3ee5503bc..9f67fd464 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/JLGCollectionSheetData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/data/JLGCollectionSheetData.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.List; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.savings.data.SavingsProductData; @@ -24,25 +25,30 @@ public class JLGCollectionSheetData { private final Collection savingsProducts; private final Collection groups; private final List attendanceTypeOptions; + private final Collection paymentTypeOptions; public static JLGCollectionSheetData instance(final LocalDate date, final Collection loanProducts, - final Collection groups, final List attendanceTypeOptions) { - return new JLGCollectionSheetData(date, loanProducts, null, groups, attendanceTypeOptions); + final Collection groups, final List attendanceTypeOptions, + final Collection paymentTypeOptions) { + return new JLGCollectionSheetData(date, loanProducts, null, groups, attendanceTypeOptions, paymentTypeOptions); } public static JLGCollectionSheetData withSavingsProducts(final JLGCollectionSheetData data, final Collection savingsProducts) { - return new JLGCollectionSheetData(data.dueDate, data.loanProducts, savingsProducts, data.groups, data.attendanceTypeOptions); + return new JLGCollectionSheetData(data.dueDate, data.loanProducts, savingsProducts, data.groups, data.attendanceTypeOptions, + data.paymentTypeOptions); } private JLGCollectionSheetData(LocalDate dueDate, Collection loanProducts, - Collection savingsProducts, Collection groups, List attendanceTypeOptions) { + Collection savingsProducts, Collection groups, List attendanceTypeOptions, + final Collection paymentTypeOptions) { this.dueDate = dueDate; this.loanProducts = loanProducts; this.savingsProducts = savingsProducts; this.groups = groups; this.attendanceTypeOptions = attendanceTypeOptions; + this.paymentTypeOptions = paymentTypeOptions; } public LocalDate getDate() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/handler/SaveIndividualCollectionSheetCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/handler/SaveIndividualCollectionSheetCommandHandler.java new file mode 100755 index 000000000..ade762993 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/handler/SaveIndividualCollectionSheetCommandHandler.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.collectionsheet.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SaveIndividualCollectionSheetCommandHandler implements NewCommandSourceHandler { + + private final CollectionSheetWritePlatformService collectionSheetWritePlatformService; + + @Autowired + public SaveIndividualCollectionSheetCommandHandler(final CollectionSheetWritePlatformService collectionSheetWritePlatformService) { + this.collectionSheetWritePlatformService = collectionSheetWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.collectionSheetWritePlatformService.saveIndividualCollectionSheet(command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer.java index 31e30c447..1bd533ca2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer.java @@ -16,6 +16,8 @@ import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand; import org.mifosplatform.portfolio.collectionsheet.command.SingleRepaymentCommand; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetailAssembler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -32,16 +34,28 @@ public final class CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer ex AbstractFromApiJsonDeserializer { private final FromJsonHelper fromApiJsonHelper; + private final PaymentDetailAssembler paymentDetailAssembler; @Autowired - public CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) { + public CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper, + final PaymentDetailAssembler paymentDetailAssembler) { this.fromApiJsonHelper = fromApiJsonHelper; + this.paymentDetailAssembler = paymentDetailAssembler; } @Override public CollectionSheetBulkRepaymentCommand commandFromApiJson(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + final JsonElement element = this.fromApiJsonHelper.parse(json); + final PaymentDetail paymentDetail = this.paymentDetailAssembler.fetchPaymentDetail(element.getAsJsonObject()); + + return commandFromApiJson(json, paymentDetail); + } + + public CollectionSheetBulkRepaymentCommand commandFromApiJson(final String json, final PaymentDetail paymentDetail) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + final JsonElement element = this.fromApiJsonHelper.parse(json); final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element); @@ -63,7 +77,12 @@ public final class CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer ex final Long loanId = this.fromApiJsonHelper.extractLongNamed("loanId", loanTransactionElement); final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalNamed("transactionAmount", loanTransactionElement, locale); - loanRepaymentTransactions[i] = new SingleRepaymentCommand(loanId, transactionAmount, transactionDate); + PaymentDetail detail = paymentDetail; + if (paymentDetail == null) { + detail = this.paymentDetailAssembler.fetchPaymentDetail(loanTransactionElement); + } + + loanRepaymentTransactions[i] = new SingleRepaymentCommand(loanId, transactionAmount, transactionDate, detail); } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetGenerateCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetGenerateCommandFromApiJsonDeserializer.java index ad68a62c5..ee209d4ac 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetGenerateCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetGenerateCommandFromApiJsonDeserializer.java @@ -5,9 +5,12 @@ */ package org.mifosplatform.portfolio.collectionsheet.serialization; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.INDIVIDUAL_COLLECTIONSHEET_SUPPORTED_PARAMS; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.calendarIdParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.dateFormatParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.localeParamName; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.officeIdParamName; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.staffIdParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.transactionDateParamName; import java.lang.reflect.Type; @@ -37,8 +40,8 @@ public class CollectionSheetGenerateCommandFromApiJsonDeserializer { /** * The parameters supported for this command. */ - final Set supportedParameters = new HashSet<>(Arrays.asList(transactionDateParamName, localeParamName, - dateFormatParamName, calendarIdParamName)); + final Set supportedParameters = new HashSet<>(Arrays.asList(transactionDateParamName, localeParamName, dateFormatParamName, + calendarIdParamName)); private final FromJsonHelper fromApiJsonHelper; @@ -74,4 +77,34 @@ public class CollectionSheetGenerateCommandFromApiJsonDeserializer { "Validation errors exist.", dataValidationErrors); } } + public void validateForGenerateCollectionSheetOfIndividuals(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INDIVIDUAL_COLLECTIONSHEET_SUPPORTED_PARAMS); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList<>(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("collectionsheet"); + + final String transactionDateStr = this.fromApiJsonHelper.extractStringNamed(transactionDateParamName, element); + baseDataValidator.reset().parameter(transactionDateParamName).value(transactionDateStr).notBlank(); + + if (!StringUtils.isBlank(transactionDateStr)) { + final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(transactionDateParamName, element); + baseDataValidator.reset().parameter(transactionDateParamName).value(dueDate).notNull(); + } + + final Long officeId = this.fromApiJsonHelper.extractLongNamed(officeIdParamName, element); + baseDataValidator.reset().parameter(officeIdParamName).value(officeId).longGreaterThanZero(); + + final Long staffId = this.fromApiJsonHelper.extractLongNamed(staffIdParamName, element); + baseDataValidator.reset().parameter(staffIdParamName).value(staffId).longGreaterThanZero(); + + 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/collectionsheet/service/CollectionSheetReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformService.java index 0cd7aafa5..f58accf2b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformService.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.collectionsheet.service; import org.mifosplatform.infrastructure.core.api.JsonQuery; +import org.mifosplatform.portfolio.collectionsheet.data.IndividualCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; public interface CollectionSheetReadPlatformService { @@ -14,4 +15,6 @@ public interface CollectionSheetReadPlatformService { JLGCollectionSheetData generateCenterCollectionSheet(final Long groupId, final JsonQuery query); + IndividualCollectionSheetData generateIndividualCollectionSheet(final JsonQuery query); + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 0076a843e..0e1aa5bb0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -7,6 +7,8 @@ package org.mifosplatform.portfolio.collectionsheet.service; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.calendarIdParamName; import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.transactionDateParamName; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.officeIdParamName; +import static org.mifosplatform.portfolio.collectionsheet.CollectionSheetConstants.staffIdParamName; import java.math.BigDecimal; import java.sql.ResultSet; @@ -20,6 +22,8 @@ import java.util.List; import java.util.Set; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; import org.mifosplatform.infrastructure.core.api.JsonQuery; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; @@ -30,6 +34,9 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarRepositoryWrapper; import org.mifosplatform.portfolio.calendar.exception.NotValidRecurringDateException; +import org.mifosplatform.portfolio.collectionsheet.data.IndividualClientData; +import org.mifosplatform.portfolio.collectionsheet.data.IndividualCollectionSheetData; +import org.mifosplatform.portfolio.collectionsheet.data.IndividualCollectionSheetLoanFlatData; import org.mifosplatform.portfolio.collectionsheet.data.JLGClientData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetFlatData; @@ -44,6 +51,7 @@ import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.meeting.attendance.service.AttendanceDropdownReadPlatformService; import org.mifosplatform.portfolio.meeting.attendance.service.AttendanceEnumerations; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; import org.mifosplatform.portfolio.savings.data.SavingsProductData; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; @@ -65,14 +73,16 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer; private final CalendarRepositoryWrapper calendarRepositoryWrapper; private final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService; - final MandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor = new MandatorySavingsCollectionsheetExtractor(); + private final MandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor = new MandatorySavingsCollectionsheetExtractor(); + private final CodeValueReadPlatformService codeValueReadPlatformService; @Autowired public CollectionSheetReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final CenterReadPlatformService centerReadPlatformService, final GroupReadPlatformService groupReadPlatformService, final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer, final CalendarRepositoryWrapper calendarRepositoryWrapper, - final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService) { + final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService, + final CodeValueReadPlatformService codeValueReadPlatformService) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource); @@ -80,6 +90,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe this.groupReadPlatformService = groupReadPlatformService; this.calendarRepositoryWrapper = calendarRepositoryWrapper; this.attendanceDropdownReadPlatformService = attendanceDropdownReadPlatformService; + this.codeValueReadPlatformService = codeValueReadPlatformService; } /* @@ -94,6 +105,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe boolean firstTime = true; Long prevGroupId = null; Long prevClientId = null; + final Collection paymentOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); final List jlgGroupsData = new ArrayList<>(); List clientsData = new ArrayList<>(); @@ -168,7 +181,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe } jlgCollectionSheetData = JLGCollectionSheetData.instance(dueDate, loanProducts, jlgGroupsData, - this.attendanceDropdownReadPlatformService.retrieveAttendanceTypeOptions()); + this.attendanceDropdownReadPlatformService.retrieveAttendanceTypeOptions(), paymentOptions); } return jlgCollectionSheetData; @@ -613,4 +626,219 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe return SavingsDueData.instance(savingsId, accountId, accountStatusId, productName, productId, currency, dueAmount); } } + + @Override + public IndividualCollectionSheetData generateIndividualCollectionSheet(final JsonQuery query) { + + this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheetOfIndividuals(query.json()); + + final LocalDate transactionDate = query.localDateValueOfParameterNamed(transactionDateParamName); + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + final String transactionDateStr = df.format(transactionDate.toDate()); + + final AppUser currentUser = this.context.authenticatedUser(); + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String officeHierarchy = hierarchy + "%"; + + final Long officeId = query.longValueOfParameterNamed(officeIdParamName); + final Long staffId = query.longValueOfParameterNamed(staffIdParamName); + final boolean checkForOfficeId = officeId != null; + final boolean checkForStaffId = staffId != null; + + final IndividualCollectionSheetFaltDataMapper mapper = new IndividualCollectionSheetFaltDataMapper(checkForOfficeId, + checkForStaffId); + + final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", transactionDateStr).addValue( + "officeHierarchy", officeHierarchy); + + if (checkForOfficeId) { + ((MapSqlParameterSource) namedParameters).addValue("officeId", officeId); + } + if (checkForStaffId) { + ((MapSqlParameterSource) namedParameters).addValue("staffId", staffId); + } + + final Collection collectionSheetFlatDatas = this.namedParameterjdbcTemplate.query( + mapper.sqlSchema(), namedParameters, mapper); + + IndividualMandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor = new IndividualMandatorySavingsCollectionsheetExtractor( + checkForOfficeId, checkForStaffId); + // mandatory savings data for collection sheet + Collection clientData = this.namedParameterjdbcTemplate.query( + mandatorySavingsExtractor.collectionSheetSchema(), namedParameters, mandatorySavingsExtractor); + + // merge savings data into loan data + mergeLoanData(collectionSheetFlatDatas, (List) clientData); + + Collection paymentOptions = this.codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + + return IndividualCollectionSheetData.instance(transactionDate, clientData, paymentOptions); + + } + + private static final class IndividualCollectionSheetFaltDataMapper implements RowMapper { + + private final String sql; + + public IndividualCollectionSheetFaltDataMapper(final boolean checkForOfficeId, final boolean checkforStaffId) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT loandata.*, sum(lc.amount_outstanding_derived) as chargesDue "); + sb.append("from (SELECT cl.display_name As clientName, "); + sb.append("cl.id As clientId, ln.id As loanId, ln.account_no As accountId, ln.loan_status_id As accountStatusId,"); + sb.append(" pl.short_name As productShortName, ln.product_id As productId, "); + sb.append("ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMultiplesOf, "); + sb.append("rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "); + sb.append("if(ln.loan_status_id = 200 , ln.principal_amount , null) As disbursementAmount, "); + sb.append("sum(ifnull(if(ln.loan_status_id = 300, ls.principal_amount, 0.0), 0.0) - ifnull(if(ln.loan_status_id = 300, ls.principal_completed_derived, 0.0), 0.0)) As principalDue, "); + sb.append("ln.principal_repaid_derived As principalPaid, "); + sb.append("sum(ifnull(if(ln.loan_status_id = 300, ls.interest_amount, 0.0), 0.0) - ifnull(if(ln.loan_status_id = 300, ls.interest_completed_derived, 0.0), 0.0)) As interestDue, "); + sb.append("ln.interest_repaid_derived As interestPaid "); + sb.append("FROM m_loan ln "); + sb.append("JOIN m_client cl ON cl.id = ln.client_id "); + sb.append("LEFT JOIN m_office of ON of.id = cl.office_id AND of.hierarchy like :officeHierarchy "); + sb.append("LEFT JOIN m_product_loan pl ON pl.id = ln.product_id "); + sb.append("LEFT JOIN m_currency rc on rc.`code` = ln.currency_code "); + sb.append("JOIN m_loan_repayment_schedule ls ON ls.loan_id = ln.id AND ls.completed_derived = 0 AND ls.duedate <= :dueDate "); + sb.append("where "); + if (checkForOfficeId) { + sb.append("of.id = :officeId and "); + } + if (checkforStaffId) { + sb.append("ln.loan_officer_id = :staffId and "); + } + sb.append("(ln.loan_status_id = 200 or ln.loan_status_id = 300) "); + sb.append("and ln.group_id is null GROUP BY cl.id , ln.id ORDER BY cl.id , ln.id ) loandata "); + sb.append("LEFT JOIN m_loan_charge lc ON lc.loan_id = loandata.loanId AND lc.is_paid_derived = 0 AND lc.is_active = 1 AND ( lc.due_for_collection_as_of_date <= :dueDate OR lc.charge_time_enum = 1) "); + sb.append("GROUP BY loandata.clientId, loandata.loanId ORDER BY loandata.clientId, loandata.loanId "); + + sql = sb.toString(); + } + + public String sqlSchema() { + return sql; + } + + @Override + public IndividualCollectionSheetLoanFlatData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final String clientName = rs.getString("clientName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final Long loanId = JdbcSupport.getLong(rs, "loanId"); + final String accountId = rs.getString("accountId"); + final Integer accountStatusId = JdbcSupport.getInteger(rs, "accountStatusId"); + final String productShortName = rs.getString("productShortName"); + final Long productId = JdbcSupport.getLong(rs, "productId"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + CurrencyData currencyData = null; + if (currencyCode != null) { + currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, + currencyNameCode); + } + + final BigDecimal disbursementAmount = rs.getBigDecimal("disbursementAmount"); + final BigDecimal principalDue = rs.getBigDecimal("principalDue"); + final BigDecimal principalPaid = rs.getBigDecimal("principalPaid"); + final BigDecimal interestDue = rs.getBigDecimal("interestDue"); + final BigDecimal interestPaid = rs.getBigDecimal("interestPaid"); + final BigDecimal chargesDue = rs.getBigDecimal("chargesDue"); + + return new IndividualCollectionSheetLoanFlatData(clientName, clientId, loanId, accountId, accountStatusId, productShortName, + productId, currencyData, disbursementAmount, principalDue, principalPaid, interestDue, interestPaid, chargesDue); + } + + } + + private static final class IndividualMandatorySavingsCollectionsheetExtractor implements + ResultSetExtractor> { + + private final SavingsDueDataMapper savingsDueDataMapper = new SavingsDueDataMapper(); + + private final String sql; + + public IndividualMandatorySavingsCollectionsheetExtractor(final boolean checkForOfficeId, final boolean checkforStaffId) { + + final StringBuffer sb = new StringBuffer(400); + sb.append("SELECT cl.display_name As clientName, cl.id As clientId, "); + sb.append("sa.id As savingsId, sa.account_no As accountId, sa.status_enum As accountStatusId, "); + sb.append("sp.short_name As productShortName, sp.id As productId, "); + sb.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); + sb.append("rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "); + sb.append("sum(ifnull(mss.deposit_amount,0) - ifnull(mss.deposit_amount_completed_derived,0)) as dueAmount "); + sb.append("FROM m_savings_account sa "); + sb.append("JOIN m_client cl ON cl.id = sa.client_id "); + sb.append("JOIN m_savings_product sp ON sa.product_id=sp.id "); + sb.append("JOIN m_deposit_account_recurring_detail dard ON sa.id = dard.savings_account_id AND dard.is_mandatory = true AND dard.is_calendar_inherited = false "); + sb.append("JOIN m_mandatory_savings_schedule mss ON mss.savings_account_id=sa.id AND mss.completed_derived = 0 AND mss.duedate <= :dueDate "); + sb.append("LEFT JOIN m_office of ON of.id = cl.office_id AND of.hierarchy like :officeHierarchy "); + sb.append("LEFT JOIN m_currency rc on rc.`code` = sa.currency_code "); + sb.append("WHERE sa.status_enum=300 and sa.group_id is null "); + sb.append("and (cl.status_enum = 300 or (cl.status_enum = 600 and cl.closedon_date >= :dueDate)) "); + if (checkForOfficeId) { + sb.append("and of.id = :officeId "); + } + if (checkforStaffId) { + sb.append("and sa.field_officer_id = :staffId "); + } + sb.append("GROUP BY cl.id , sa.id ORDER BY cl.id , sa.id "); + + this.sql = sb.toString(); + } + + public String collectionSheetSchema() { + return this.sql; + } + + @SuppressWarnings("null") + @Override + public Collection extractData(ResultSet rs) throws SQLException, DataAccessException { + List clientData = new ArrayList<>(); + int rowNum = 0; + + IndividualClientData client = null; + Long previousClientId = null; + + while (rs.next()) { + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + if (previousClientId == null || clientId.compareTo(previousClientId) != 0) { + final String clientName = rs.getString("clientName"); + client = IndividualClientData.instance(clientId, clientName); + client = IndividualClientData.withSavings(client, new ArrayList()); + clientData.add(client); + previousClientId = clientId; + } + SavingsDueData saving = savingsDueDataMapper.mapRow(rs, rowNum); + client.addSavings(saving); + rowNum++; + } + + return clientData; + } + } + + private void mergeLoanData(final Collection loanFlatDatas, List clientDatas) { + + IndividualClientData clientSavingsData = null; + for (IndividualCollectionSheetLoanFlatData loanFlatData : loanFlatDatas) { + IndividualClientData clientData = loanFlatData.getClientData(); + if (clientSavingsData == null || !clientSavingsData.equals(clientData)) { + if (clientDatas.contains(clientData)) { + final int index = clientDatas.indexOf(clientData); + if (index < 0) return; + clientSavingsData = clientDatas.get(index); + clientSavingsData.setLoans(new ArrayList()); + } else { + clientSavingsData = clientData; + clientSavingsData.setLoans(new ArrayList()); + clientDatas.add(clientSavingsData); + } + } + clientSavingsData.addLoans(loanFlatData.getLoanDueData()); + } + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformService.java index 2c9f5ea9c..433c5019b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformService.java @@ -11,4 +11,6 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; public interface CollectionSheetWritePlatformService { CommandProcessingResult updateCollectionSheet(JsonCommand command); + + CommandProcessingResult saveIndividualCollectionSheet(JsonCommand command); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformServiceJpaRepositoryImpl.java index 0ea08fa57..37a615599 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetWritePlatformServiceJpaRepositoryImpl.java @@ -5,8 +5,10 @@ */ package org.mifosplatform.portfolio.collectionsheet.service; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; @@ -20,8 +22,11 @@ import org.mifosplatform.portfolio.collectionsheet.serialization.CollectionSheet import org.mifosplatform.portfolio.collectionsheet.serialization.CollectionSheetBulkRepaymentCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; import org.mifosplatform.portfolio.meeting.service.MeetingWritePlatformService; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetailAssembler; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; import org.mifosplatform.portfolio.savings.domain.DepositAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.mifosplatform.portfolio.savings.service.DepositAccountWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -36,6 +41,7 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col private final MeetingWritePlatformService meetingWritePlatformService; private final DepositAccountAssembler accountAssembler; private final DepositAccountWritePlatformService accountWritePlatformService; + private final PaymentDetailAssembler paymentDetailAssembler; @Autowired public CollectionSheetWritePlatformServiceJpaRepositoryImpl(final LoanWritePlatformService loanWritePlatformService, @@ -43,7 +49,7 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col final CollectionSheetBulkDisbursalCommandFromApiJsonDeserializer bulkDisbursalCommandFromApiJsonDeserializer, final CollectionSheetTransactionDataValidator transactionDataValidator, final MeetingWritePlatformService meetingWritePlatformService, final DepositAccountAssembler accountAssembler, - final DepositAccountWritePlatformService accountWritePlatformService) { + final DepositAccountWritePlatformService accountWritePlatformService, final PaymentDetailAssembler paymentDetailAssembler) { this.loanWritePlatformService = loanWritePlatformService; this.bulkRepaymentCommandFromApiJsonDeserializer = bulkRepaymentCommandFromApiJsonDeserializer; this.bulkDisbursalCommandFromApiJsonDeserializer = bulkDisbursalCommandFromApiJsonDeserializer; @@ -51,6 +57,7 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col this.meetingWritePlatformService = meetingWritePlatformService; this.accountAssembler = accountAssembler; this.accountWritePlatformService = accountWritePlatformService; + this.paymentDetailAssembler = paymentDetailAssembler; } @Override @@ -67,11 +74,12 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col changes.put("note", noteText); } - changes.putAll(updateBulkReapayments(command)); + final PaymentDetail paymentDetail = this.paymentDetailAssembler.fetchPaymentDetail(command.parsedJson().getAsJsonObject()); + changes.putAll(updateBulkReapayments(command, paymentDetail)); changes.putAll(updateBulkDisbursals(command)); - - changes.putAll(updateBulkMandatorySavingsDuePayments(command)); + + changes.putAll(updateBulkMandatorySavingsDuePayments(command, paymentDetail)); this.meetingWritePlatformService.updateCollectionSheetAttendance(command); @@ -82,10 +90,39 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col .with(changes).with(changes).build(); } - private Map updateBulkReapayments(final JsonCommand command) { + @Override + public CommandProcessingResult saveIndividualCollectionSheet(final JsonCommand command) { + + this.transactionDataValidator.validateIndividualCollectionSheet(command); + + final Map changes = new HashMap<>(); + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); + } + + final PaymentDetail paymentDetail = null; + + changes.putAll(updateBulkReapayments(command, paymentDetail)); + + changes.putAll(updateBulkDisbursals(command)); + + changes.putAll(updateBulkMandatorySavingsDuePayments(command, paymentDetail)); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(command.entityId()) // + .withGroupId(command.entityId()) // + .with(changes).with(changes).build(); + } + + private Map updateBulkReapayments(final JsonCommand command, final PaymentDetail paymentDetail) { final Map changes = new HashMap<>(); final CollectionSheetBulkRepaymentCommand bulkRepaymentCommand = this.bulkRepaymentCommandFromApiJsonDeserializer - .commandFromApiJson(command.json()); + .commandFromApiJson(command.json(), paymentDetail); changes.putAll(this.loanWritePlatformService.makeLoanBulkRepayment(bulkRepaymentCommand)); return changes; } @@ -98,20 +135,21 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col return changes; } - private Map updateBulkMandatorySavingsDuePayments(final JsonCommand command) { + private Map updateBulkMandatorySavingsDuePayments(final JsonCommand command, final PaymentDetail paymentDetail) { final Map changes = new HashMap<>(); - final Collection savingsTransactions = this.accountAssembler.assembleBulkMandatorySavingsAccountTransactionDTOs(command); - + final Collection savingsTransactions = this.accountAssembler + .assembleBulkMandatorySavingsAccountTransactionDTOs(command, paymentDetail); + List depositTransactionIds = new ArrayList<>(); for (SavingsAccountTransactionDTO savingsAccountTransactionDTO : savingsTransactions) { try { - this.accountWritePlatformService.mandatorySavingsAccountDeposit(savingsAccountTransactionDTO); - changes.put("savingsAccountId", savingsAccountTransactionDTO.getSavingsAccountId()); - changes.put("transationAmount", savingsAccountTransactionDTO.getTransactionAmount()); + SavingsAccountTransaction savingsAccountTransaction = this.accountWritePlatformService.mandatorySavingsAccountDeposit(savingsAccountTransactionDTO); + depositTransactionIds.add(savingsAccountTransaction.getId()); } catch (Exception e) { // TODO: handle exception } } - + changes.put("SavingsTransactions", depositTransactionIds); return changes; } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 2e2fb9967..17e834bfd 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -9,7 +9,6 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -124,7 +123,6 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; import org.mifosplatform.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException; -import org.mifosplatform.portfolio.loanaccount.exception.InvalidRefundDateException; import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; @@ -761,21 +759,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final boolean isRecoveryRepayment = false; if (repaymentCommand == null) { return changes; } + List transactionIds = new ArrayList<>(); boolean isAccountTransfer = false; for (final SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) { - /**** - * TODO Vishwas, have a re-look at this implementation, defaulting - * it to null for now - ***/ final Loan loan = this.loanAssembler.assembleFrom(singleLoanRepaymentCommand.getLoanId()); - final PaymentDetail paymentDetail = null; + final PaymentDetail paymentDetail = singleLoanRepaymentCommand.getPaymentDetail(); + if (paymentDetail != null && paymentDetail.getId() == null) { + this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); + } final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); - this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, bulkRepaymentCommand.getTransactionDate(), + LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, bulkRepaymentCommand.getTransactionDate(), singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail, bulkRepaymentCommand.getNote(), null, isRecoveryRepayment, isAccountTransfer); + transactionIds.add(loanTransaction.getId()); - changes.put("bulkTransactions", singleLoanRepaymentCommand); } + changes.put("loanTransactions", transactionIds); return changes; } @@ -1971,19 +1970,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } - + @Transactional @Override public void applyMeetingDateChanges(final Calendar calendar, final Collection loanCalendarInstances) { - final Boolean reschedulebasedOnMeetingDates = null; + final Boolean reschedulebasedOnMeetingDates = null; final LocalDate presentMeetingDate = null; final LocalDate newMeetingDate = null; - + applyMeetingDateChanges(calendar, loanCalendarInstances, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate); - + } - @Transactional @Override @@ -2011,16 +2009,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf throw new CalendarParameterUpdateNotSupportedException("jlg.loan.recalculation", defaultUserMessage); } holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate()); - - if(reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates ){ + + if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) { loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, holidays, workingDays, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate); - }else{ + } else { loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, - holidays, workingDays); + holidays, workingDays); } - - + saveLoanWithDataIntegrityViolationChecks(loan); } } @@ -2708,7 +2705,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } return installments; } - + @Override @Transactional public CommandProcessingResult makeLoanRefund(Long loanId, JsonCommand command) { @@ -2718,11 +2715,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); - //checkRefundDateIsAfterAtLeastOneRepayment(loanId, transactionDate); + // checkRefundDateIsAfterAtLeastOneRepayment(loanId, transactionDate); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); checkIfLoanIsPaidInAdvance(loanId, transactionAmount); - + final Map changes = new LinkedHashMap(); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); @@ -2748,15 +2745,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } - private void checkIfLoanIsPaidInAdvance(final Long loanId,final BigDecimal transactionAmount) { + private void checkIfLoanIsPaidInAdvance(final Long loanId, final BigDecimal transactionAmount) { BigDecimal overpaid = this.loanReadPlatformService.retrieveTotalPaidInAdvance(loanId).getPaidInAdvance(); - - if(overpaid == null || overpaid.equals(new BigDecimal(0)) || transactionAmount.floatValue() > overpaid.floatValue()) { - if(overpaid == null) - overpaid = BigDecimal.ZERO; + + if (overpaid == null || overpaid.equals(new BigDecimal(0)) || transactionAmount.floatValue() > overpaid.floatValue()) { + if (overpaid == null) overpaid = BigDecimal.ZERO; throw new InvalidPaidInAdvanceAmountException(overpaid.toPlainString()); } - } + } private AppUser getAppUserIfPresent() { AppUser user = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetail.java index 470e47c73..5b9d6529b 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetail.java @@ -76,6 +76,11 @@ public final class PaymentDetail extends AbstractPersistable { return paymentDetail; } + public static PaymentDetail instance(final CodeValue paymentType, final String accountNumber, final String checkNumber, + final String routingCode, final String receiptNumber, final String bankNumber) { + return new PaymentDetail(paymentType, accountNumber, checkNumber, routingCode, receiptNumber, bankNumber); + } + private PaymentDetail(final CodeValue paymentType, final String accountNumber, final String checkNumber, final String routingCode, final String receiptNumber, final String bankNumber) { this.paymentType = paymentType; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetailAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetailAssembler.java new file mode 100755 index 000000000..ee035aacd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/domain/PaymentDetailAssembler.java @@ -0,0 +1,43 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.paymentdetail.domain; + +import org.mifosplatform.infrastructure.codes.domain.CodeValue; +import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonObject; + +@Service +public class PaymentDetailAssembler { + + private final FromJsonHelper fromApiJsonHelper; + private final CodeValueRepositoryWrapper codeValueRepositoryWrapper; + + @Autowired + public PaymentDetailAssembler(final FromJsonHelper fromApiJsonHelper, final CodeValueRepositoryWrapper codeValueRepositoryWrapper) { + this.fromApiJsonHelper = fromApiJsonHelper; + this.codeValueRepositoryWrapper = codeValueRepositoryWrapper; + } + + public PaymentDetail fetchPaymentDetail(final JsonObject json) { + final Long paymentTypeId = this.fromApiJsonHelper.extractLongNamed(PaymentDetailConstants.paymentTypeParamName, json); + if (paymentTypeId == null) { return null; } + + final CodeValue paymentType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection( + PaymentDetailConstants.paymentTypeCodeName, paymentTypeId); + + final String accountNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.accountNumberParamName, json); + final String checkNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.checkNumberParamName, json); + final String routingCode = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.routingCodeParamName, json); + final String receiptNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.receiptNumberParamName, json); + final String bankNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.bankNumberParamName, json); + return PaymentDetail.instance(paymentType, accountNumber, checkNumber, routingCode, receiptNumber, bankNumber); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/service/PaymentDetailWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/service/PaymentDetailWritePlatformServiceJpaRepositoryImpl.java index 7c71d6a61..7d6d998ae 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/service/PaymentDetailWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/paymentdetail/service/PaymentDetailWritePlatformServiceJpaRepositoryImpl.java @@ -31,7 +31,6 @@ public class PaymentDetailWritePlatformServiceJpaRepositoryImpl implements Payme } @Override - @Transactional public PaymentDetail createPaymentDetail(final JsonCommand command, final Map changes) { final Long paymentTypeId = command.longValueOfParameterNamed(PaymentDetailConstants.paymentTypeParamName); if (paymentTypeId == null) { return null; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java index c13b2c11b..84058080a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java @@ -24,8 +24,7 @@ public class SavingsAccountTransactionDTO { private final AppUser appUser; public SavingsAccountTransactionDTO(final DateTimeFormatter formatter, final LocalDate transactionDate, - final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final Date createdDate, - final AppUser appUser) { + final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final Date createdDate, final AppUser appUser) { this.formatter = formatter; this.transactionDate = transactionDate; this.transactionAmount = transactionAmount; @@ -79,9 +78,8 @@ public class SavingsAccountTransactionDTO { public Long getSavingsAccountId() { return this.savingsAccountId; } - - public AppUser getAppUser() { - return this.appUser; - } + public AppUser getAppUser() { + return this.appUser; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java index 3350a7762..e2d286a71 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountAssembler.java @@ -48,6 +48,8 @@ import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.codes.domain.CodeValue; +import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; @@ -66,7 +68,9 @@ import org.mifosplatform.portfolio.group.exception.CenterNotActiveException; import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; +import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetailAssembler; import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; @@ -101,6 +105,7 @@ public class DepositAccountAssembler { private final SavingsAccountChargeAssembler savingsAccountChargeAssembler; private final FromJsonHelper fromApiJsonHelper; private final DepositProductAssembler depositProductAssembler; + private final PaymentDetailAssembler paymentDetailAssembler; @Autowired public DepositAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, @@ -110,7 +115,8 @@ public class DepositAccountAssembler { final SavingsAccountChargeAssembler savingsAccountChargeAssembler, final FromJsonHelper fromApiJsonHelper, final DepositProductAssembler depositProductAssembler, final RecurringDepositProductRepository recurringDepositProductRepository, - final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final PlatformSecurityContext context) { + final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final PlatformSecurityContext context, + final PaymentDetailAssembler paymentDetailAssembler) { this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper; this.clientRepository = clientRepository; @@ -124,6 +130,7 @@ public class DepositAccountAssembler { this.recurringDepositProductRepository = recurringDepositProductRepository; this.savingsHelper = new SavingsHelper(accountTransfersReadPlatformService); this.context = context; + this.paymentDetailAssembler = paymentDetailAssembler; } /** @@ -392,13 +399,12 @@ public class DepositAccountAssembler { return depositAccountRecurringDetail; } - public Collection assembleBulkMandatorySavingsAccountTransactionDTOs(final JsonCommand command) { + public Collection assembleBulkMandatorySavingsAccountTransactionDTOs(final JsonCommand command,final PaymentDetail paymentDetail) { AppUser user = getAppUserIfPresent(); final String json = command.json(); if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final JsonElement element = this.fromApiJsonHelper.parse(json); final Collection savingsAccountTransactions = new ArrayList<>(); - final PaymentDetail paymentDetail = null; final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transactionDateParamName, element); final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(element.getAsJsonObject()); final JsonObject topLevelJsonElement = element.getAsJsonObject(); @@ -415,8 +421,12 @@ public class DepositAccountAssembler { final Long savingsId = this.fromApiJsonHelper.extractLongNamed(savingsIdParamName, savingsTransactionElement); final BigDecimal dueAmount = this.fromApiJsonHelper.extractBigDecimalNamed(transactionAmountParamName, savingsTransactionElement, locale); + PaymentDetail detail = paymentDetail; + if (paymentDetail == null) { + detail = this.paymentDetailAssembler.fetchPaymentDetail(savingsTransactionElement); + } final SavingsAccountTransactionDTO savingsAccountTransactionDTO = new SavingsAccountTransactionDTO(formatter, - transactionDate, dueAmount, paymentDetail, new Date(), savingsId, user); + transactionDate, dueAmount, detail, new Date(), savingsId, user); savingsAccountTransactions.add(savingsAccountTransactionDTO); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index dc7146ba4..23e87ed18 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -1286,9 +1286,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isRegularTransaction = false; final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom( accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT); + final PaymentDetail paymentDetail = accountTransactionDTO.getPaymentDetail(); + if (paymentDetail != null && paymentDetail.getId() == null) { + this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); + } return this.depositAccountDomainService.handleRDDeposit(account, accountTransactionDTO.getFormatter(), - accountTransactionDTO.getTransactionDate(), accountTransactionDTO.getTransactionAmount(), - accountTransactionDTO.getPaymentDetail(), isRegularTransaction); + accountTransactionDTO.getTransactionDate(), accountTransactionDTO.getTransactionAmount(), paymentDetail, + isRegularTransaction); } private AppUser getAppUserIfPresent() { diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V236__individual_collection_sheet_permissions.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V236__individual_collection_sheet_permissions.sql new file mode 100755 index 000000000..effe292ba --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V236__individual_collection_sheet_permissions.sql @@ -0,0 +1,2 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('collection_sheet', 'READ_COLLECTIONSHEET', 'COLLECTIONSHEET', 'READ', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('collection_sheet', 'SAVE_COLLECTIONSHEET', 'COLLECTIONSHEET', 'SAVE', 0); \ No newline at end of file