diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 11e6ee558..d8efbfadf 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -1846,11 +1846,17 @@ Working days + workingdays + List workingdays + Working days template + + + workingdays/{workingdaysId} - + Update a workingdays User Generated Documents @@ -13737,6 +13743,31 @@ Request Body:

Allow transactions on non-working days is configurable by enabling/disbaling the allow-transactions-on-non_workingday parameter in Global configurations.

+ + + + + + + + + + + + + + + + + + + + + + + +
Mandotory Field Descriptions
recurrence
Recurrence pattern
repaymentRescheduleType
repayment reschedule type .Options listed on the schedule for repayments on non-working days +
locale
locale must be provided.
@@ -13772,6 +13803,111 @@ Request Body:
Schedule for repayments on non-working day
+   +
+
+

Working Days Template

+

This is a convenience resource. It can be useful when + building maintenance user interface screens for working days. +

+

Example Request:

+
workingdays/template
+
+
+ +GET https://DomainName/api/v1/workingdays/template + + +{ + "repaymentRescheduleOptions": [ + { + "id": 1, + "code": "RepaymentRescheduleType.same.day", + "value": "same day" + }, + { + "id": 4, + "code": "RepaymentRescheduleType.move.to.previous.working.day", + "value": "move to previous working day" + }, + { + "id": 3, + "code": "RepaymentRescheduleType.move.to.next.repayment.meeting.day", + "value": "move to next repayment meeting day" + }, + { + "id": 2, + "code": "RepaymentRescheduleType.move.to.next.working.day", + "value": "move to next working day" + } + ] +} + +
+
+   +
+
+

List Working days

+

Example Requests:

+
workingdays
+
+
+
+
+ +GET https://DomainName/api/v1/workingdays + + +[ + { + "id": 1, + "recurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR", + "repaymentRescheduleType": { + "id": 4, + "code": "RepaymentRescheduleType.move.to.next.working.day", + "value": "move to next working day" + } + } +] + +
+
+   +
+
+

Update a Working Day

