diff --git a/.gitignore b/.gitignore index c63dfd09fa9..2ac78be74f5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,8 +26,6 @@ licenses **/.asciidoctor/diagram/* **/images/diag-*.svg -fineract-provider/src/main/generated/ - **/out/ gradleExp/ diff --git a/config/docker/env/mariadb.env b/config/docker/env/mariadb.env new file mode 100644 index 00000000000..26aae0972be --- /dev/null +++ b/config/docker/env/mariadb.env @@ -0,0 +1,20 @@ +# +# 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. +# + +MARIADB_ROOT_PASSWORD=mysql \ No newline at end of file diff --git a/docker-compose-mariadb.yml b/docker-compose-mariadb.yml new file mode 100644 index 00000000000..59f18160b9f --- /dev/null +++ b/docker-compose-mariadb.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: + mariadb: + container_name: mariadb + image: mariadb:11.4 + volumes: + - ./config/docker/mysql/conf.d/server_collation.cnf:/etc/mysql/conf.d/server_collation.cnf:ro + - ./config/docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:Z,ro + restart: always + env_file: + - ./config/docker/env/mariadb.env + healthcheck: + test: [ "CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized" ] + timeout: 10s + retries: 10 + ports: + - "3306:3306" diff --git a/docker-compose-mysql.yml b/docker-compose-mysql.yml new file mode 100644 index 00000000000..5f0521724ad --- /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 8fbe7125b58..0c5c7af8e48 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 @@ 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,57 @@ 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; - } + // 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; + // } + // + // public Map update(WorkingDaysUpdateRequest request) { + // + // } } 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 e3f9606285a..c2caf644841 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 static EnumOptionData repaymentRescheduleType(final RepaymentRescheduleTy 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 static EnumOptionData repaymentRescheduleType(final RepaymentRescheduleTy 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 c8234d97e09..49eaa12a226 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 @@ -154,6 +154,13 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_CURRENCY") .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/currencies")) .hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_WRITE", "UPDATE_CURRENCY") + // working days + .requestMatchers(antMatcher(HttpMethod.GET, "/api/*/workingdays")) + .hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_WORKING_DAYS") + .requestMatchers(antMatcher(HttpMethod.GET, "api/*/workingdays/template")) + .hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_WORKING_DAYS") + .requestMatchers(antMatcher(HttpMethod.PUT, "/api/*/workingdays")) + .hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_WRITE", "UPDATE_WORKING_DAYS") // ... .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() // .requestMatchers(antMatcher("/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 3cb7555c92f..c05d3c54eb1 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,27 +19,24 @@ 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.responses.ApiResponses; 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.util.UUID; +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.infrastructure.core.service.DateUtils; +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; @@ -52,17 +49,15 @@ @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(); } @@ -71,16 +66,17 @@ public WorkingDaysData retrieveAll() { @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))) - @ApiResponses({ - @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.setId(UUID.randomUUID()); + command.setCreatedAt(DateUtils.getAuditOffsetDateTime()); + command.setPayload(request); - return this.toApiJsonSerializer.serialize(result); + final Supplier response = commandPipeline.send(command); + + return response.get(); } @GET @@ -89,11 +85,7 @@ public String update(@Parameter(hidden = true) final String jsonRequestBody) { @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") - @ApiResponses({ - @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 00000000000..d9fb6693165 --- /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 8c3e365c18a..97416f3806a 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.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 ef2e62ab7b9..24d778779e8 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 @@ 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 00000000000..8eebb989b71 --- /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 00000000000..233d9e1c6fd --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysUpdateRequestValidator.java @@ -0,0 +1,61 @@ +/** + * 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(); + + } + + 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 00000000000..725c3e9e77a --- /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 0f1fc6bad15..2528d818f8e 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 59d6deeaa7d..1b4ecbafc29 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 WorkingDaysData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowN 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 WorkingDaysData retrieve() { 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 WorkingDaysData retrieve() { @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 dcdb3d1e40c..411991cd65f 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 b0611c28bfb..c7e866bd6f7 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 @@ 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 CommandProcessingResult updateWorkingDays(JsonCommand command) { } } + 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 256909f5137..14f0e5c096d 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 WorkingDaysReadPlatformService workingDaysReadPlatformService(JdbcTemplat @Bean @ConditionalOnMissingBean(WorkingDaysWritePlatformService.class) public WorkingDaysWritePlatformService workingDaysWritePlatformService(WorkingDaysRepositoryWrapper daysRepositoryWrapper, - WorkingDayValidator fromApiJsonDeserializer) { - return new WorkingDaysWritePlatformServiceJpaRepositoryImpl(daysRepositoryWrapper, fromApiJsonDeserializer); + WorkingDaysUpdateRequestValidator validator) { + return new WorkingDaysWritePlatformServiceJpaRepositoryImpl(daysRepositoryWrapper, validator); } }