diff --git a/docker-compose-mysql.yml b/docker-compose-mysql.yml new file mode 100644 index 000000000..5f0521724 --- /dev/null +++ b/docker-compose-mysql.yml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +services: + mysql: + container_name: mysql + image: mysql:8 + volumes: + - ${PWD}/config/docker/mysql/conf.d/server_collation.cnf:/etc/mysql/conf.d/server_collation.cnf:ro + - ${PWD}/config/docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:Z,ro + restart: always + env_file: + - ${PWD}/config/docker/env/mysql.env + healthcheck: + test: [ "CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized" ] + timeout: 10s + retries: 10 + ports: + - "3306:3306" diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java index 8fbe7125b..b55b591c7 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java @@ -21,16 +21,21 @@ package org.apache.fineract.organisation.workingdays.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; -import java.util.LinkedHashMap; -import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; -import org.apache.fineract.infrastructure.core.api.JsonCommand; +import lombok.experimental.FieldNameConstants; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; -import org.apache.fineract.organisation.workingdays.api.WorkingDaysApiConstants; +@Builder @Getter +@Setter @Entity +@NoArgsConstructor +@AllArgsConstructor +@FieldNameConstants @Table(name = "m_working_days") public class WorkingDays extends AbstractPersistableCustom { @@ -47,51 +52,4 @@ public class WorkingDays extends AbstractPersistableCustom { @Column(name = "extend_term_holiday_repayment", nullable = false) private Boolean extendTermForRepaymentsOnHolidays; - protected WorkingDays() { - - } - - public WorkingDays(final String recurrence, final Integer repaymentReschedulingType, final Boolean extendTermForDailyRepayments, - final Boolean extendTermForRepaymentsOnHolidays) { - this.recurrence = recurrence; - this.repaymentReschedulingType = repaymentReschedulingType; - this.extendTermForDailyRepayments = extendTermForDailyRepayments; - this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays; - } - - 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(); - } - - if (command.isChangeInBooleanParameterNamed(WorkingDaysApiConstants.extendTermForDailyRepayments, - this.extendTermForDailyRepayments)) { - final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed(WorkingDaysApiConstants.extendTermForDailyRepayments); - actualChanges.put(WorkingDaysApiConstants.extendTermForDailyRepayments, newValue); - this.extendTermForDailyRepayments = newValue; - } - - if (command.isChangeInBooleanParameterNamed(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays, - this.extendTermForRepaymentsOnHolidays)) { - final Boolean newValue = command - .booleanPrimitiveValueOfParameterNamed(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays); - actualChanges.put(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays, newValue); - this.extendTermForRepaymentsOnHolidays = newValue; - } - - return actualChanges; - } - } diff --git a/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysEnumerations.java b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysEnumerations.java index e3f960628..c2caf6448 100644 --- a/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysEnumerations.java +++ b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysEnumerations.java @@ -41,8 +41,8 @@ public final class WorkingDaysEnumerations { 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"); @@ -52,10 +52,12 @@ public final class WorkingDaysEnumerations { 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; + case MOVE_TO_NEXT_MEETING_DAY: optionData = new EnumOptionData(RepaymentRescheduleType.MOVE_TO_NEXT_MEETING_DAY.getValue().longValue(), RepaymentRescheduleType.MOVE_TO_NEXT_MEETING_DAY.getCode(), "move to next meeting day"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java index 6168cfc63..2aa14cb57 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java @@ -328,6 +328,13 @@ public class SecurityConfig { .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_MIX_REPORT") .requestMatchers(API_MATCHER.matcher(HttpMethod.DELETE, "/api/*/mixreport/*")) .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_MIX_REPORT") + // working days + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/workingdays")) + .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_WORKING_DAYS") + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/workingdays/template")) + .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_WORKING_DAYS") + .requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/workingdays")) + .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_WORKING_DAYS") .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() .requestMatchers(API_MATCHER.matcher("/api/*/twofactor")).fullyAuthenticated() diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiResource.java index 1ec2298d2..d58dd0bfa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiResource.java @@ -19,26 +19,23 @@ package org.apache.fineract.organisation.workingdays.api; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.parameters.RequestBody; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; +import java.time.Instant; +import java.util.function.Supplier; import lombok.RequiredArgsConstructor; -import org.apache.fineract.commands.domain.CommandWrapper; -import org.apache.fineract.commands.service.CommandWrapperBuilder; -import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; -import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; -import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.command.core.CommandPipeline; +import org.apache.fineract.organisation.workingdays.command.WorkingDaysUpdateCommand; import org.apache.fineract.organisation.workingdays.data.WorkingDaysData; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequest; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequestValidator; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateResponse; import org.apache.fineract.organisation.workingdays.service.WorkingDaysReadPlatformService; import org.springframework.stereotype.Component; @@ -51,17 +48,15 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class WorkingDaysApiResource { - private final DefaultToApiJsonSerializer toApiJsonSerializer; private final WorkingDaysReadPlatformService workingDaysReadPlatformService; - private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - private final PlatformSecurityContext context; + private final WorkingDaysUpdateRequestValidator workingDaysUpdateRequestValidator; + private final CommandPipeline commandPipeline; @GET @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "List Working days", description = "Example Requests:\n" + "\n" + "workingdays") public WorkingDaysData retrieveAll() { - this.context.authenticatedUser().validateHasReadPermission(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); return this.workingDaysReadPlatformService.retrieve(); } @@ -70,15 +65,17 @@ public class WorkingDaysApiResource { @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Update a Working Day", description = "Mandatory Fields\n" + "recurrence,repaymentRescheduleType,extendTermForDailyRepayments,locale") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingDaysApiResourceSwagger.PutWorkingDaysRequest.class))) - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = WorkingDaysApiResourceSwagger.PutWorkingDaysResponse.class))) - public String update(@Parameter(hidden = true) final String jsonRequestBody) { + public WorkingDaysUpdateResponse update(@Valid WorkingDaysUpdateRequest request) { - final CommandWrapper commandRequest = new CommandWrapperBuilder().updateWorkingDays().withJson(jsonRequestBody).build(); + final var command = new WorkingDaysUpdateCommand(); - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + command.setCommandId(System.currentTimeMillis()); + command.setCreatedAt(Instant.now()); + command.setPayload(request); - return this.toApiJsonSerializer.serialize(result); + final Supplier response = commandPipeline.send(command); + + return response.get(); } @GET @@ -87,10 +84,7 @@ public class WorkingDaysApiResource { @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Working Days Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for working days.\n" + "\n" + "Example Request:\n" + "\n" + "workingdays/template") - @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = WorkingDaysApiResourceSwagger.GetWorkingDaysTemplateResponse.class))) public WorkingDaysData template() { - this.context.authenticatedUser().validateHasReadPermission(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); - return this.workingDaysReadPlatformService.repaymentRescheduleType(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/command/WorkingDaysUpdateCommand.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/command/WorkingDaysUpdateCommand.java new file mode 100644 index 000000000..d9fb66931 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/command/WorkingDaysUpdateCommand.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.organisation.workingdays.command; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.fineract.command.core.Command; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequest; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WorkingDaysUpdateCommand extends Command {} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java index 8c3e365c1..97416f380 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java @@ -37,6 +37,7 @@ import org.apache.fineract.organisation.workingdays.api.WorkingDaysApiConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +//Not used @Component public class WorkingDayValidator { diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java index ef2e62ab7..24d778779 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java @@ -21,12 +21,18 @@ package org.apache.fineract.organisation.workingdays.data; import java.io.Serial; import java.io.Serializable; import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.FieldNameConstants; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +@Builder @Data @NoArgsConstructor +@AllArgsConstructor +@FieldNameConstants public class WorkingDaysData implements Serializable { @Serial @@ -42,37 +48,7 @@ public class WorkingDaysData implements Serializable { private Boolean extendTermForRepaymentsOnHolidays; - // template date @SuppressWarnings("unused") private Collection repaymentRescheduleOptions; - public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, Boolean extendTermForDailyRepayments, - Boolean extendTermForRepaymentsOnHolidays) { - this.id = id; - this.recurrence = recurrence; - this.repaymentRescheduleType = repaymentRescheduleType; - this.repaymentRescheduleOptions = null; - this.extendTermForDailyRepayments = extendTermForDailyRepayments; - this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays; - } - - public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, - Collection repaymentRescheduleOptions, Boolean extendTermForDailyRepayments, - Boolean extendTermForRepaymentsOnHolidays) { - this.id = id; - this.recurrence = recurrence; - this.repaymentRescheduleType = repaymentRescheduleType; - this.repaymentRescheduleOptions = repaymentRescheduleOptions; - this.extendTermForDailyRepayments = extendTermForDailyRepayments; - this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays; - } - - public WorkingDaysData(WorkingDaysData data, Collection repaymentRescheduleOptions) { - this.id = data.id; - this.recurrence = data.recurrence; - this.repaymentRescheduleType = data.repaymentRescheduleType; - this.repaymentRescheduleOptions = repaymentRescheduleOptions; - this.extendTermForDailyRepayments = data.extendTermForDailyRepayments; - this.extendTermForRepaymentsOnHolidays = data.extendTermForRepaymentsOnHolidays; - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequest.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequest.java new file mode 100644 index 000000000..8eebb989b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequest.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.organisation.workingdays.data; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.io.Serial; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class WorkingDaysUpdateRequest implements Serializable { + + @Serial + public static final long serialVersionUID = 1L; + + private String recurrence; + + private Integer repaymentRescheduleType; + + private Boolean extendTermForDailyRepayments; + + private Boolean extendTermForRepaymentsOnHolidays; + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequestValidator.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequestValidator.java new file mode 100644 index 000000000..b0a831783 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequestValidator.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.organisation.workingdays.data; + +import java.util.ArrayList; +import java.util.List; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.organisation.workingdays.api.WorkingDaysApiConstants; +import org.springframework.stereotype.Component; + +@Component +public class WorkingDaysUpdateRequestValidator { + + public void validateForUpdate(final WorkingDaysUpdateRequest request) { + + List validationErrors = new ArrayList<>(); + DataValidatorBuilder validator = new DataValidatorBuilder(validationErrors) + .resource(WorkingDaysApiConstants.WORKING_DAYS_RESOURCE_NAME); + + // recurrence (mandatory) + String recurrence = request.getRecurrence(); + validator.reset().parameter(WorkingDaysApiConstants.recurrence).value(recurrence).notNull(); + + // repaymentRescheduleType (optional, but must be 1–4 if present) + validator.reset().parameter(WorkingDaysApiConstants.repayment_rescheduling_enum).value(request.getRepaymentRescheduleType()) + .ignoreIfNull().inMinMaxRange(1, 4); + + // extendTermForDailyRepayments (optional but must be boolean if provided) + validator.reset().parameter(WorkingDaysApiConstants.extendTermForDailyRepayments).value(request.getExtendTermForDailyRepayments()) + .ignoreIfNull().validateForBooleanValue(); + + // extendTermForRepaymentsOnHolidays (optional but must be boolean if provided) + validator.reset().parameter(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays) + .value(request.getExtendTermForRepaymentsOnHolidays()).ignoreIfNull().validateForBooleanValue(); + + throwExceptionIfValidationWarningsExist(validationErrors); + } + + private void throwExceptionIfValidationWarningsExist(final List validationErrors) { + if (!validationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(validationErrors); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateResponse.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateResponse.java new file mode 100644 index 000000000..725c3e9e7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateResponse.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.organisation.workingdays.data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WorkingDaysUpdateResponse implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + private Long resourceId; + private String recurrence; + private Integer repaymentRescheduleType; + private Boolean extendTermForDailyRepayments; + private Boolean extendTermForRepaymentsOnHolidays; + private Map changes; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java index 0f1fc6bad..2528d818f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/handler/UpdateWorkingDaysCommandHandler.java @@ -18,29 +18,38 @@ */ package org.apache.fineract.organisation.workingdays.handler; -import org.apache.fineract.commands.annotation.CommandType; -import org.apache.fineract.commands.handler.NewCommandSourceHandler; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import java.util.Collections; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.command.core.Command; +import org.apache.fineract.command.core.CommandHandler; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequest; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateResponse; import org.apache.fineract.organisation.workingdays.service.WorkingDaysWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -@Service -@CommandType(entity = "WORKINGDAYS", action = "UPDATE") -public class UpdateWorkingDaysCommandHandler implements NewCommandSourceHandler { +@Slf4j +@Component +@RequiredArgsConstructor +public class UpdateWorkingDaysCommandHandler implements CommandHandler { 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); + public WorkingDaysUpdateResponse handle(Command command) { + WorkingDaysUpdateRequest request = command.getPayload(); + Map changes = this.workingDaysWritePlatformService.updateWorkingDays(request); + if (changes == null) { + changes = Collections.emptyMap(); + } + + return WorkingDaysUpdateResponse.builder().resourceId((Long) changes.get("resourceId")).changes(changes) + .recurrence(request.getRecurrence()).repaymentRescheduleType(request.getRepaymentRescheduleType()) + .extendTermForDailyRepayments(request.getExtendTermForDailyRepayments()) + .extendTermForRepaymentsOnHolidays(request.getExtendTermForRepaymentsOnHolidays()).build(); } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java index 59d6deeaa..1b4ecbafc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java @@ -65,7 +65,9 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo final Boolean extendTermForDailyRepayments = rs.getBoolean("extendTermForDailyRepayments"); final Boolean extendTermForRepaymentsOnHolidays = rs.getBoolean("extendTermForRepaymentsOnHolidays"); - return new WorkingDaysData(id, recurrence, status, extendTermForDailyRepayments, extendTermForRepaymentsOnHolidays); + return WorkingDaysData.builder().id(id).recurrence(recurrence).repaymentRescheduleType(status) + .extendTermForDailyRepayments(extendTermForDailyRepayments) + .extendTermForRepaymentsOnHolidays(extendTermForRepaymentsOnHolidays).build(); } } @@ -77,7 +79,11 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo final String sql = " select " + rm.schema(); WorkingDaysData data = this.jdbcTemplate.queryForObject(sql, rm); // NOSONAR Collection repaymentRescheduleOptions = repaymentRescheduleTypeOptions(); - return new WorkingDaysData(data, repaymentRescheduleOptions); + return WorkingDaysData.builder().id(data.getId()).recurrence(data.getRecurrence()) + .repaymentRescheduleType(data.getRepaymentRescheduleType()) + .extendTermForDailyRepayments(data.getExtendTermForDailyRepayments()) + .extendTermForRepaymentsOnHolidays(data.getExtendTermForRepaymentsOnHolidays()) + .repaymentRescheduleOptions(repaymentRescheduleOptions).build(); } catch (final EmptyResultDataAccessException e) { throw new WorkingDaysNotFoundException(e); } @@ -86,7 +92,7 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo @Override public WorkingDaysData repaymentRescheduleType() { Collection repaymentRescheduleOptions = repaymentRescheduleTypeOptions(); - return new WorkingDaysData(null, null, null, repaymentRescheduleOptions, null, null); + return WorkingDaysData.builder().repaymentRescheduleOptions(repaymentRescheduleOptions).build(); } private Collection repaymentRescheduleTypeOptions() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformService.java index dcdb3d1e4..411991cd6 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformService.java @@ -18,10 +18,12 @@ */ package org.apache.fineract.organisation.workingdays.service; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import java.util.Map; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequest; +import org.springframework.transaction.annotation.Transactional; public interface WorkingDaysWritePlatformService { - CommandProcessingResult updateWorkingDays(JsonCommand command); + @Transactional + Map updateWorkingDays(WorkingDaysUpdateRequest request); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java index b0611c28b..c7e866bd6 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java @@ -19,16 +19,15 @@ package org.apache.fineract.organisation.workingdays.service; import java.text.ParseException; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import lombok.RequiredArgsConstructor; import net.fortuna.ical4j.model.property.RRule; import net.fortuna.ical4j.validate.ValidationException; -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; -import org.apache.fineract.organisation.workingdays.api.WorkingDaysApiConstants; -import org.apache.fineract.organisation.workingdays.data.WorkingDayValidator; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequest; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequestValidator; import org.apache.fineract.organisation.workingdays.domain.WorkingDays; import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.springframework.transaction.annotation.Transactional; @@ -37,25 +36,27 @@ import org.springframework.transaction.annotation.Transactional; public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements WorkingDaysWritePlatformService { private final WorkingDaysRepositoryWrapper daysRepositoryWrapper; - private final WorkingDayValidator fromApiJsonDeserializer; + private final WorkingDaysUpdateRequestValidator validator; @Transactional @Override - public CommandProcessingResult updateWorkingDays(JsonCommand command) { + public Map updateWorkingDays(WorkingDaysUpdateRequest request) { String recurrence = ""; RRule rrule = null; try { - this.fromApiJsonDeserializer.validateForUpdate(command.json()); + this.validator.validateForUpdate(request); final WorkingDays workingDays = this.daysRepositoryWrapper.findOne(); - recurrence = command.stringValueOfParameterNamed(WorkingDaysApiConstants.recurrence); + recurrence = request.getRecurrence(); rrule = new RRule(recurrence); rrule.validate(); - Map changes = workingDays.update(command); + Map changes = update(workingDays, request); + // include the current WorkingDays resource id in the changes for response consumption + changes.put("resourceId", workingDays.getId()); this.daysRepositoryWrapper.saveAndFlush(workingDays); - return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(workingDays.getId()).with(changes) - .build(); + return changes; + } catch (final ValidationException e) { throw new PlatformDataIntegrityException("error.msg.invalid.recurring.rule", "The Recurring Rule value: " + recurrence + " is not valid.", "recurrence", recurrence, e); @@ -65,4 +66,32 @@ public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements Working } } + public HashMap update(WorkingDays workingDays, WorkingDaysUpdateRequest request) { + HashMap changes = new HashMap<>(); + + if (!Objects.equals(request.getRecurrence(), workingDays.getRecurrence())) { + workingDays.setRecurrence(request.getRecurrence()); + changes.put("recurrence", request.getRecurrence()); + } + + Integer repaymentRescheduleType = request.getRepaymentRescheduleType(); + if (repaymentRescheduleType != null && !Objects.equals(repaymentRescheduleType, workingDays.getRepaymentReschedulingType())) { + workingDays.setRepaymentReschedulingType(repaymentRescheduleType); + changes.put("repaymentRescheduleType", repaymentRescheduleType); + } + + Boolean extendDaily = request.getExtendTermForDailyRepayments(); + if (extendDaily != null && !Objects.equals(extendDaily, workingDays.getExtendTermForDailyRepayments())) { + workingDays.setExtendTermForDailyRepayments(extendDaily); + changes.put("extendTermForDailyRepayments", extendDaily); + } + + Boolean extendHolidays = request.getExtendTermForRepaymentsOnHolidays(); + if (extendHolidays != null && !Objects.equals(extendHolidays, workingDays.getExtendTermForRepaymentsOnHolidays())) { + workingDays.setExtendTermForRepaymentsOnHolidays(extendHolidays); + changes.put("extendTermForRepaymentsOnHolidays", extendHolidays); + } + return changes; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/starter/OrganisationWorkingDaysConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/starter/OrganisationWorkingDaysConfiguration.java index 256909f51..14f0e5c09 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/starter/OrganisationWorkingDaysConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/starter/OrganisationWorkingDaysConfiguration.java @@ -18,7 +18,7 @@ */ package org.apache.fineract.organisation.workingdays.starter; -import org.apache.fineract.organisation.workingdays.data.WorkingDayValidator; +import org.apache.fineract.organisation.workingdays.data.WorkingDaysUpdateRequestValidator; import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.apache.fineract.organisation.workingdays.service.WorkingDaysReadPlatformService; import org.apache.fineract.organisation.workingdays.service.WorkingDaysReadPlatformServiceImpl; @@ -41,7 +41,7 @@ public class OrganisationWorkingDaysConfiguration { @Bean @ConditionalOnMissingBean(WorkingDaysWritePlatformService.class) public WorkingDaysWritePlatformService workingDaysWritePlatformService(WorkingDaysRepositoryWrapper daysRepositoryWrapper, - WorkingDayValidator fromApiJsonDeserializer) { - return new WorkingDaysWritePlatformServiceJpaRepositoryImpl(daysRepositoryWrapper, fromApiJsonDeserializer); + WorkingDaysUpdateRequestValidator validator) { + return new WorkingDaysWritePlatformServiceJpaRepositoryImpl(daysRepositoryWrapper, validator); } }