+ + + + + + + +
Mandatory Fields
recurrence,repaymentRescheduleType,locale
+
+
+ +PUT https://DomainName/api/v1/Workingdays/1 + + +PUT holidays +Content-Type: application/json +Request Body: +{ +"recurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,SU", +"locale": "en", +"repaymentsRescheduledType": 4 +} + + +{ +"resourceId": 1 +} + +
+
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ConcurrencyIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ConcurrencyIntegrationTest.java old mode 100644 new mode 100755 index 4d44c925f..fc1d3dde2 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ConcurrencyIntegrationTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/ConcurrencyIntegrationTest.java @@ -1,3 +1,8 @@ +/** + * 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.integrationtests; import java.text.DateFormat; diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/WorkingDaysTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/WorkingDaysTest.java new file mode 100755 index 000000000..f5fa117f8 --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/WorkingDaysTest.java @@ -0,0 +1,55 @@ +/** + * 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.integrationtests; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mifosplatform.integrationtests.common.CommonConstants; +import org.mifosplatform.integrationtests.common.Utils; +import org.mifosplatform.integrationtests.common.WorkingDaysHelper; + +import java.util.HashMap; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class WorkingDaysTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private ResponseSpecification generalResponseSpec; + + @Before + public void setUp(){ + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.generalResponseSpec = new ResponseSpecBuilder().build(); + + } + + @Test + public void updateWorkingDays(){ + HashMap response = (HashMap)WorkingDaysHelper.updateWorkingDays(requestSpec, responseSpec); + Assert.assertNotNull(response.get("resourceId")); + } + + @Test + public void updateWorkingDaysWithWrongRecurrencePattern(){ + final List error = (List) WorkingDaysHelper.updateWorkingDaysWithWrongRecurrence(requestSpec, generalResponseSpec, CommonConstants.RESPONSE_ERROR); + assertEquals("Verify wrong recurrence pattern error", "error.msg.recurring.rule.parsing.error", error.get(0).get("userMessageGlobalisationCode")); + } + + +} diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java new file mode 100755 index 000000000..92115506f --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java @@ -0,0 +1,78 @@ +/** + * 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.integrationtests.common; + + +import com.google.gson.Gson; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; + +public class WorkingDaysHelper { + private static final String WORKINGDAYS_URL = "/mifosng-provider/api/v1/workingdays"; + private static final String CREATE_HOLIDAY_URL = WORKINGDAYS_URL + "?" + Utils.TENANT_IDENTIFIER; + + + public static Object updateWorkingDays(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) { + final String UPDATE_WORKINGDAYS_URL = WORKINGDAYS_URL +"/" +workingDaysId(requestSpec,responseSpec) + "?" + Utils.TENANT_IDENTIFIER; + System.out.println("---------------------------------UPDATE WORKINGDAY---------------------------------------------"); + return Utils.performServerPut(requestSpec, responseSpec,UPDATE_WORKINGDAYS_URL, updateWorkingDaysAsJson(), ""); + } + + + public static Object updateWorkingDaysWithWrongRecurrence(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,String jsonAttributeToGetback){ + final String UPDATE_WORKINGDAYS_URL = WORKINGDAYS_URL +"/" +workingDaysId(requestSpec,responseSpec) + "?" + Utils.TENANT_IDENTIFIER; + System.out.println("---------------------------------UPDATE WORKINGDAY WITH WRONG RECURRENCE-----------------------------------------"); + return Utils.performServerPut(requestSpec, responseSpec,UPDATE_WORKINGDAYS_URL, updateWorkingDayWithWrongRecur(), jsonAttributeToGetback); + } + + + public static String updateWorkingDaysAsJson(){ + final HashMap map = new HashMap<>(); + map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH"); + map.put("locale", "en"); + map.put("repaymentRescheduleType", randomInt(1,4)); + System.out.println("map : " + map); + return new Gson().toJson(map); + } + + + public static String updateWorkingDayWithWrongRecur(){ + final HashMap map = new HashMap<>(); + map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MP,TI,TE,TH"); + map.put("locale", "en"); + map.put("repaymentRescheduleType", randomInt(1,4)); + System.out.println("map : " + map); + return new Gson().toJson(map); + } + + + public static int randomInt(int low, int high){ + int i = new Random().nextInt(high) + low; + return i; + } + + public static int workingDaysId(final RequestSpecification requestSpec,final ResponseSpecification responseSpec){ + List> workingDaysId = getAllWorkingDays(requestSpec,responseSpec); + final HashMap workingDay = workingDaysId.get(0); + return (int) workingDay.get("id"); + } + + + public static ArrayList> getAllWorkingDays( + final RequestSpecification requestSpec, + final ResponseSpecification responseSpec) { + + return Utils.performServerGet(requestSpec, responseSpec, WORKINGDAYS_URL + "?" + + Utils.TENANT_IDENTIFIER, ""); + + } + +} 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 f088e3488..af213f405 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 @@ -1012,8 +1012,13 @@ public class CommandWrapper { public boolean isEnableOperation() { return this.actionName.equalsIgnoreCase("ENABLE"); } - + public boolean isEntityMappingResource() { return this.entityName.equalsIgnoreCase("ENTITYMAPPING"); } + + public boolean isWorkingDaysResource() { + return this.entityName.equalsIgnoreCase("WORKINGDAYS"); + } + } \ No newline at end of file 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 987e2cb57..43a6bf67f 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 @@ -2353,4 +2353,12 @@ public class CommandWrapperBuilder { this.href = "/entitytoentitymapping/" + mapId; return this; } + public CommandWrapperBuilder updateWorkingDays(final Long workingDaysId) { + this.actionName = "UPDATE"; + this.entityName = "WORKINGDAYS"; + this.entityId = workingDaysId; + this.href = "/workingdays/" + workingDaysId; + return this; + } + } \ No newline at end of file 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 a891ed8bd..a094c43b0 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 @@ -923,6 +923,12 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else { throw new UnsupportedCommandException(wrapper.commandName()); } + } else if(wrapper.isWorkingDaysResource()) { + if(wrapper.isUpdate()){ + handler = this.applicationContext.getBean("updateWorkingDaysCommandHandler",NewCommandSourceHandler.class); + }else { + throw new UnsupportedCommandException(wrapper.commandName()); + } } else { throw new UnsupportedCommandException(wrapper.commandName()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java new file mode 100755 index 000000000..f18164bac --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java @@ -0,0 +1,36 @@ +/** + * 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.organisation.workingdays.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class WorkingDaysApiConstants { + + public static final String WORKING_DAYS_RESOURCE_NAME = "workingdays"; + + // request parameters + public static final String recurrence = "recurrence"; + + public static final String repayment_rescheduling_enum = "repaymentRescheduleType"; + + public static final String idParamName = "id"; + + public static final String rescheduleRepaymentTemplate = "rescheduleRepaymentTemplate"; + public static final String localeParamName = "locale"; + + + + public static final Set WORKING_DAYS_CREATE_OR_UPDATE_REQUEST_DATA_PARAMETERS =new HashSet<>(Arrays.asList( + recurrence,repayment_rescheduling_enum,localeParamName + )); + public static final Set WORKING_DAYS_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, + recurrence,repayment_rescheduling_enum + )); + + public static final Set WORKING_DAYS_TEMPLATE_PARAMETERS = new HashSet<>(Arrays.asList(rescheduleRepaymentTemplate)); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiResource.java new file mode 100755 index 000000000..e2557dde4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiResource.java @@ -0,0 +1,109 @@ +/** + * 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.organisation.workingdays.api; + +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.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.workingdays.data.WorkingDaysData; +import org.mifosplatform.organisation.workingdays.service.WorkingDaysReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +import java.util.Collection; + +@Path("/workingdays") +@Component +@Scope("singleton") +public class WorkingDaysApiResource { + + + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final WorkingDaysReadPlatformService workingDaysReadPlatformService; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final PlatformSecurityContext context; + private final ApiRequestParameterHelper apiRequestParameterHelper; + + + @Autowired + public WorkingDaysApiResource(DefaultToApiJsonSerializer toApiJsonSerializer, WorkingDaysReadPlatformService workingDaysReadPlatformService, PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, PlatformSecurityContext context, ApiRequestParameterHelper apiRequestParameterHelper) { + this.toApiJsonSerializer = toApiJsonSerializer; + this.workingDaysReadPlatformService = workingDaysReadPlatformService; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.context = context; + this.apiRequestParameterHelper = apiRequestParameterHelper; + } + + + + + @GET + @Path("{workingdaysId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("workingdaysId") final Long workingDaysId, @Context final UriInfo uriInfo){ + this.context.authenticatedUser().validateHasReadPermission(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); + + final WorkingDaysData workingDaysData = this.workingDaysReadPlatformService.retrieveOne(workingDaysId); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings,workingDaysData); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo){ + this.context.authenticatedUser().validateHasReadPermission(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); + final Collection workingDaysData = this.workingDaysReadPlatformService.retrieveAll(); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings,workingDaysData); + } + + + + @PUT + @Path("{workingdaysId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String update(@PathParam("workingdaysId") final Long workingDaysId,final String jsonRequestBody, @Context final UriInfo uriInfo){ + this.context.authenticatedUser().validateHasReadPermission(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateWorkingDays(workingDaysId).withJson(jsonRequestBody).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + + @GET + @Path("/template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@Context final UriInfo uriInfo){ + this.context.authenticatedUser().validateHasReadPermission(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); + + final WorkingDaysData repaymentRescheduleOptions = this.workingDaysReadPlatformService.repaymentRescheduleType(); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings,repaymentRescheduleOptions,WorkingDaysApiConstants.WORKING_DAYS_TEMPLATE_PARAMETERS); + } + + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java new file mode 100755 index 000000000..1f0d2868b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java @@ -0,0 +1,70 @@ +/** + * 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.organisation.workingdays.data; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +import org.apache.commons.lang.StringUtils; +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.mifosplatform.organisation.workingdays.api.WorkingDaysApiConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Component +public class WorkingDayValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public WorkingDayValidator(FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + + public void validateForUpdate(final String json){ + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + WorkingDaysApiConstants.WORKING_DAYS_CREATE_OR_UPDATE_REQUEST_DATA_PARAMETERS); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList<>(); + + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource( WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); + + final String recurrence = this.fromApiJsonHelper.extractStringNamed(WorkingDaysApiConstants.recurrence, element); + baseDataValidator.reset().parameter(WorkingDaysApiConstants.recurrence).value(recurrence).notNull(); + + final Integer repaymentRescheduleType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("repaymentRescheduleType", element); + baseDataValidator.reset().parameter("repaymentRescheduleType").value(repaymentRescheduleType).ignoreIfNull().inMinMaxRange(1,4); + + + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + } + + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { + // + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java new file mode 100755 index 000000000..9955936f5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java @@ -0,0 +1,39 @@ +/** + * 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.organisation.workingdays.data; + + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +import java.util.Collection; + +public class WorkingDaysData { + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final String recurrence; + + @SuppressWarnings("unused") + private final EnumOptionData repaymentRescheduleType; + + //template date + private final Collection repaymentRescheduleOptions; + + + public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType) { + this.id = id; + this.recurrence = recurrence; + this.repaymentRescheduleType = repaymentRescheduleType; + this.repaymentRescheduleOptions = null; + } + + public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, Collection repaymentRescheduleOptions) { + this.id = null; + this.recurrence = null; + this.repaymentRescheduleType = null; + this.repaymentRescheduleOptions = repaymentRescheduleOptions; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java old mode 100644 new mode 100755 index 664d1f79a..4e2383952 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java @@ -6,11 +6,14 @@ package org.mifosplatform.organisation.workingdays.domain; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.springframework.data.jpa.domain.AbstractPersistable; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; - -import org.springframework.data.jpa.domain.AbstractPersistable; +import java.util.LinkedHashMap; +import java.util.Map; @Entity @Table(name = "m_working_days") @@ -45,4 +48,24 @@ public class WorkingDays extends AbstractPersistable { return this.repaymentReschedulingType; } + + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap<>(7); + + final String recurrenceParamName = "recurrence"; + if (command.isChangeInStringParameterNamed(recurrenceParamName, this.recurrence)) { + final String newValue = command.stringValueOfParameterNamed(recurrenceParamName); + actualChanges.put(recurrenceParamName, newValue); + this.recurrence = newValue; + } + + final String repaymentRescheduleTypeParamName = "repaymentRescheduleType"; + if (command.isChangeInIntegerParameterNamed(repaymentRescheduleTypeParamName, this.repaymentReschedulingType)) { + final Integer newValue =command.integerValueOfParameterNamed(repaymentRescheduleTypeParamName); + actualChanges.put(repaymentRescheduleTypeParamName, WorkingDaysEnumerations.workingDaysStatusType(newValue)); + this.repaymentReschedulingType = RepaymentRescheduleType.fromInt(newValue).getValue(); + } + return actualChanges; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysEnumerations.java new file mode 100755 index 000000000..61e56cb16 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysEnumerations.java @@ -0,0 +1,47 @@ +/** + * 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.organisation.workingdays.domain; + + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +public class WorkingDaysEnumerations { + + public static EnumOptionData workingDaysStatusType(final int id) { + return repaymentRescheduleType(RepaymentRescheduleType.fromInt(id)); + } + + public static EnumOptionData repaymentRescheduleType(final RepaymentRescheduleType type){ + EnumOptionData optionData = null; + switch(type){ + case INVALID: + optionData = new EnumOptionData(RepaymentRescheduleType.INVALID.getValue().longValue(),RepaymentRescheduleType.INVALID.getCode(), + "invalid"); + break; + + case SAME_DAY: + optionData = new EnumOptionData(RepaymentRescheduleType.SAME_DAY.getValue().longValue(),RepaymentRescheduleType.SAME_DAY.getCode(), + "same day"); + + break; + case MOVE_TO_NEXT_WORKING_DAY: + optionData = new EnumOptionData(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue().longValue(),RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getCode(), + "move to next working day"); + break; + + case MOVE_TO_NEXT_REPAYMENT_MEETING_DAY: + optionData = new EnumOptionData(RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY.getValue().longValue(),RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY.getCode(), + "move to next repayment meeting day"); + break; + case MOVE_TO_PREVIOUS_WORKING_DAY: + optionData = new EnumOptionData(RepaymentRescheduleType.MOVE_TO_PREVIOUS_WORKING_DAY.getValue().longValue(),RepaymentRescheduleType.MOVE_TO_PREVIOUS_WORKING_DAY.getCode(), + "move to previous working day"); + break; + } + + return optionData; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/exception/WorkingDaysNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/exception/WorkingDaysNotFoundException.java old mode 100644 new mode 100755 index 26a4ddf50..33991c2c6 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/exception/WorkingDaysNotFoundException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/exception/WorkingDaysNotFoundException.java @@ -15,4 +15,8 @@ public class WorkingDaysNotFoundException extends AbstractPlatformResourceNotFou public WorkingDaysNotFoundException() { super("error.msg.working.days.not.configured", "Must configure the Working days for the organisation."); } + + public WorkingDaysNotFoundException(Long id){ + super("error.msg.workingdays.id.invalid", "Working day with identifier " + id + " does not exist", id); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java new file mode 100755 index 000000000..74aae1241 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java @@ -0,0 +1,31 @@ +/** + * 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.organisation.workingdays.handler; + + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.organisation.workingdays.service.WorkingDaysWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UpdateWorkingDaysCommandHandler implements NewCommandSourceHandler { + + private final WorkingDaysWritePlatformService workingDaysWritePlatformService; + + @Autowired + public UpdateWorkingDaysCommandHandler(final WorkingDaysWritePlatformService workingDaysWritePlatformService) { + this.workingDaysWritePlatformService = workingDaysWritePlatformService; + } + @Transactional + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.workingDaysWritePlatformService.updateWorkingDays(command); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformService.java new file mode 100755 index 000000000..621ce4daa --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformService.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.organisation.workingdays.service; + + +import org.mifosplatform.organisation.workingdays.data.WorkingDaysData; + +import java.util.Collection; + +public interface WorkingDaysReadPlatformService { + + Collection retrieveAll(); + WorkingDaysData retrieveOne(Long id); + + WorkingDaysData repaymentRescheduleType(); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java new file mode 100755 index 000000000..3a6077cf4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.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.organisation.workingdays.service; + + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.workingdays.data.WorkingDaysData; +import org.mifosplatform.organisation.workingdays.domain.RepaymentRescheduleType; +import org.mifosplatform.organisation.workingdays.domain.WorkingDaysEnumerations; +import org.mifosplatform.organisation.workingdays.exception.WorkingDaysNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collection; + +@Service +public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatformService{ + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + + @Autowired + public WorkingDaysReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + + private static final class WorkingDaysMapper implements RowMapper { + + private final String schema; + + public WorkingDaysMapper() { + final StringBuilder sqlBuilder = new StringBuilder(100); + sqlBuilder.append("w.id as id,w.recurrence as recurrence,w.repayment_rescheduling_enum as status_enum "); + sqlBuilder.append("from m_working_days w"); + + this.schema = sqlBuilder.toString(); + } + public String schema() { + return this.schema; + } + + @Override + public WorkingDaysData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String recurrence = rs.getString("recurrence"); + final Integer statusEnum = JdbcSupport.getInteger(rs, "status_enum"); + final EnumOptionData status = WorkingDaysEnumerations.workingDaysStatusType(statusEnum); + + return new WorkingDaysData(id,recurrence,status); + } + } + + @Override + public Collection retrieveAll() { + final WorkingDaysMapper rm = new WorkingDaysMapper(); + final String sql = " select " + rm.schema() ; + return this.jdbcTemplate.query(sql, rm, new Object[] {}); + } + + @Override + public WorkingDaysData retrieveOne(final Long id) { + try { + final WorkingDaysMapper rm = new WorkingDaysMapper(); + + final String sql = " select " + rm.schema() + " where w.id = ?"; + + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { id }); + } catch (final EmptyResultDataAccessException e) { + throw new WorkingDaysNotFoundException(id); + } + } + + @Override + public WorkingDaysData repaymentRescheduleType() { + Collection repaymentRescheduleOptions = Arrays.asList(WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.SAME_DAY), + WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY), + WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY), + WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_PREVIOUS_WORKING_DAY) + ); + return new WorkingDaysData(null,null,null,repaymentRescheduleOptions); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java old mode 100644 new mode 100755 index 5b429420b..b5a8e40ae --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java @@ -6,10 +6,14 @@ package org.mifosplatform.organisation.workingdays.service; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; public interface WorkingDaysWritePlatformService { boolean isWorkingDay(LocalDate transactionDate); boolean isTransactionAllowedOnNonWorkingDay(); + + CommandProcessingResult updateWorkingDays(JsonCommand command); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java old mode 100644 new mode 100755 index 6c32d24a0..247e950e1 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java @@ -5,24 +5,39 @@ */ package org.mifosplatform.organisation.workingdays.service; +import net.fortuna.ical4j.model.ValidationException; +import net.fortuna.ical4j.model.property.RRule; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.organisation.workingdays.api.WorkingDaysApiConstants; +import org.mifosplatform.organisation.workingdays.data.WorkingDayValidator; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.text.ParseException; +import java.util.Map; @Service public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements WorkingDaysWritePlatformService { private final WorkingDaysRepositoryWrapper daysRepositoryWrapper; private final ConfigurationDomainService configurationDomainService; + private final WorkingDayValidator fromApiJsonDeserializer; @Autowired public WorkingDaysWritePlatformServiceJpaRepositoryImpl(final WorkingDaysRepositoryWrapper daysRepositoryWrapper, - final ConfigurationDomainService configurationDomainService) { + final ConfigurationDomainService configurationDomainService, + final WorkingDayValidator fromApiJsonDeserializer) { this.daysRepositoryWrapper = daysRepositoryWrapper; this.configurationDomainService = configurationDomainService; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; } @Override @@ -36,4 +51,30 @@ public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements Working return this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); } + @Transactional + @Override + public CommandProcessingResult updateWorkingDays(JsonCommand command) { + String recurrence = ""; + RRule rrule = null; + try{ + this.fromApiJsonDeserializer.validateForUpdate(command.json()); + final WorkingDays workingDays = this.daysRepositoryWrapper.findOne(); + + recurrence = command.stringValueOfParameterNamed(WorkingDaysApiConstants.recurrence); + rrule = new RRule(recurrence); + rrule.validate(); + + Map changes = workingDays.update(command); + this.daysRepositoryWrapper.saveAndFlush(workingDays); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(workingDays.getId()).build(); + }catch (final ValidationException e) { + throw new PlatformDataIntegrityException("error.msg.invalid.recurring.rule", "The Recurring Rule value: " + recurrence + + " is not valid.", "recurrence", recurrence); + } catch (final IllegalArgumentException|ParseException e) { + throw new PlatformDataIntegrityException("error.msg.recurring.rule.parsing.error", + "Error in passing the Recurring Rule value: " + recurrence, "recurrence", e.getMessage()); + } + } + + } diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V242__workingdays_permissions.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V242__workingdays_permissions.sql new file mode 100755 index 000000000..aa738ad5c --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V242__workingdays_permissions.sql @@ -0,0 +1,36 @@ +INSERT INTO `m_permission` ( +`grouping` , +`code` , +`entity_name` , +`action_name` , +`can_maker_checker` +) +VALUES ( + 'organisation', 'READ_WORKINGDAYS', 'WORKINGDAYS', 'READ', '0' +); + + +INSERT INTO `m_permission` ( +`grouping` , +`code` , +`entity_name` , +`action_name` , +`can_maker_checker` +) +VALUES ( + 'organisation', 'UPDATE_WORKINGDAYS', 'WORKINGDAYS', 'UPDATE', '0' +); + +INSERT INTO `m_permission` ( +`grouping` , +`code` , +`entity_name` , +`action_name` , +`can_maker_checker` +) +VALUES ( + 'organisation', 'UPDATE_WORKINGDAYS_CHECKER', 'WORKINGDAYS', 'UPDATE_CHECKER', '0' +); + + +