diff --git a/mifosng-db/migrations/core_db/V37__add-center-group-collection-sheet-permissions.sql b/mifosng-db/migrations/core_db/V37__add-center-group-collection-sheet-permissions.sql new file mode 100644 index 000000000..141f1fc48 --- /dev/null +++ b/mifosng-db/migrations/core_db/V37__add-center-group-collection-sheet-permissions.sql @@ -0,0 +1,3 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'SAVECOLLECTIONSHEET_GROUP', 'GROUP', 'SAVECOLLECTIONSHEET', 0); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'SAVECOLLECTIONSHEET_CENTER', 'CENTER', 'SAVECOLLECTIONSHEET', 0); 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 2300ee8f7..6236f5b83 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -484,6 +484,10 @@ public class CommandWrapper { public boolean isGroupResource() { return this.entityName.equalsIgnoreCase("GROUP"); } + + public boolean isSaveGroupCollectionSheet(){ + return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("SAVECOLLECTIONSHEET"); + } public boolean isCollectionSheetResource() { return this.entityName.equals("COLLECTIONSHEET"); @@ -493,6 +497,10 @@ public class CommandWrapper { return this.entityName.equalsIgnoreCase("CENTER"); } + public boolean isSaveCenterCollectionSheet(){ + return this.entityName.equalsIgnoreCase("CENTER") && this.actionName.equalsIgnoreCase("SAVECOLLECTIONSHEET"); + } + public boolean isReportResource() { return this.entityName.equalsIgnoreCase("REPORT"); } 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 9a21d53c4..65a233ffd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -847,7 +847,16 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId + "?command=activate"; return this; } - + + public CommandWrapperBuilder saveGroupCollectionSheet(final Long groupId) { + this.actionName = "SAVECOLLECTIONSHEET"; + this.entityName = "GROUP"; + this.entityId = groupId; + this.groupId = groupId; + this.href = "/groups/" + groupId + "?command=saveCollectionSheet"; + return this; + } + public CommandWrapperBuilder deleteGroup(final Long groupId) { this.actionName = "DELETE"; this.entityName = "GROUP"; @@ -951,6 +960,14 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder saveCenterCollectionSheet(final Long centerId) { + this.actionName = "SAVECOLLECTIONSHEET"; + this.entityName = "CENTER"; + this.entityId = centerId; + this.href = "/centers/" + centerId + "?command=saveCollectionSheet"; + return this; + } + public CommandWrapperBuilder createAccountingRule() { this.actionName = "CREATE"; this.entityName = "ACCOUNTINGRULE"; 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 dbd58bb6a..7e29b25e9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -404,6 +404,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = applicationContext.getBean("associateClientsToGroupCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDisassociateClients()) { handler = applicationContext.getBean("disassociateClientsFromGroupCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isSaveGroupCollectionSheet()) { + handler = applicationContext.getBean("saveGroupCollectionSheetCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } @@ -416,6 +418,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = applicationContext.getBean("deleteCenterCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isCenterActivation()) { handler = applicationContext.getBean("activateCenterCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isSaveCenterCollectionSheet()) { + handler = applicationContext.getBean("saveCenterCollectionSheetCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResource.java deleted file mode 100644 index 355ab8442..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/api/CollectionSheetApiResource.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -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.joda.time.LocalDate; -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.data.ApiParameterError; -import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; -import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; -import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; -import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; -import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; -import org.mifosplatform.infrastructure.core.service.DateUtils; -import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; -import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; -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; - -/* - * At present API supports groups/{groupId} - * Future version supports staffs/{staffId} - * Future version supports offices/{officeId} - */ - -@Path("/groups/{groupId}/collectionsheet") -@Component -@Scope("singleton") -public class CollectionSheetApiResource { - - private final Set COLLECTIONSHEET_DATA_PARAMETERS = new HashSet(Arrays.asList("dueDate", "loanProducts", "groups")); - /* - * dueDate format is ISO 8601 Calendar Date (YYYYMMDD) - */ - private final String datePattern = "yyyyMMdd"; - private final PlatformSecurityContext context; - private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; - private final ToApiJsonSerializer toApiJsonSerializer; - private final ApiRequestParameterHelper apiRequestParameterHelper; - private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - - @Autowired - public CollectionSheetApiResource(final PlatformSecurityContext context, - final CollectionSheetReadPlatformService collectionSheetReadPlatformService, - final ToApiJsonSerializer toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { - - this.context = context; - this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; - this.toApiJsonSerializer = toApiJsonSerializer; - this.apiRequestParameterHelper = apiRequestParameterHelper; - this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; - } - - @GET - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String collectionSheetData(@Context final UriInfo uriInfo, @PathParam("groupId") final Long groupId, - @QueryParam("dueDate") final String dueDate) { - - this.context.authenticatedUser().validateHasReadPermission("COLLECTIONSHEET"); - - validateRequest(groupId, dueDate); - - LocalDate localDate = null; - if(StringUtils.isBlank(dueDate)){ - localDate = DateUtils.getLocalDateOfTenant(); - }else{ - localDate = DateUtils.parseLocalDate(dueDate, datePattern); - } - - - final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.retriveCollectionSheet(localDate, groupId); - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, collectionSheet, this.COLLECTIONSHEET_DATA_PARAMETERS); - - } - - private void validateRequest(Long groupId, String dueDate) { - final List dataValidationErrors = new ArrayList(); - - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("collectionsheet"); - - baseDataValidator.reset().parameter("groupId").value(groupId).notBlank().longGreaterThanZero(); - - baseDataValidator.reset().parameter("dueDate").value(dueDate).notBlank(); - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", - "Validation errors exist.", dataValidationErrors); } - - } - - @POST - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String updateCalendar(@PathParam("groupId") final Long groupId, final String jsonRequestBody) { - - final CommandWrapper commandRequest = new CommandWrapperBuilder().updateCollectionSheet(groupId).withJson(jsonRequestBody).build(); - - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); - } -} \ No newline at end of file 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 new file mode 100644 index 000000000..8076ffe3f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/serialization/CollectionSheetGenerateCommandFromApiJsonDeserializer.java @@ -0,0 +1,69 @@ +/** + * 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.serialization; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public class CollectionSheetGenerateCommandFromApiJsonDeserializer { + + /** + * The parameters supported for this command. + */ + final Set supportedParameters = new HashSet(Arrays.asList("dueDate", "locale", "dateFormat")); + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public CollectionSheetGenerateCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validateForGenerateCollectionSheet(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("collectionsheet"); + + final String dueDateParameterName = "dueDate"; + final String dueDateStr = this.fromApiJsonHelper.extractStringNamed(dueDateParameterName, element); + baseDataValidator.reset().parameter(dueDateParameterName).value(dueDateStr).notBlank(); + + if(!StringUtils.isBlank(dueDateStr)){ + final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(dueDateParameterName, element); + baseDataValidator.reset().parameter(dueDateParameterName).value(dueDate).notNull(); + } + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformService.java index fd858c2f5..333bd336f 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 @@ -8,12 +8,17 @@ package org.mifosplatform.portfolio.collectionsheet.service; import java.util.Collection; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonQuery; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetFlatData; public interface CollectionSheetReadPlatformService { JLGCollectionSheetData retriveCollectionSheet(LocalDate localDate, Long groupId); + + JLGCollectionSheetData generateGroupCollectionSheet(final Long groupId, final JsonQuery query); + + JLGCollectionSheetData generateCenterCollectionSheet(final Long groupId, final JsonQuery query); Collection retriveJLGCollectionSheet(String groupHierarchy, String officeHierarchy, LocalDate dueDate); 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 dae5f2868..4b910fc0d 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 @@ -17,6 +17,7 @@ import java.util.List; import java.util.Set; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonQuery; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; @@ -26,8 +27,11 @@ import org.mifosplatform.portfolio.collectionsheet.data.JLGClientsData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetFlatData; import org.mifosplatform.portfolio.collectionsheet.data.LoanDueData; +import org.mifosplatform.portfolio.collectionsheet.serialization.CollectionSheetGenerateCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.group.data.CenterData; +import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; +import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; @@ -43,13 +47,17 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private final PlatformSecurityContext context; private final NamedParameterJdbcTemplate namedParameterjdbcTemplate; private final CenterReadPlatformService centerReadPlatformService; + private final GroupReadPlatformService groupReadPlatformService; + private final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer; @Autowired public CollectionSheetReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, - final CenterReadPlatformService centerReadPlatformService) { + final CenterReadPlatformService centerReadPlatformService, final GroupReadPlatformService groupReadPlatformService, final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource); + this.collectionSheetGenerateCommandFromApiJsonDeserializer = collectionSheetGenerateCommandFromApiJsonDeserializer; + this.groupReadPlatformService = groupReadPlatformService; } @Override @@ -61,8 +69,10 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final CenterData center = this.centerReadPlatformService.retrieveOne(centerId); final String groupHierarchy = center.getHierarchy() + "%"; - - final JLGCollectionSheetData jlgCollectionSheetData = buildJLGCollectionSheet(groupHierarchy, officeHierarchy, dueDate); + + Collection collectionSheetFlatDatas = this.retriveJLGCollectionSheet(groupHierarchy, officeHierarchy, dueDate); + + final JLGCollectionSheetData jlgCollectionSheetData = buildJLGCollectionSheet(dueDate, collectionSheetFlatDatas); return jlgCollectionSheetData; } @@ -73,11 +83,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe * Groups >> Clients >> Loans. */ @SuppressWarnings("null") - private JLGCollectionSheetData buildJLGCollectionSheet(final String groupHierarchy, final String officeHierarchy, - final LocalDate dueDate) { - - final Collection jlgCollectionSheetFlatData = retriveJLGCollectionSheet(groupHierarchy, - officeHierarchy, dueDate); + private JLGCollectionSheetData buildJLGCollectionSheet(final LocalDate dueDate, final Collection jlgCollectionSheetFlatData) { boolean firstTime = true; Long prevGroupId = null; @@ -191,13 +197,14 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "ln.product_id As productId, " + "ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + "if(ln.loan_status_id = 200 , ln.principal_amount , null) As disbursementAmount, " - + "sum(ifnull(ls.principal_amount, 0.0) - ifnull(ls.principal_completed_derived, 0.0)) As principalDue, " + + "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, " + "ln.principal_repaid_derived As principalPaid, " - + "sum(ifnull(ls.interest_amount, 0.0) - ifnull(ls.interest_completed_derived, 0.0)) As interestDue, " + + "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, " + "ln.interest_repaid_derived As interestPaid, " - + "sum(lc.amount_outstanding_derived) as chargesDue " + + "sum(if(ln.loan_status_id = 300, lc.amount_outstanding_derived, 0.0)) as chargesDue " + "FROM m_group gp " + "LEFT JOIN m_office of ON of.id = gp.office_id AND of.hierarchy like :officeHierarchy " + //+ "LEFT JOIN m_office of ON of.id = gp.office_id " + "JOIN m_group_level gl ON gl.id = gp.level_Id " + "LEFT JOIN m_staff sf ON sf.id = gp.staff_id " + "JOIN m_group_client gc ON gc.group_id = gp.id " @@ -206,8 +213,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "LEFT JOIN m_product_loan pl ON pl.id = ln.product_id " + "JOIN m_currency rc on rc.`code` = ln.currency_code " + "LEFT JOIN m_loan_repayment_schedule ls ON ls.loan_id = ln.id AND ls.completed_derived = 0 AND ls.duedate <= :dueDate " - + "LEFT JOIN m_loan_charge lc ON lc.loan_id = ln.id AND lc.is_paid_derived = 0 AND ( lc.due_for_collection_as_of_date <= :dueDate OR lc.charge_time_enum = 1) " - + "WHERE gp.hierarchy like :hierarchy " + "GROUP BY gp.id ,cl.id , ln.id " + "ORDER BY gp.id , cl.id , ln.id "; + + "LEFT JOIN m_loan_charge lc ON lc.loan_id = ln.id AND lc.is_paid_derived = 0 AND ( lc.due_for_collection_as_of_date <= :dueDate OR lc.charge_time_enum = 1) "; + //+ "WHERE gp.hierarchy like :hierarchy " + "GROUP BY gp.id ,cl.id , ln.id " + "ORDER BY gp.id , cl.id , ln.id "; } @Override @@ -248,4 +255,63 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe } } + + @Override + public JLGCollectionSheetData generateGroupCollectionSheet(final Long groupId, final JsonQuery query) { + + this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheet(query.json()); + + final AppUser currentUser = this.context.authenticatedUser(); + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String officeHierarchy = hierarchy + "%"; + + final GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId); + + final LocalDate dueDate = query.localDateValueOfParameterNamed("dueDate"); + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + final String dueDateStr = df.format(dueDate.toDate()); + + final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(); + + String sql = mapper.collectionSheetSchema(); + sql += "WHERE gp.id = :groupId GROUP BY gp.id ,cl.id , ln.id ORDER BY gp.id , cl.id , ln.id"; + + final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", dueDateStr).addValue("groupId", group.getId()) + .addValue("officeHierarchy", officeHierarchy); + + final Collection collectionSheetFlatDatas = this.namedParameterjdbcTemplate.query(sql, namedParameters, + mapper); + + return buildJLGCollectionSheet(dueDate, collectionSheetFlatDatas); + } + + @Override + public JLGCollectionSheetData generateCenterCollectionSheet(final Long centerId, final JsonQuery query) { + + this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheet(query.json()); + + final AppUser currentUser = this.context.authenticatedUser(); + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String officeHierarchy = hierarchy + "%"; + + final CenterData center = this.centerReadPlatformService.retrieveOne(centerId); + + final LocalDate dueDate = query.localDateValueOfParameterNamed("dueDate"); + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + final String dueDateStr = df.format(dueDate.toDate()); + + final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(); + + String sql = mapper.collectionSheetSchema(); + sql += "WHERE gp.parent_id = :centerId GROUP BY gp.id ,cl.id , ln.id ORDER BY gp.id , cl.id , ln.id"; + + final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", dueDateStr).addValue("centerId", center.getId()) + .addValue("officeHierarchy", officeHierarchy); + + final Collection collectionSheetFlatDatas = this.namedParameterjdbcTemplate.query(sql, namedParameters, + mapper); + + return buildJLGCollectionSheet(dueDate, collectionSheetFlatDatas); + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java index 3fc6918ae..9ad7f4f31 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java @@ -28,12 +28,16 @@ import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; 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.exception.UnrecognizedQueryParamException; 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.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; +import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; import org.mifosplatform.portfolio.group.data.CenterData; import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; @@ -43,6 +47,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import com.google.gson.JsonElement; + @Path("/centers") @Component @Scope("singleton") @@ -56,6 +62,8 @@ public class CentersApiResource { private final ToApiJsonSerializer groupSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; + private final FromJsonHelper fromJsonHelper; @Autowired public CentersApiResource(final PlatformSecurityContext context, final CenterReadPlatformService centerReadPlatformService, @@ -63,7 +71,8 @@ public class CentersApiResource { final ToApiJsonSerializer toApiJsonSerializer, final ToApiJsonSerializer groupSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, - final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.groupReadPlatformService = groupReadPlatformService; @@ -72,6 +81,8 @@ public class CentersApiResource { this.groupSummaryToApiJsonSerializer = groupSummaryToApiJsonSerializer; this.apiRequestParameterHelper = apiRequestParameterHelper; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; + this.fromJsonHelper = fromJsonHelper; } @GET @@ -178,7 +189,7 @@ public class CentersApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) public String activate(@PathParam("centerId") final Long centerId, @QueryParam("command") final String commandParam, - final String apiRequestBodyAsJson) { + final String apiRequestBodyAsJson, @Context final UriInfo uriInfo) { final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); @@ -186,14 +197,21 @@ public class CentersApiResource { if (is(commandParam, "activate")) { final CommandWrapper commandRequest = builder.activateCenter(centerId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + }else if (is(commandParam, "generateCollectionSheet")) { + final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); + final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); + final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.generateCenterCollectionSheet(centerId, query); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, collectionSheet, GroupingTypesApiConstants.COLLECTIONSHEET_DATA_PARAMETERS); + }else if (is(commandParam, "saveCollectionSheet")) { + final CommandWrapper commandRequest = builder.saveCenterCollectionSheet(centerId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } else { // + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet" }); } - if (result == null) { - // - throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate" }); - } - - return this.toApiJsonSerializer.serialize(result); } private boolean is(final String commandParam, final String commandValue) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java index f15179d88..d322c1976 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java @@ -71,4 +71,7 @@ public class GroupingTypesApiConstants { public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, activationDateParamName)); + + + public static final Set COLLECTIONSHEET_DATA_PARAMETERS = new HashSet(Arrays.asList("dueDate", "loanProducts", "groups")); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index cf994e6f8..f1833ad25 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -29,14 +29,18 @@ import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; 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.exception.UnrecognizedQueryParamException; 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.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; +import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; @@ -47,6 +51,8 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import com.google.gson.JsonElement; + @Path("/groups") @Component @Scope("singleton") @@ -61,6 +67,8 @@ public class GroupsApiResource { private final ToApiJsonSerializer groupSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; + private final FromJsonHelper fromJsonHelper; @Autowired public GroupsApiResource(final PlatformSecurityContext context, final GroupReadPlatformService groupReadPlatformService, @@ -69,7 +77,8 @@ public class GroupsApiResource { final ToApiJsonSerializer groupTopOfHierarchyApiJsonSerializer, final ToApiJsonSerializer groupSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, - final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper) { this.context = context; this.groupReadPlatformService = groupReadPlatformService; this.centerReadPlatformService = centerReadPlatformService; @@ -79,6 +88,8 @@ public class GroupsApiResource { this.groupSummaryToApiJsonSerializer = groupSummaryToApiJsonSerializer; this.apiRequestParameterHelper = apiRequestParameterHelper; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; + this.fromJsonHelper = fromJsonHelper; } @GET @@ -214,8 +225,8 @@ public class GroupsApiResource { @Path("{groupId}") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String activate(@PathParam("groupId") final Long groupId, @QueryParam("command") final String commandParam, - final String apiRequestBodyAsJson) { + public String activateOrGenerateCollectionSheet(@PathParam("groupId") final Long groupId, @QueryParam("command") final String commandParam, + final String apiRequestBodyAsJson, @Context final UriInfo uriInfo) { final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); @@ -223,20 +234,29 @@ public class GroupsApiResource { if (is(commandParam, "activate")) { final CommandWrapper commandRequest = builder.activateGroup(groupId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); }else if (is(commandParam, "associateClients")) { final CommandWrapper commandRequest = builder.associateClientsToGroup(groupId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); }else if (is(commandParam, "disassociateClients")) { final CommandWrapper commandRequest = builder.disassociateClientsFromGroup(groupId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + }else if (is(commandParam, "generateCollectionSheet")) { + final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); + final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); + final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.generateGroupCollectionSheet(groupId, query); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, collectionSheet, GroupingTypesApiConstants.COLLECTIONSHEET_DATA_PARAMETERS); + }else if (is(commandParam, "saveCollectionSheet")) { + final CommandWrapper commandRequest = builder.saveGroupCollectionSheet(groupId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } else { + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "saveCollectionSheet" }); } - - if (result == null) { - // - throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate" }); - } - - return this.toApiJsonSerializer.serialize(result); + } private boolean is(final String commandParam, final String commandValue) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java index 5d50c7e9c..36b50da77 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java @@ -147,4 +147,9 @@ public class GroupGeneralData { public Long officeId() { return this.officeId; } + + public String getHierarchy() { + return this.hierarchy; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/CenterNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/CenterNotFoundException.java new file mode 100644 index 000000000..5fab9a14e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/CenterNotFoundException.java @@ -0,0 +1,19 @@ +/** + * 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.group.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when group resources are not found. + */ +public class CenterNotFoundException extends AbstractPlatformResourceNotFoundException { + + public CenterNotFoundException(final Long id) { + super("error.msg.center.id.invalid", "Center with identifier " + id + " does not exist", id); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/SaveCenterCollectionSheetCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/SaveCenterCollectionSheetCommandHandler.java new file mode 100644 index 000000000..6efd6d55a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/SaveCenterCollectionSheetCommandHandler.java @@ -0,0 +1,32 @@ +/** + * 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.group.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 SaveCenterCollectionSheetCommandHandler implements NewCommandSourceHandler { + + private final CollectionSheetWritePlatformService writePlatformService; + + @Autowired + public SaveCenterCollectionSheetCommandHandler(final CollectionSheetWritePlatformService collectionSheetWritePlatformService) { + this.writePlatformService = collectionSheetWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.updateCollectionSheet(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/SaveGroupCollectionSheetCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/SaveGroupCollectionSheetCommandHandler.java new file mode 100644 index 000000000..461bad292 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/SaveGroupCollectionSheetCommandHandler.java @@ -0,0 +1,32 @@ +/** + * 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.group.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 SaveGroupCollectionSheetCommandHandler implements NewCommandSourceHandler { + + private final CollectionSheetWritePlatformService writePlatformService; + + @Autowired + public SaveGroupCollectionSheetCommandHandler(final CollectionSheetWritePlatformService collectionSheetWritePlatformService) { + this.writePlatformService = collectionSheetWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.updateCollectionSheet(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java index bfa5e3cd6..6d5ff3f6a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java @@ -30,7 +30,7 @@ import org.mifosplatform.portfolio.group.data.CenterData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.domain.GroupTypes; import org.mifosplatform.portfolio.group.domain.GroupingTypeEnumerations; -import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; +import org.mifosplatform.portfolio.group.exception.CenterNotFoundException; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; @@ -289,7 +289,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService return this.jdbcTemplate.queryForObject(sql, this.centerMapper, new Object[] { centerId, hierarchySearchString }); } catch (EmptyResultDataAccessException e) { - throw new GroupNotFoundException(centerId); + throw new CenterNotFoundException(centerId); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index c525ff66b..c054887ad 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1394,16 +1394,16 @@ public class Loan extends AbstractPersistable { } if (disbursedOn.isAfter(new LocalDate())) { - final String errorMessage = "The date on which a loan is disbursed cannot be in the future."; + final String errorMessage = "The date on which a loan with identifier : " + this.accountNumber + " is disbursed cannot be in the future."; throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.a.future.date", errorMessage, disbursedOn); } LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments.get(0).getDueDate(); if (disbursedOn.isAfter(firstRepaymentDueDate)) { - final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: " + final String errorMessage = "The date on which a loan with identifier : " + this.accountNumber + " is disbursed cannot be after the first expected repayment date: " + firstRepaymentDueDate.toString(); throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.first.repayment.due.date", errorMessage, - disbursedOn, firstRepaymentDueDate); + this.accountNumber, disbursedOn, firstRepaymentDueDate); } return disbursementTransaction;