From 4465e632f14f9b2fcefd6e6ad168a7447892876b Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Tue, 14 Jun 2022 19:05:52 +0200 Subject: [PATCH] Business date part 1 (#2362) Business date implementation - part 1 --- .../org.apache.fineract.dependencies.gradle | 3 + fineract-provider/dependencies.gradle | 3 +- .../service/CommandWrapperBuilder.java | 7 + .../api/BusinessDateApiResource.java | 107 +++++++ .../api/BusinessDateApiResourceSwagger.java | 63 +++++ .../businessdate/data/BusinessDateData.java | 45 +++ .../businessdate/domain/BusinessDate.java | 64 +++++ .../domain/BusinessDateRepository.java | 28 ++ .../businessdate/domain/BusinessDateType.java | 37 +++ .../BusinessDateActionException.java | 31 ++ .../BusinessDateNotFoundException.java | 41 +++ .../handler/BusinessDateUpdateHandler.java | 43 +++ .../mapper/BusinessDateMapper.java | 35 +++ .../BusinessDateReadPlatformService.java | 29 ++ .../BusinessDateReadPlatformServiceImpl.java | 63 +++++ .../BusinessDateWritePlatformService.java | 32 +++ .../BusinessDateWritePlatformServiceImpl.java | 149 ++++++++++ .../BusinessDateDataParserAndValidator.java | 107 +++++++ .../domain/ConfigurationDomainService.java | 4 + .../domain/ConfigurationDomainServiceJpa.java | 11 + .../infrastructure/core/api/JsonCommand.java | 5 + .../core/data/ApiParameterError.java | 2 +- .../core/data/CommandProcessingResult.java | 2 + .../core/data/DataValidatorBuilder.java | 186 ++++++------ .../PlatformApiDataValidationException.java | 5 + .../DefaultToApiJsonSerializer.java | 2 +- .../core/serialization/JsonParserHelper.java | 35 +-- .../infrastructure/jobs/service/JobName.java | 6 +- .../search/data/AdHocQueryDataValidator.java | 4 +- .../ScheduledJobRunnerServiceImpl.java | 8 +- .../useradministration/domain/AppUser.java | 15 +- .../db/changelog/tenant/changelog-tenant.xml | 1 + .../tenant/parts/0015_add_business_date.xml | 121 ++++++++ .../businessdate/api/BusinessDateApiTest.java | 163 +++++++++++ .../data/BusinessDataSerialized.java | 63 +++++ .../data/BusinessDataTypeTest.java | 37 +++ .../mapper/BusinessDateMapperTest.java | 56 ++++ .../BusinessDateReadPlatformServiceTest.java | 96 +++++++ .../BusinessDateWritePlatformServiceTest.java | 264 ++++++++++++++++++ .../validator/BusinessDateValidatorTest.java | 120 ++++++++ .../integrationtests/SchedulerJobsTest.java | 5 + .../common/GlobalConfigurationHelper.java | 25 +- 42 files changed, 1998 insertions(+), 125 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResourceSwagger.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDateData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDate.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateType.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateActionException.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateNotFoundException.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/handler/BusinessDateUpdateHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapper.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateDataParserAndValidator.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0015_add_business_date.xml create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataSerialized.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataTypeTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapperTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateValidatorTest.java diff --git a/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle b/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle index a5f1b92d9..02cc5347f 100644 --- a/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle +++ b/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle @@ -180,5 +180,8 @@ dependencyManagement { entry 'json-path' entry 'xml-path' } + + dependency 'org.mapstruct:mapstruct:1.5.1.Final' + dependency 'org.mapstruct:mapstruct-processor:1.5.1.Final' } } diff --git a/fineract-provider/dependencies.gradle b/fineract-provider/dependencies.gradle index 0d0d99e74..ffaa5baa3 100644 --- a/fineract-provider/dependencies.gradle +++ b/fineract-provider/dependencies.gradle @@ -73,6 +73,7 @@ dependencies { 'org.springdoc:springdoc-openapi-ui', 'org.springdoc:springdoc-openapi-common', 'org.springdoc:springdoc-openapi-security', + 'org.mapstruct:mapstruct', ) implementation ('org.apache.commons:commons-email') { @@ -128,7 +129,6 @@ dependencies { exclude group: 'xml-apis' } - runtimeOnly('org.glassfish.jaxb:jaxb-runtime') { exclude group: 'com.sun.activation' } @@ -144,6 +144,7 @@ dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' // testCompile dependencies are ONLY used in src/test, not src/main. // Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly! diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 5a2b9239a..8d750cdbb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -3478,4 +3478,11 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder updateBusinessDate() { + this.actionName = "UPDATE"; + this.entityName = "BUSINESS_DATE"; + this.href = "/businessdate"; + return this; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java new file mode 100644 index 000000000..63f756fc4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResource.java @@ -0,0 +1,107 @@ +/** + * 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.infrastructure.businessdate.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +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 java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +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.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService; +import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Path("businessdate") +@Component +@Tag(name = "Business Date Management", description = "Business date management enables you to set up, fetch and adjust organisation business dates") +public class BusinessDateApiResource { + + private final ApiRequestParameterHelper parameterHelper; + private final PlatformSecurityContext securityContext; + private final DefaultToApiJsonSerializer jsonSerializer; + private final BusinessDateReadPlatformService readPlatformService; + private final PortfolioCommandSourceWritePlatformService commandWritePlatformService; + + @GET + @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "List all business dates", description = "") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = BusinessDateApiResourceSwagger.BusinessDateResponse.class)))) }) + public String getBusinessDates(@Context final UriInfo uriInfo) { + securityContext.authenticatedUser().validateHasReadPermission("BUSINESS_DATE"); + final List foundBusinessDates = this.readPlatformService.findAll(); + ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters()); + return this.jsonSerializer.serialize(settings, foundBusinessDates); + } + + @GET + @Path("{type}") + @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Retrieve a specific Business date", description = "") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BusinessDateApiResourceSwagger.BusinessDateResponse.class))) }) + public String getBusinessDate(@PathParam("type") @Parameter(description = "type") final String type, @Context final UriInfo uriInfo) { + securityContext.authenticatedUser().validateHasReadPermission("BUSINESS_DATE"); + final BusinessDateData businessDate = this.readPlatformService.findByType(type); + ApiRequestJsonSerializationSettings settings = parameterHelper.process(uriInfo.getQueryParameters()); + return this.jsonSerializer.serialize(settings, businessDate); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Update Business Date", description = "") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = BusinessDateApiResourceSwagger.BusinessDateRequest.class))) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "", content = @Content(schema = @Schema(implementation = BusinessDateApiResourceSwagger.BusinessDateRequest.class))) }) + public String updateBusinessDate(final String jsonRequestBody, @Context UriInfo uriInfo) { + securityContext.authenticatedUser().validateHasUpdatePermission("BUSINESS_DATE"); + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateBusinessDate().withJson(jsonRequestBody).build(); + + CommandProcessingResult result = commandWritePlatformService.logCommandSource(commandRequest); + return jsonSerializer.serialize(result); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResourceSwagger.java new file mode 100644 index 000000000..273461c40 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiResourceSwagger.java @@ -0,0 +1,63 @@ +/** + * 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.infrastructure.businessdate.api; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; + +final class BusinessDateApiResourceSwagger { + + private BusinessDateApiResourceSwagger() { + + } + + @Schema(description = "BusinessDateResponse") + public static final class BusinessDateResponse { + + @Schema(example = "yyyy-MM-dd") + public String dateFormat; + @Schema(example = "COB date") + private String description; + @Schema(example = "COB_DATE") + private String type; + @Schema(example = "2015-02-15") + private LocalDate date; + + private BusinessDateResponse() { + + } + } + + @Schema(description = "BusinessDateRequest") + public static final class BusinessDateRequest { + + @Schema(example = "yyyy-MM-dd") + public String dateFormat; + @Schema(example = "COB_DATE") + private String type; + @Schema(example = "2015-02-15") + private LocalDate date; + @Schema(example = "en") + private String locale; + + private BusinessDateRequest() {} + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDateData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDateData.java new file mode 100644 index 000000000..3104e188d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDateData.java @@ -0,0 +1,45 @@ +/** + * 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.infrastructure.businessdate.data; + +import java.io.Serializable; +import java.time.LocalDate; +import lombok.Getter; +import lombok.ToString; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; + +@ToString +@Getter +public class BusinessDateData implements Serializable { + + private String description; + private String type; + private LocalDate date; + + public BusinessDateData(BusinessDateType businessDateType, LocalDate date) { + this.type = businessDateType.getName(); + this.description = businessDateType.getDescription(); + this.date = date; + } + + public static BusinessDateData instance(BusinessDateType businessDateType, LocalDate value) { + return new BusinessDateData(businessDateType, value); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDate.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDate.java new file mode 100644 index 000000000..2aef3a287 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDate.java @@ -0,0 +1,64 @@ +/** + * 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.infrastructure.businessdate.domain; + +import java.time.LocalDate; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.persistence.Version; +import javax.validation.constraints.NotNull; +import lombok.Getter; +import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; + +@Getter +@Entity +@Table(name = "m_business_date", uniqueConstraints = { @UniqueConstraint(name = "uq_business_date_type", columnNames = { "type" }) }) +public class BusinessDate extends AbstractAuditableCustom { + + @Enumerated(EnumType.STRING) + @Column(name = "type") + private BusinessDateType type; + + @Column(name = "date", columnDefinition = "DATE") + private LocalDate date; + + @Version + private Long version; + + protected BusinessDate() { + // TODO Auto-generated constructor stub + } + + protected BusinessDate(@NotNull BusinessDateType type, @NotNull LocalDate date) { + this.type = type; + this.date = date; + } + + public static BusinessDate instance(@NotNull BusinessDateType businessDateType, @NotNull LocalDate date) { + return new BusinessDate(businessDateType, date); + } + + public void updateDate(LocalDate date) { + this.date = date; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateRepository.java new file mode 100644 index 000000000..7bf1d1b92 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateRepository.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.infrastructure.businessdate.domain; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface BusinessDateRepository extends JpaRepository, JpaSpecificationExecutor { + + Optional findByType(BusinessDateType type); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateType.java new file mode 100644 index 000000000..eba229783 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/domain/BusinessDateType.java @@ -0,0 +1,37 @@ +/** + * 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.infrastructure.businessdate.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum BusinessDateType { + + BUSINESS_DATE(1, "Business Date"), COB_DATE(2, "Close of Business Date"); + + private final Integer id; + private final String description; + + public String getName() { + return name(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateActionException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateActionException.java new file mode 100644 index 000000000..35d41f212 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateActionException.java @@ -0,0 +1,31 @@ +/** + * 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.infrastructure.businessdate.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when business date is violating any domain rule. + */ +public class BusinessDateActionException extends AbstractPlatformDomainRuleException { + + public BusinessDateActionException(final String violationCode, String message) { + super(violationCode, message, violationCode); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateNotFoundException.java new file mode 100644 index 000000000..fcf986648 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/exception/BusinessDateNotFoundException.java @@ -0,0 +1,41 @@ +/** + * 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.infrastructure.businessdate.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when business date is not found. + */ +public class BusinessDateNotFoundException extends AbstractPlatformResourceNotFoundException { + + public BusinessDateNotFoundException(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs); + } + + public static BusinessDateNotFoundException notExist(final String type, Throwable... e) { + return new BusinessDateNotFoundException("error.msg.businessdate.type.not.exist", + "Business date with type `" + type + "` does not exist.", type, e); + } + + public static BusinessDateNotFoundException notFound(final String type, Throwable... e) { + return new BusinessDateNotFoundException("error.msg.businessdate.not.found", + "Business date with type `" + type + "` does not found.", type, e); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/handler/BusinessDateUpdateHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/handler/BusinessDateUpdateHandler.java new file mode 100644 index 000000000..15d6a5689 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/handler/BusinessDateUpdateHandler.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.infrastructure.businessdate.handler; + +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.businessdate.service.BusinessDateWritePlatformService; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +@CommandType(entity = "BUSINESS_DATE", action = "UPDATE") +public class BusinessDateUpdateHandler implements NewCommandSourceHandler { + + private final BusinessDateWritePlatformService businessDateWritePlatformService; + + @Transactional + @Override + public CommandProcessingResult processCommand(@NotNull final JsonCommand command) { + return businessDateWritePlatformService.updateBusinessDate(command); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapper.java new file mode 100644 index 000000000..ba8abc95e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapper.java @@ -0,0 +1,35 @@ +/** + * 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.infrastructure.businessdate.mapper; + +import java.util.List; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDate; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; + +@Mapper(componentModel = "spring") +public interface BusinessDateMapper { + + @Mappings({ @Mapping(target = "businessDateType", source = "source.type"), @Mapping(target = "date", source = "source.date") }) + BusinessDateData map(BusinessDate source); + + List map(List sources); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformService.java new file mode 100644 index 000000000..c2d836b83 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformService.java @@ -0,0 +1,29 @@ +/** + * 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.infrastructure.businessdate.service; + +import java.util.List; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; + +public interface BusinessDateReadPlatformService { + + List findAll(); + + BusinessDateData findByType(String type); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceImpl.java new file mode 100644 index 000000000..455a529ca --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceImpl.java @@ -0,0 +1,63 @@ +/** + * 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.infrastructure.businessdate.service; + +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDate; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.businessdate.exception.BusinessDateNotFoundException; +import org.apache.fineract.infrastructure.businessdate.mapper.BusinessDateMapper; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BusinessDateReadPlatformServiceImpl implements BusinessDateReadPlatformService { + + private final BusinessDateRepository repository; + private final BusinessDateMapper mapper; + + @Override + public List findAll() { + List businessDateList = repository.findAll(); + return mapper.map(businessDateList); + } + + @Override + public BusinessDateData findByType(String type) { + BusinessDateType businessDateType; + try { + businessDateType = BusinessDateType.valueOf(type); + } catch (IllegalArgumentException e) { + log.error("Provided business date type cannot be found: {}", type); + throw BusinessDateNotFoundException.notExist(type, e); + } + Optional businessDate = repository.findByType(businessDateType); + if (businessDate.isEmpty()) { + log.error("Business date with the provided type cannot be found {}", type); + throw BusinessDateNotFoundException.notFound(type); + } + return mapper.map(businessDate.get()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformService.java new file mode 100644 index 000000000..17c31a03f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformService.java @@ -0,0 +1,32 @@ +/** + * 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.infrastructure.businessdate.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; + +public interface BusinessDateWritePlatformService { + + CommandProcessingResult updateBusinessDate(JsonCommand command); + + void increaseCOBDateByOneDay() throws JobExecutionException; + + void increaseBusinessDateByOneDay() throws JobExecutionException; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceImpl.java new file mode 100644 index 000000000..0a02df061 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceImpl.java @@ -0,0 +1,149 @@ +/** + * 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.infrastructure.businessdate.service; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDate; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.businessdate.exception.BusinessDateActionException; +import org.apache.fineract.infrastructure.businessdate.validator.BusinessDateDataParserAndValidator; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; +import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BusinessDateWritePlatformServiceImpl implements BusinessDateWritePlatformService { + + private final BusinessDateDataParserAndValidator dataValidator; + private final BusinessDateRepository repository; + private final ConfigurationDomainService configurationDomainService; + + @Override + public CommandProcessingResult updateBusinessDate(@NotNull final JsonCommand command) { + BusinessDateData data = dataValidator.validateAndParseUpdate(command); + Map changes = new HashMap<>(); + adjustDate(data, changes); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).with(changes).build(); + + } + + private void adjustDate(BusinessDateData data, Map changes) { + boolean isCOBDateAdjustmentEnabled = configurationDomainService.isCOBDateAdjustmentEnabled(); + boolean isBusinessDateEnabled = configurationDomainService.isBusinessDateEnabled(); + + if (!isBusinessDateEnabled) { + log.error("Business date functionality is not enabled!"); + throw new BusinessDateActionException("business.date.is.not.enabled", "Business date functionality is not enabled"); + } + updateOrCreateBusinessDate(data.getType(), data.getDate(), changes); + if (isCOBDateAdjustmentEnabled && BusinessDateType.BUSINESS_DATE.name().equals(data.getType())) { + updateOrCreateBusinessDate(BusinessDateType.COB_DATE.getName(), data.getDate().minus(1, ChronoUnit.DAYS), changes); + } + } + + @Override + @CronTarget(jobName = JobName.INCREASE_COB_DATE_BY_1_DAY) + public void increaseCOBDateByOneDay() throws JobExecutionException { + increaseDateByTypeByOneDay(BusinessDateType.COB_DATE); + } + + @Override + @CronTarget(jobName = JobName.INCREASE_BUSINESS_DATE_BY_1_DAY) + public void increaseBusinessDateByOneDay() throws JobExecutionException { + increaseDateByTypeByOneDay(BusinessDateType.BUSINESS_DATE); + } + + private void increaseDateByTypeByOneDay(BusinessDateType businessDateType) throws JobExecutionException { + List exceptions = new ArrayList<>(); + Map changes = new HashMap<>(); + Optional businessDateEntity = repository.findByType(businessDateType); + LocalDate businessDate = businessDateEntity.map(BusinessDate::getDate).orElse(DateUtils.getLocalDateOfTenant()); + businessDate = businessDate.plusDays(1); + try { + BusinessDateData businessDateData = BusinessDateData.instance(businessDateType, businessDate); + adjustDate(businessDateData, changes); + } catch (final PlatformApiDataValidationException e) { + final List errors = e.getErrors(); + for (final ApiParameterError error : errors) { + log.error("Increasing {} by 1 day failed due to: {}", businessDateType.getDescription(), error.getDeveloperMessage()); + } + exceptions.add(e); + } catch (final AbstractPlatformDomainRuleException e) { + log.error("Increasing {} by 1 day failed due to: {}", businessDateType.getDescription(), e.getDefaultUserMessage()); + exceptions.add(e); + } catch (Exception e) { + log.error("Increasing {} by 1 day failed due to: {}", businessDateType.getDescription(), e.getMessage()); + exceptions.add(e); + } + + if (!exceptions.isEmpty()) { + throw new JobExecutionException(exceptions); + } + } + + private void updateOrCreateBusinessDate(String type, LocalDate newDate, Map changes) { + BusinessDateType businessDateType = BusinessDateType.valueOf(type); + Optional businessDate = repository.findByType(businessDateType); + + if (businessDate.isEmpty()) { + BusinessDate newBusinessDate = BusinessDate.instance(businessDateType, newDate); + repository.save(newBusinessDate); + changes.put(type, newBusinessDate.getDate()); + } else { + updateBusinessDate(businessDate.get(), newDate, changes); + } + } + + private void updateBusinessDate(BusinessDate businessDate, LocalDate newDate, Map changes) { + LocalDate oldDate = businessDate.getDate(); + + if (!hasChange(oldDate, newDate)) { + return; + } + businessDate.updateDate(newDate); + repository.save(businessDate); + changes.put(businessDate.getType().name(), newDate); + } + + private boolean hasChange(@NotNull LocalDate oldDate, @NotNull LocalDate date) { + return (date.isBefore(oldDate) || date.isAfter(oldDate)); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateDataParserAndValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateDataParserAndValidator.java new file mode 100644 index 000000000..a42372947 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateDataParserAndValidator.java @@ -0,0 +1,107 @@ +/** + * 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.infrastructure.businessdate.validator; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.stereotype.Component; + +@Slf4j +@RequiredArgsConstructor +@Component +public class BusinessDateDataParserAndValidator { + + private final FromJsonHelper jsonHelper; + + public BusinessDateData validateAndParseUpdate(@NotNull final JsonCommand command) { + final DataValidatorBuilder dataValidator = new DataValidatorBuilder(new ArrayList<>()).resource("businessdate.update"); + JsonObject element = extractJsonObject(command); + + BusinessDateData result = validateAndParseUpdate(dataValidator, element, jsonHelper); + throwExceptionIfValidationWarningsExist(dataValidator); + + return result; + } + + private JsonObject extractJsonObject(JsonCommand command) { + String json = command.json(); + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + final JsonElement element = jsonHelper.parse(json); + return element.getAsJsonObject(); + } + + private void throwExceptionIfValidationWarningsExist(DataValidatorBuilder dataValidator) { + if (dataValidator.hasError()) { + log.error("Business date - Validation errors: {}", dataValidator.getDataValidationErrors()); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidator.getDataValidationErrors()); + } + } + + private BusinessDateData validateAndParseUpdate(final DataValidatorBuilder dataValidator, JsonObject element, + FromJsonHelper jsonHelper) { + if (element == null) { + return null; + } + + jsonHelper.checkForUnsupportedParameters(element, List.of("type", "date", "dateFormat", "locale")); + + String businessDateTypeName = jsonHelper.extractStringNamed("type", element); + final String localeValue = jsonHelper.extractStringNamed("locale", element); + final String dateFormat = jsonHelper.extractDateFormatParameter(element); + final String dateValue = jsonHelper.extractStringNamed("date", element); + dataValidator.reset().parameter("type").value(businessDateTypeName).notBlank(); + dataValidator.reset().parameter("locale").value(localeValue).notBlank(); + dataValidator.reset().parameter("dateFormat").value(dateFormat).notBlank(); + dataValidator.reset().parameter("date").value(dateValue).notBlank(); + + if (dataValidator.hasError()) { + return null; + } + Locale locale = jsonHelper.extractLocaleParameter(element); + BusinessDateType type; + try { + type = BusinessDateType.valueOf(businessDateTypeName); + } catch (IllegalArgumentException e) { + dataValidator.reset().parameter("type").failWithCode("Invalid Business Type value: `" + businessDateTypeName + "`"); + return null; + } + LocalDate date = jsonHelper.extractLocalDateNamed("date", element, dateFormat, locale); + dataValidator.reset().parameter("date").value(date).notNull(); + return dataValidator.hasError() ? null : BusinessDateData.instance(type, date); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java index 037f4d6a2..e7839ed34 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -112,4 +112,8 @@ public interface ConfigurationDomainService { boolean isRelaxingDaysConfigForPivotDateEnabled(); Long retrieveRelaxingDaysConfigForPivotDate(); + + boolean isBusinessDateEnabled(); + + boolean isCOBDateAdjustmentEnabled(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 30426ef74..4dbd63d45 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -38,6 +38,8 @@ import org.springframework.transaction.annotation.Transactional; @Service public class ConfigurationDomainServiceJpa implements ConfigurationDomainService { + public static final String ENABLE_BUSINESS_DATE = "enable_business_date"; + public static final String ENABLE_AUTOMATIC_COB_DATE_ADJUSTMENT = "enable_automatic_cob_date_adjustment"; private final PermissionRepository permissionRepository; private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository; private final PlatformCacheRepository cacheTypeRepository; @@ -432,4 +434,13 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService return property.isEnabled(); } + @Override + public boolean isBusinessDateEnabled() { + return getGlobalConfigurationPropertyData(ENABLE_BUSINESS_DATE).isEnabled(); + } + + @Override + public boolean isCOBDateAdjustmentEnabled() { + return getGlobalConfigurationPropertyData(ENABLE_AUTOMATIC_COB_DATE_ADJUSTMENT).isEnabled(); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java index 9d9f265dd..595ae95d3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java @@ -175,6 +175,11 @@ public final class JsonCommand { this.organisationCreditBureauId = null; } + public static JsonCommand from(final String jsonCommand) { + return new JsonCommand(null, jsonCommand, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + + } + public Long getOrganisationCreditBureauId() { return this.organisationCreditBureauId; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiParameterError.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiParameterError.java index 887ebcf62..b88e84da8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiParameterError.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiParameterError.java @@ -133,6 +133,6 @@ public final class ApiParameterError { @Override public String toString() { - return "ApiParameterError{developerMessage=" + developerMessage + "; ... }"; + return developerMessage; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java index 8cd248166..d061e6322 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java @@ -21,10 +21,12 @@ package org.apache.fineract.infrastructure.core.data; import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import lombok.ToString; /** * Represents the successful result of an REST API call that results in processing a command. */ +@ToString public class CommandProcessingResult implements Serializable { private Long commandId; diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java index 2b3e0260c..5cbe6f4a8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java @@ -115,8 +115,8 @@ public class DataValidatorBuilder { if (StringUtils.isBlank(linkedValue)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(linkedParameterName).append(".cannot.be.empty.when.").append(this.parameter).append(".is.populated"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(linkedParameterName) - .append(" cannot be empty when ").append(this.parameter).append(" is populated."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(linkedParameterName) + .append("` cannot be empty when ").append(this.parameter).append(" is populated."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), linkedParameterName, linkedValue, this.value); this.dataValidationErrors.add(error); @@ -149,8 +149,8 @@ public class DataValidatorBuilder { if (this.value != null && !this.value.equals(linkedValue)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(linkedParameterName).append(".not.equal.to.").append(this.parameter); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(linkedParameterName) - .append(" is not equal to ").append(this.parameter).append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(linkedParameterName) + .append("` is not equal to ").append(this.parameter).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), linkedParameterName, linkedValue, this.value); this.dataValidationErrors.add(error); @@ -166,8 +166,8 @@ public class DataValidatorBuilder { if (this.value != null && this.value.equals(linkedValue)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(linkedParameterName).append(".same.as.").append(this.parameter); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(linkedParameterName) - .append(" is same as ").append(this.parameter).append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(linkedParameterName) + .append("` is same as ").append(this.parameter).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), linkedParameterName, linkedValue, this.value); this.dataValidationErrors.add(error); @@ -184,8 +184,8 @@ public class DataValidatorBuilder { if (!trueOfFalseFieldProvided && !this.ignoreNullValue) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".must.be.true.or.false"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be set as true or false."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be set as true or false."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter); this.dataValidationErrors.add(error); @@ -199,8 +199,8 @@ public class DataValidatorBuilder { if (!trueOfFalseField.toString().equalsIgnoreCase("true") && !trueOfFalseField.toString().equalsIgnoreCase("false")) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".must.be.true.or.false"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be set as true or false."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be set as true or false."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter); this.dataValidationErrors.add(error); @@ -223,8 +223,8 @@ public class DataValidatorBuilder { } validationErrorCode.append(".cannot.be.blank"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(realParameterName) - .append(" is mandatory."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(realParameterName) + .append("` is mandatory."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), realParameterName, this.arrayIndex); this.dataValidationErrors.add(error); @@ -248,8 +248,8 @@ public class DataValidatorBuilder { } validationErrorCode.append(".cannot.be.blank"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(realParameterName) - .append(" is mandatory."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(realParameterName) + .append("` is mandatory."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), realParameterName, this.arrayIndex); this.dataValidationErrors.add(error); @@ -265,8 +265,8 @@ public class DataValidatorBuilder { if (this.value != null && this.value.toString().trim().length() > maxLength) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".exceeds.max.length"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" exceeds max length of ").append(maxLength).append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` exceeds max length of ").append(maxLength).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, maxLength, this.value.toString()); this.dataValidationErrors.add(error); @@ -284,8 +284,8 @@ public class DataValidatorBuilder { if (number < min || number > max) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.within.expected.range"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be between ").append(min).append(" and ").append(max).append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be between ").append(min).append(" and ").append(max).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, min, max); this.dataValidationErrors.add(error); @@ -305,8 +305,8 @@ public class DataValidatorBuilder { if (this.value == null || !valuesList.contains(this.value)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.one.of.expected.enumerations"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be one of [ ").append(valuesListStr).append(" ] ").append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be one of [ ").append(valuesListStr).append(" ] ").append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, values); @@ -333,8 +333,8 @@ public class DataValidatorBuilder { if (this.value == null || !valuesList.contains(this.value.toString().toLowerCase())) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.one.of.expected.enumerations"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be one of [ ").append(valuesListStr).append(" ] ").append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be one of [ ").append(valuesListStr).append(" ] ").append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, values); @@ -357,8 +357,8 @@ public class DataValidatorBuilder { if (this.value == null || !valuesListLowercase.contains(this.value.toString().toLowerCase())) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.one.of.expected.enumerations"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be one of [ ").append(valuesListStr).append(" ] ").append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be one of [ ").append(valuesListStr).append(" ] ").append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, valuesList); @@ -381,8 +381,8 @@ public class DataValidatorBuilder { if (valuesList.contains(this.value)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.one.of.unwanted.enumerations"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must not be any of [ ").append(valuesListStr).append(" ] ").append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must not be any of [ ").append(valuesListStr).append(" ] ").append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, values); @@ -403,8 +403,8 @@ public class DataValidatorBuilder { if (number.compareTo(BigDecimal.ZERO) <= 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.greater.than.zero"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than 0."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than 0."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, 0); this.dataValidationErrors.add(error); @@ -426,8 +426,8 @@ public class DataValidatorBuilder { if (number.compareTo(BigDecimal.ZERO) < 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.zero.or.greater"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than or equal to 0."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than or equal to 0."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, 0); this.dataValidationErrors.add(error); @@ -449,8 +449,8 @@ public class DataValidatorBuilder { if (number < 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.zero.or.greater"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be zero or greater."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be zero or greater."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, 0); this.dataValidationErrors.add(error); @@ -472,8 +472,8 @@ public class DataValidatorBuilder { if (number < 1) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.greater.than.zero"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than 0."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than 0."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, 0); this.dataValidationErrors.add(error); @@ -492,8 +492,8 @@ public class DataValidatorBuilder { if (intValue < number + 1) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.greater.than.specified.number"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than ").append(number); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than ").append(number); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, intValue, number); this.dataValidationErrors.add(error); @@ -512,8 +512,8 @@ public class DataValidatorBuilder { if (intValue < number) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.equal.to.or.greater.than.specified.number"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be equal to or greater than").append(number); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be equal to or greater than").append(number); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, intValue, number); this.dataValidationErrors.add(error); @@ -532,8 +532,8 @@ public class DataValidatorBuilder { if (!intValue.equals(number)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.equal.to.specified.number"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be same as").append(number); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be same as").append(number); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, intValue, number); this.dataValidationErrors.add(error); @@ -552,8 +552,8 @@ public class DataValidatorBuilder { if (intValue < number || intValue % number != 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.in.multiples.of.specified.number"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be multiples of ").append(number); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be multiples of ").append(number); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, intValue, number); this.dataValidationErrors.add(error); @@ -575,8 +575,8 @@ public class DataValidatorBuilder { if (number < 1) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.greater.than.zero"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than 0."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than 0."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, 0); this.dataValidationErrors.add(error); @@ -598,8 +598,8 @@ public class DataValidatorBuilder { if (number < 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.equal.or.greater.than.zero"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be equal or greater than 0."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be equal or greater than 0."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, 0); this.dataValidationErrors.add(error); @@ -618,8 +618,8 @@ public class DataValidatorBuilder { if (longValue < number + 1) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.greater.than.specified.number"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than ").append(number); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than ").append(number); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, longValue, number); this.dataValidationErrors.add(error); @@ -638,8 +638,8 @@ public class DataValidatorBuilder { if (longValue < number + 1) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".not.greater.than.specified.").append(paramName).append(".at Index.").append(index); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be greater than ").append(number); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than ").append(number); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, longValue, number); this.dataValidationErrors.add(error); @@ -657,8 +657,8 @@ public class DataValidatorBuilder { if (ObjectUtils.isEmpty(array)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".cannot.be.empty"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" cannot be empty. You must select at least one."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` cannot be empty. You must select at least one."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter); this.dataValidationErrors.add(error); @@ -675,8 +675,8 @@ public class DataValidatorBuilder { if (this.value != null && !array.iterator().hasNext()) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".cannot.be.empty"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" cannot be empty. You must select at least one."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` cannot be empty. You must select at least one."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter); this.dataValidationErrors.add(error); @@ -687,7 +687,8 @@ public class DataValidatorBuilder { public void expectedArrayButIsNot() { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.an.array"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append(" is not an array."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` is not an array."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter); this.dataValidationErrors.add(error); @@ -716,8 +717,8 @@ public class DataValidatorBuilder { public DataValidatorBuilder inValidValue(final String parameterValueCode, final Object invalidValue) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".invalid.").append(parameterValueCode); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" has an invalid value."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` has an invalid value."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, invalidValue); this.dataValidationErrors.add(error); @@ -740,8 +741,8 @@ public class DataValidatorBuilder { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".cannot.also.be.provided.when.").append(parameterName).append(".is.populated"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" cannot also be provided when ").append(parameterName).append(" is populated."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` cannot also be provided when `").append(parameterName).append("` is populated."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, parameterName, parameterValue); this.dataValidationErrors.add(error); @@ -765,8 +766,8 @@ public class DataValidatorBuilder { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".cannot.also.be.provided.when.").append(parameterName).append(".is.") .append(parameterValue); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" cannot also be provided when ").append(parameterName).append(" is ").append(parameterValue); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` cannot also be provided when `").append(parameterName).append("` is ").append(parameterValue); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, parameterName, parameterValue); this.dataValidationErrors.add(error); @@ -780,21 +781,22 @@ public class DataValidatorBuilder { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must be provided when ").append(parameterName).append(" is ").append(parameterValue); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be provided when `").append(parameterName).append("` is ").append(parameterValue); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, parameterName, parameterValue); this.dataValidationErrors.add(error); return this; } - public DataValidatorBuilder comapareMinimumAndMaximumAmounts(final BigDecimal minimumBalance, final BigDecimal maximumBalance) { + public DataValidatorBuilder compareMinimumAndMaximumAmounts(final BigDecimal minimumBalance, final BigDecimal maximumBalance) { if (minimumBalance != null && maximumBalance != null) { if (maximumBalance.compareTo(minimumBalance) < 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.within.expected.range"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(" minimum amount ") - .append(minimumBalance).append(" should less than maximum amount ").append(maximumBalance).append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` minimum amount ").append(minimumBalance).append(" should less than the maximum amount ") + .append(maximumBalance).append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, minimumBalance, maximumBalance); this.dataValidationErrors.add(error); @@ -810,7 +812,7 @@ public class DataValidatorBuilder { if (amount.compareTo(minimumAmount) < 0 || amount.compareTo(maximumAmount) > 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".amount.is.not.within.min.max.range"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter).append(" amount ") + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter).append("` amount ") .append(amount).append(" must be between ").append(minimumAmount).append(" and ").append(maximumAmount) .append(" ."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), @@ -828,8 +830,8 @@ public class DataValidatorBuilder { if (amount.compareTo(min) < 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.less.than.min"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter).append(" value ") - .append(amount).append(" must not be less than minimum value ").append(min); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter).append("` value ") + .append(amount).append(" must not be less than the minimum value ").append(min); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, amount, min); this.dataValidationErrors.add(error); @@ -845,7 +847,7 @@ public class DataValidatorBuilder { if (amount.compareTo(max) > 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.greater.than.max"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter).append(" value ") + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter).append("` value ") .append(amount).append(" must not be more than maximum value ").append(max); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, amount, max); @@ -856,7 +858,7 @@ public class DataValidatorBuilder { return this; } - public DataValidatorBuilder comapareMinAndMaxOfTwoBigDecmimalNos(final BigDecimal min, final BigDecimal max) { + public DataValidatorBuilder compareMinAndMaxOfTwoBigDecmimalNos(final BigDecimal min, final BigDecimal max) { if (min != null && max != null) { if (max.compareTo(min) < 0) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") @@ -884,7 +886,7 @@ public class DataValidatorBuilder { return this; } catch (final ParseException e) { final ApiParameterError error = ApiParameterError.parameterError("validation.msg.recurring.rule.parsing.error", - "Error in pasring the Recurring Rule value: " + recurringRule + ".", this.parameter, recurringRule); + "Error in parsing the Recurring Rule value: " + recurringRule + ".", this.parameter, recurringRule); this.dataValidationErrors.add(error); return this; } @@ -902,8 +904,8 @@ public class DataValidatorBuilder { if (number < min) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.less.than.min"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) - .append(" must be greater than minimum value ").append(min); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than the minimum value ").append(min); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, min); this.dataValidationErrors.add(error); @@ -922,8 +924,8 @@ public class DataValidatorBuilder { if (number > max) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.greater.than.max"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) - .append(" must be less than maximum value ").append(max); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be less than the maximum value ").append(max); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, number, max); this.dataValidationErrors.add(error); @@ -940,8 +942,8 @@ public class DataValidatorBuilder { if (this.value != null && !this.value.toString().matches(expression)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".does.not.match.regexp"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" must match the provided regular expression [ ").append(expression).append(" ] ").append("."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must match the provided regular expression [ ").append(expression).append(" ] ").append("."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value, expression); @@ -986,8 +988,8 @@ public class DataValidatorBuilder { if (validationErr) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".value.should.true.or.false"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" value should true or false "); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` value should true or false "); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value); this.dataValidationErrors.add(error); @@ -1017,8 +1019,8 @@ public class DataValidatorBuilder { if (validationErr) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".format.is.invalid"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.resource).append(this.parameter) - .append(" is in invalid format, should contain '-','+','()' and numbers only."); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` is in invalid format, should contain '-','+','()' and numbers only."); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value); this.dataValidationErrors.add(error); @@ -1030,8 +1032,8 @@ public class DataValidatorBuilder { if (this.value != null && !CronExpression.isValidExpression(this.value.toString().trim())) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".invalid"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) - .append(" value is not a valid cron expression"); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` value is not a valid cron expression"); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, this.value); this.dataValidationErrors.add(error); @@ -1049,8 +1051,8 @@ public class DataValidatorBuilder { if (date.isAfter(dateVal)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.less.than.date"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) - .append(" must be greter than provided date").append(date); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be greater than the provided date").append(date); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, dateVal, date); this.dataValidationErrors.add(error); @@ -1069,8 +1071,8 @@ public class DataValidatorBuilder { if (date.isBefore(dateVal)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.greater.than.date"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) - .append(" must be less than provided date").append(date); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be less than the provided date").append(date); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, dateVal, date); this.dataValidationErrors.add(error); @@ -1089,8 +1091,8 @@ public class DataValidatorBuilder { if (dateVal.isAfter(date)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.greater.than.date"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) - .append(" must be less than or equal to provided date").append(date); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be less than or equal to the provided date").append(date); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, dateVal, date); this.dataValidationErrors.add(error); @@ -1109,8 +1111,8 @@ public class DataValidatorBuilder { if (!dateVal.isEqual(date)) { final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") .append(this.parameter).append(".is.not.equal.to.date"); - final StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(this.parameter) - .append(" must be equal to provided date").append(date); + final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) + .append("` must be equal to the provided date").append(date); final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), this.parameter, dateVal, date); this.dataValidationErrors.add(error); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/PlatformApiDataValidationException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/PlatformApiDataValidationException.java index 70d8aeeca..f20b4cf34 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/PlatformApiDataValidationException.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/PlatformApiDataValidationException.java @@ -59,4 +59,9 @@ public class PlatformApiDataValidationException extends AbstractPlatformExceptio public List getErrors() { return this.errors; } + + @Override + public String toString() { + return "PlatformApiDataValidationException{" + "errors=" + errors + '}'; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DefaultToApiJsonSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DefaultToApiJsonSerializer.java index 12251ce43..1cea0f608 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DefaultToApiJsonSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DefaultToApiJsonSerializer.java @@ -30,7 +30,7 @@ import org.springframework.stereotype.Component; * into JSON. */ @Component -public final class DefaultToApiJsonSerializer implements ToApiJsonSerializer { +public class DefaultToApiJsonSerializer implements ToApiJsonSerializer { private final ExcludeNothingWithPrettyPrintingOffJsonSerializerGoogleGson excludeNothingWithPrettyPrintingOff; private final ExcludeNothingWithPrettyPrintingOnJsonSerializerGoogleGson excludeNothingWithPrettyPrintingOn; diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java index ac43cfbca..b7d30c2f9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java @@ -36,6 +36,7 @@ import java.time.LocalDateTime; import java.time.MonthDay; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.Arrays; @@ -349,8 +350,8 @@ public class JsonParserHelper { } catch (final IllegalArgumentException e) { final List dataValidationErrors = new ArrayList<>(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.month.day", - "The parameter " + parameterName + " is invalid based on the monthDayFormat: '" + dateFormat - + "' and locale: '" + clientApplicationLocale + "' provided:", + "The parameter `" + parameterName + "` is invalid based on the monthDayFormat: `" + dateFormat + + "` and locale: `" + clientApplicationLocale + "` provided:", parameterName, valueAsString, dateFormat); dataValidationErrors.add(error); @@ -422,7 +423,7 @@ public class JsonParserHelper { } } catch (IllegalArgumentException e) { final List dataValidationErrors = new ArrayList<>(); - final String defaultMessage = new StringBuilder("The parameter '" + timeValueAsString + "' is not in correct format.") + final String defaultMessage = new StringBuilder("The parameter `" + timeValueAsString + "` is not in correct format.") .toString(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.TimeFormat", defaultMessage, parameterName); @@ -475,12 +476,12 @@ public class JsonParserHelper { .parseDefaulting(ChronoField.HOUR_OF_DAY, 0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).toFormatter(clientApplicationLocale); eventLocalDateTime = LocalDateTime.parse(dateTimeAsString, formatter); - } catch (final IllegalArgumentException e) { + } catch (final IllegalArgumentException | DateTimeParseException e) { final List dataValidationErrors = new ArrayList<>(); final ApiParameterError error = ApiParameterError .parameterError("validation.msg.invalid.dateFormat.format", - "The parameter " + parameterName + " is invalid based on the dateFormat: '" + dateTimeFormat - + "' and locale: '" + clientApplicationLocale + "' provided:", + "The parameter `" + parameterName + "` is invalid based on the dateFormat: `" + dateTimeFormat + + "` and locale: `" + clientApplicationLocale + "` provided:", parameterName, eventLocalDateTime, dateTimeFormat); dataValidationErrors.add(error); @@ -499,14 +500,14 @@ public class JsonParserHelper { final List dataValidationErrors = new ArrayList<>(); if (StringUtils.isBlank(dateFormat)) { final String defaultMessage = new StringBuilder( - "The parameter '" + parameterName + "' requires a 'dateFormat' parameter to be passed with it.").toString(); + "The parameter `" + parameterName + "` requires a `dateFormat` parameter to be passed with it.").toString(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.dateFormat.parameter", defaultMessage, parameterName); dataValidationErrors.add(error); } if (clientApplicationLocale == null) { final String defaultMessage = new StringBuilder( - "The parameter '" + parameterName + "' requires a 'locale' parameter to be passed with it.").toString(); + "The parameter `" + parameterName + "` requires a `locale` parameter to be passed with it.").toString(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage, parameterName); dataValidationErrors.add(error); @@ -524,7 +525,7 @@ public class JsonParserHelper { final List dataValidationErrors = new ArrayList<>(); final String defaultMessage = new StringBuilder( - "The parameter '" + parameterName + "' requires a 'locale' parameter to be passed with it.").toString(); + "The parameter `" + parameterName + "` requires a `locale` parameter to be passed with it.").toString(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage, parameterName); dataValidationErrors.add(error); @@ -572,7 +573,7 @@ public class JsonParserHelper { final List dataValidationErrors = new ArrayList<>(); final ApiParameterError error = ApiParameterError.parameterErrorWithValue("validation.msg.invalid.integer.format", - "The parameter " + parameterName + " has value: " + numericalValueFormatted + "The parameter `" + parameterName + "` has value: " + numericalValueFormatted + " which is invalid integer value for provided locale of [" + clientApplicationLocale.toString() + "].", parameterName, numericalValueFormatted, numericalValueFormatted, clientApplicationLocale); dataValidationErrors.add(error); @@ -596,7 +597,7 @@ public class JsonParserHelper { final List dataValidationErrors = new ArrayList<>(); final ApiParameterError error = ApiParameterError.parameterErrorWithValue("validation.msg.invalid.integer", - "The parameter " + parameterName + " has value: " + numericalValueFormatted + " which is invalid integer.", + "The parameter `" + parameterName + "` has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted, numericalValueFormatted); dataValidationErrors.add(error); @@ -611,7 +612,7 @@ public class JsonParserHelper { final List dataValidationErrors = new ArrayList<>(); final String defaultMessage = new StringBuilder( - "The parameter '" + parameterName + "' requires a 'locale' parameter to be passed with it.").toString(); + "The parameter `" + parameterName + "` requires a `locale` parameter to be passed with it.").toString(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage, parameterName); dataValidationErrors.add(error); @@ -650,8 +651,8 @@ public class JsonParserHelper { final List dataValidationErrors = new ArrayList<>(); final ApiParameterError error = ApiParameterError.parameterErrorWithValue("validation.msg.invalid.decimal.format", - "The parameter " + parameterName + " has value: " + numericalValueFormatted - + " which is invalid decimal value for provided locale of [" + clientApplicationLocale.toString() + "].", + "The parameter `" + parameterName + "` has value: " + numericalValueFormatted + + " which is invalid decimal value for provided locale of [" + clientApplicationLocale + "].", parameterName, numericalValueFormatted, numericalValueFormatted, clientApplicationLocale); dataValidationErrors.add(error); @@ -668,7 +669,7 @@ public class JsonParserHelper { if (StringUtils.isBlank(localeAsString)) { final List dataValidationErrors = new ArrayList<>(); final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format", - "The parameter locale is invalid. It cannot be blank.", "locale"); + "The parameter `locale` is invalid. It cannot be blank.", "locale"); dataValidationErrors.add(error); throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", @@ -706,7 +707,7 @@ public class JsonParserHelper { final List allowedLanguages = Arrays.asList(Locale.getISOLanguages()); if (!allowedLanguages.contains(languageCode.toLowerCase())) { final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format", - "The parameter locale has an invalid language value " + languageCode + " .", "locale", languageCode); + "The parameter `locale` has an invalid language value " + languageCode + " .", "locale", languageCode); dataValidationErrors.add(error); } @@ -714,7 +715,7 @@ public class JsonParserHelper { final List allowedCountries = Arrays.asList(Locale.getISOCountries()); if (!allowedCountries.contains(courntryCode)) { final ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format", - "The parameter locale has an invalid country value " + courntryCode + " .", "locale", courntryCode); + "The parameter `locale` has an invalid country value " + courntryCode + " .", "locale", courntryCode); dataValidationErrors.add(error); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java index 23baa55cd..6da2d4bbf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java @@ -45,9 +45,11 @@ public enum JobName { "Get Delivery Reports from SMS Gateway"), GENERATE_ADHOCCLIENT_SCEHDULE( "Generate AdhocClient Schedule"), UPDATE_EMAIL_OUTBOUND_WITH_CAMPAIGN_MESSAGE( "Update Email Outbound with campaign message"), EXECUTE_EMAIL( - "Execute Email"), UPDATE_TRAIL_BALANCE_DETAILS( + "Execute Email"), UPDATE_TRIAL_BALANCE_DETAILS( "Update Trial Balance Details"), EXECUTE_DIRTY_JOBS( - "Execute All Dirty Jobs"); + "Execute All Dirty Jobs"), INCREASE_BUSINESS_DATE_BY_1_DAY( + "Increase Business Date by 1 day"), INCREASE_COB_DATE_BY_1_DAY( + "Increase COB Date by 1 day"); private final String name; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/search/data/AdHocQueryDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/search/data/AdHocQueryDataValidator.java index c7b418105..643e8f575 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/search/data/AdHocQueryDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/search/data/AdHocQueryDataValidator.java @@ -151,7 +151,7 @@ public class AdHocQueryDataValidator { .extractBigDecimalWithLocaleNamed(AdHocQuerySearchConstants.maxOutStandingAmountPercentageParamName, element); baseDataValidator.reset().parameter(AdHocQuerySearchConstants.maxOutStandingAmountPercentageParamName) .value(maxOutStandingAmountPercentage).notNull().notLessThanMin(BigDecimal.ZERO); - baseDataValidator.reset().comapareMinAndMaxOfTwoBigDecmimalNos(minOutStandingAmountPercentage, + baseDataValidator.reset().compareMinAndMaxOfTwoBigDecmimalNos(minOutStandingAmountPercentage, maxOutStandingAmountPercentage); } else { if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outStandingAmountPercentageParamName, element)) { @@ -184,7 +184,7 @@ public class AdHocQueryDataValidator { .extractBigDecimalWithLocaleNamed(AdHocQuerySearchConstants.maxOutstandingAmountParamName, element); baseDataValidator.reset().parameter(AdHocQuerySearchConstants.maxOutstandingAmountParamName).value(maxOutstandingAmount) .notNull().notLessThanMin(BigDecimal.ZERO); - baseDataValidator.reset().comapareMinAndMaxOfTwoBigDecmimalNos(minOutstandingAmount, maxOutstandingAmount); + baseDataValidator.reset().compareMinAndMaxOfTwoBigDecmimalNos(minOutstandingAmount, maxOutstandingAmount); } else { final BigDecimal outstandingAmount = this.fromApiJsonHelper .extractBigDecimalWithLocaleNamed(AdHocQuerySearchConstants.outstandingAmountParamName, element); diff --git a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index 862dc0d71..3cc6775e7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -20,7 +20,6 @@ package org.apache.fineract.scheduledjobs.service; import java.math.BigDecimal; import java.math.BigInteger; -import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -329,7 +328,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService } @Override - @CronTarget(jobName = JobName.UPDATE_TRAIL_BALANCE_DETAILS) + @CronTarget(jobName = JobName.UPDATE_TRIAL_BALANCE_DETAILS) public void updateTrialBalanceDetails() throws JobExecutionException { final JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSourceServiceFactory.determineDataSourceService().retrieveDataSource()); final StringBuilder tbGapSqlBuilder = new StringBuilder(500); @@ -344,15 +343,14 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService if (days < 1) { continue; } - final String formattedDate = new SimpleDateFormat("yyyy-MM-dd").format(tbGap); final StringBuilder sqlBuilder = new StringBuilder(600); sqlBuilder.append("Insert Into m_trial_balance(office_id, account_id, Amount, entry_date, created_date,closing_balance) ") .append("Select je.office_id, je.account_id, SUM(CASE WHEN je.type_enum=1 THEN (-1) * je.amount ELSE je.amount END) ") - .append("as Amount, Date(je.entry_date) as 'Entry_Date', je.transaction_date as 'Created_Date',sum(je.amount) as closing_balance ") + .append("as Amount, Date(je.entry_date) as Entry_Date, je.transaction_date as Created_Date,sum(je.amount) as closing_balance ") .append("from acc_gl_journal_entry je WHERE je.transaction_date = ? ") .append("group by je.account_id, je.office_id, je.transaction_date, Date(je.entry_date)"); - final int result = jdbcTemplate.update(sqlBuilder.toString(), formattedDate); + final int result = jdbcTemplate.update(sqlBuilder.toString(), tbGap); LOG.info("{}: Records affected by updateTrialBalanceDetails: {}", ThreadLocalContextUtil.getTenant().getName(), result); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java index 6fce5360b..7f31e9ca2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java @@ -552,9 +552,20 @@ public class AppUser extends AbstractPersistableCustom implements PlatformUser { } public void validateHasReadPermission(final String resourceType) { + validateHasPermission("READ", resourceType); + } - final String authorizationMessage = "User has no authority to view " + resourceType.toLowerCase() + "s"; - final String matchPermission = "READ_" + resourceType.toUpperCase(); + public void validateHasCreatePermission(final String resourceType) { + validateHasPermission("CREATE", resourceType); + } + + public void validateHasUpdatePermission(final String resourceType) { + validateHasPermission("UPDATE", resourceType); + } + + private void validateHasPermission(final String prefix, final String resourceType) { + final String authorizationMessage = "User has no authority to " + prefix + " " + resourceType.toLowerCase() + "s"; + final String matchPermission = prefix + "_" + resourceType.toUpperCase(); if (!hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission)) { return; diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 9d28ead57..00372f771 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -34,4 +34,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0015_add_business_date.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0015_add_business_date.xml new file mode 100644 index 000000000..f6c0d24a7 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0015_add_business_date.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiTest.java new file mode 100644 index 000000000..def9947cc --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/api/BusinessDateApiTest.java @@ -0,0 +1,163 @@ +/** + * 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.infrastructure.businessdate.api; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import java.io.IOException; +import java.util.List; +import javax.servlet.ServletException; +import javax.ws.rs.core.UriInfo; +import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService; +import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.useradministration.domain.AppUser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class BusinessDateApiTest { + + @Mock + private ApiRequestParameterHelper parameterHelper; + + @Mock + private BusinessDateReadPlatformService readPlatformService; + + @Mock + private UriInfo uriInfo; + + @Mock + private PlatformSecurityContext securityContext; + + @Mock + private DefaultToApiJsonSerializer jsonSerializer; + + @Mock + private PortfolioCommandSourceWritePlatformService commandWritePlatformService; + + @InjectMocks + private BusinessDateApiResource underTest; + + private ApiRequestJsonSerializationSettings apiRequestJsonSerializationSettings; + + @BeforeEach + void setUp() throws IOException { + apiRequestJsonSerializationSettings = new ApiRequestJsonSerializationSettings(false, null, false, false, false); + given(parameterHelper.process(Mockito.any())).willReturn(apiRequestJsonSerializationSettings); + } + + @Test + void getBusinessDatesAPIHasPermission() throws ServletException, IOException { + AppUser appUser = Mockito.mock(AppUser.class); + List response = Mockito.mock(List.class); + given(readPlatformService.findAll()).willReturn(response); + // given + Mockito.doNothing().when(appUser).validateHasReadPermission("BUSINESS_DATE"); + given(securityContext.authenticatedUser()).willReturn(appUser); + // when + underTest.getBusinessDates(uriInfo); + // then + verify(readPlatformService, Mockito.times(1)).findAll(); + verify(jsonSerializer, Mockito.times(1)).serialize(apiRequestJsonSerializationSettings, response); + } + + @Test + void getBusinessDatesAPIHasNoPermission() throws ServletException, IOException { + AppUser appUser = Mockito.mock(AppUser.class); + // given + Mockito.doThrow(NoAuthorizationException.class).when(appUser).validateHasReadPermission("BUSINESS_DATE"); + given(securityContext.authenticatedUser()).willReturn(appUser); + // when + assertThatThrownBy(() -> underTest.getBusinessDates(uriInfo)).isInstanceOf(NoAuthorizationException.class); + // then + verifyNoInteractions(readPlatformService); + } + + @Test + void getBusinessDateByTypeAPIHasPermission() throws ServletException, IOException { + AppUser appUser = Mockito.mock(AppUser.class); + BusinessDateData response = Mockito.mock(BusinessDateData.class); + given(readPlatformService.findByType("type")).willReturn(response); + // given + Mockito.doNothing().when(appUser).validateHasReadPermission("BUSINESS_DATE"); + given(securityContext.authenticatedUser()).willReturn(appUser); + // when + underTest.getBusinessDate("type", uriInfo); + // then + verify(readPlatformService, Mockito.times(1)).findByType("type"); + verify(jsonSerializer, Mockito.times(1)).serialize(apiRequestJsonSerializationSettings, response); + } + + @Test + void getBusinessDateByTypeAPIHasNoPermission() throws ServletException, IOException { + AppUser appUser = Mockito.mock(AppUser.class); + // given + Mockito.doThrow(NoAuthorizationException.class).when(appUser).validateHasReadPermission("BUSINESS_DATE"); + given(securityContext.authenticatedUser()).willReturn(appUser); + // when + assertThatThrownBy(() -> underTest.getBusinessDate("type", uriInfo)).isInstanceOf(NoAuthorizationException.class); + // then + verifyNoInteractions(readPlatformService); + } + + @Test + void postBusinessDateAPIHasPermission() throws ServletException, IOException { + AppUser appUser = Mockito.mock(AppUser.class); + CommandProcessingResult response = Mockito.mock(CommandProcessingResult.class); + // given + Mockito.doNothing().when(appUser).validateHasUpdatePermission("BUSINESS_DATE"); + given(securityContext.authenticatedUser()).willReturn(appUser); + given(commandWritePlatformService.logCommandSource(Mockito.any())).willReturn(response); + // when + underTest.updateBusinessDate("{}", uriInfo); + // then + verify(commandWritePlatformService, Mockito.times(1)).logCommandSource(Mockito.any()); + verify(jsonSerializer, Mockito.times(1)).serialize(response); + } + + @Test + void postBusinessDateAPIHasNoPermission() throws ServletException, IOException { + AppUser appUser = Mockito.mock(AppUser.class); + // given + Mockito.doThrow(NoAuthorizationException.class).when(appUser).validateHasUpdatePermission("BUSINESS_DATE"); + given(securityContext.authenticatedUser()).willReturn(appUser); + // when + assertThatThrownBy(() -> underTest.updateBusinessDate("{}", uriInfo)).isInstanceOf(NoAuthorizationException.class); + // then + verifyNoInteractions(readPlatformService); + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataSerialized.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataSerialized.java new file mode 100644 index 000000000..ff5ede342 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataSerialized.java @@ -0,0 +1,63 @@ +/** + * 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.infrastructure.businessdate.data; + +import static org.junit.Assert.assertEquals; + +import java.time.LocalDate; +import java.time.ZoneId; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.core.serialization.CommandProcessingResultJsonSerializer; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.apache.fineract.infrastructure.core.serialization.ExcludeNothingWithPrettyPrintingOffJsonSerializerGoogleGson; +import org.apache.fineract.infrastructure.core.serialization.ExcludeNothingWithPrettyPrintingOnJsonSerializerGoogleGson; +import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper; +import org.junit.Test; + +public class BusinessDataSerialized { + + @Test + public void serializeBusinessDateData() { + DefaultToApiJsonSerializer jsonSerializer = new DefaultToApiJsonSerializer<>( + new ExcludeNothingWithPrettyPrintingOffJsonSerializerGoogleGson(), + new ExcludeNothingWithPrettyPrintingOnJsonSerializerGoogleGson(), new CommandProcessingResultJsonSerializer(), + new GoogleGsonSerializerHelper()); + + LocalDate now = LocalDate.now(ZoneId.systemDefault()); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, now); + String result = jsonSerializer.serialize(businessDateData); + assertEquals("{\"description\":\"Business Date\",\"type\":\"BUSINESS_DATE\",\"date\":[" + now.getYear() + "," + now.getMonthValue() + + "," + now.getDayOfMonth() + "]}", result); + } + + @Test + public void serializeBusinessDateData_COB() { + DefaultToApiJsonSerializer jsonSerializer = new DefaultToApiJsonSerializer<>( + new ExcludeNothingWithPrettyPrintingOffJsonSerializerGoogleGson(), + new ExcludeNothingWithPrettyPrintingOnJsonSerializerGoogleGson(), new CommandProcessingResultJsonSerializer(), + new GoogleGsonSerializerHelper()); + + LocalDate now = LocalDate.now(ZoneId.systemDefault()); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.COB_DATE, now); + String result = jsonSerializer.serialize(businessDateData); + assertEquals("{\"description\":\"Close of Business Date\",\"type\":\"COB_DATE\",\"date\":[" + now.getYear() + "," + + now.getMonthValue() + "," + now.getDayOfMonth() + "]}", result); + } + +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataTypeTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataTypeTest.java new file mode 100644 index 000000000..a3257a317 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/data/BusinessDataTypeTest.java @@ -0,0 +1,37 @@ +/** + * 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.infrastructure.businessdate.data; + +import static org.junit.Assert.assertEquals; + +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.junit.Test; + +public class BusinessDataTypeTest { + + @Test + public void typoCheck() { + for (BusinessDateType businessDateType : BusinessDateType.values()) { + switch (businessDateType) { + case BUSINESS_DATE -> assertEquals("Business Date", businessDateType.getDescription()); + case COB_DATE -> assertEquals("Close of Business Date", businessDateType.getDescription()); + } + } + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapperTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapperTest.java new file mode 100644 index 000000000..9bbd6a5f0 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/mapper/BusinessDateMapperTest.java @@ -0,0 +1,56 @@ +/** + * 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.infrastructure.businessdate.mapper; + +import static org.junit.Assert.assertEquals; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Collections; +import java.util.List; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDate; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.junit.Test; +import org.mapstruct.factory.Mappers; + +public class BusinessDateMapperTest { + + private BusinessDateMapper businessDateMapper = Mappers.getMapper(BusinessDateMapper.class); + + @Test + public void testMapping() { + LocalDate now = LocalDate.now(ZoneId.systemDefault()); + BusinessDate businessDate = BusinessDate.instance(BusinessDateType.BUSINESS_DATE, now); + BusinessDateData businessDateData = businessDateMapper.map(businessDate); + assertEquals(businessDate.getDate(), businessDateData.getDate()); + assertEquals(businessDate.getType().getDescription(), businessDateData.getDescription()); + assertEquals(businessDate.getType().getName(), businessDateData.getType()); + } + + @Test + public void testMappingList() { + LocalDate now = LocalDate.now(ZoneId.systemDefault()); + BusinessDate businessDate = BusinessDate.instance(BusinessDateType.BUSINESS_DATE, now); + List businessDateData = businessDateMapper.map(Collections.singletonList(businessDate)); + assertEquals(businessDate.getDate(), businessDateData.get(0).getDate()); + assertEquals(businessDate.getType().getDescription(), businessDateData.get(0).getDescription()); + assertEquals(businessDate.getType().getName(), businessDateData.get(0).getType()); + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceTest.java new file mode 100644 index 000000000..b20bbb85e --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateReadPlatformServiceTest.java @@ -0,0 +1,96 @@ +/** + * 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.infrastructure.businessdate.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.Optional; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDate; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.businessdate.exception.BusinessDateNotFoundException; +import org.apache.fineract.infrastructure.businessdate.mapper.BusinessDateMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class BusinessDateReadPlatformServiceTest { + + @InjectMocks + private BusinessDateReadPlatformServiceImpl businessDateReadPlatformService; + + @Mock + private BusinessDateRepository repository; + + @Mock + private BusinessDateMapper mapper; + + @Test + public void notFoundByTypeNonexistentType() { + BusinessDateNotFoundException businessDateNotFoundException = assertThrows(BusinessDateNotFoundException.class, + () -> businessDateReadPlatformService.findByType("invalid")); + assertEquals("Business date with type `invalid` does not exist.", businessDateNotFoundException.getDefaultUserMessage()); + } + + @Test + public void notFoundByTypeNotStoredInDB() { + BusinessDateNotFoundException businessDateNotFoundException = assertThrows(BusinessDateNotFoundException.class, + () -> businessDateReadPlatformService.findByType("BUSINESS_DATE")); + assertEquals("Business date with type `BUSINESS_DATE` does not found.", businessDateNotFoundException.getDefaultUserMessage()); + } + + @Test + public void findAll() { + List resultList = Mockito.mock(List.class); + given(repository.findAll()).willReturn(resultList); + businessDateReadPlatformService.findAll(); + verify(repository, times(1)).findAll(); + verify(mapper, times(1)).map(resultList); + } + + @Test + public void findByCOBType() { + Optional result = Optional.of(Mockito.mock(BusinessDate.class)); + given(repository.findByType(BusinessDateType.COB_DATE)).willReturn(result); + businessDateReadPlatformService.findByType("COB_DATE"); + verify(repository, times(1)).findByType(BusinessDateType.COB_DATE); + verify(mapper, times(1)).map(result.get()); + } + + @Test + public void findByBusinessType() { + Optional result = Optional.of(Mockito.mock(BusinessDate.class)); + given(repository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(result); + businessDateReadPlatformService.findByType("BUSINESS_DATE"); + verify(repository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(mapper, times(1)).map(result.get()); + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceTest.java new file mode 100644 index 000000000..b896f8334 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/service/BusinessDateWritePlatformServiceTest.java @@ -0,0 +1,264 @@ +/** + * 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.infrastructure.businessdate.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Optional; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDate; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.businessdate.exception.BusinessDateActionException; +import org.apache.fineract.infrastructure.businessdate.validator.BusinessDateDataParserAndValidator; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class BusinessDateWritePlatformServiceTest { + + @InjectMocks + private BusinessDateWritePlatformServiceImpl underTest; + + @Mock + private BusinessDateDataParserAndValidator businessDateDataParserAndValidator; + + @Mock + private BusinessDateRepository businessDateRepository; + + @Mock + private ConfigurationDomainService configurationDomainService; + + @Captor + private ArgumentCaptor businessDateArgumentCaptor; + + @Test + public void businessDateIsNotEnabled() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, + LocalDate.now(ZoneId.systemDefault())); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.FALSE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + BusinessDateActionException exception = assertThrows(BusinessDateActionException.class, + () -> underTest.updateBusinessDate(command)); + assertEquals("Business date functionality is not enabled", exception.getDefaultUserMessage()); + } + + @Test + public void businessDateSetNew() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.FALSE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newEntity = Optional.empty(); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + LocalDate resultData = (LocalDate) result.getChanges().get("BUSINESS_DATE"); + assertEquals(LocalDate.of(2022, 6, 13), resultData); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(businessDateRepository, times(1)).save(businessDateArgumentCaptor.capture()); + assertEquals(LocalDate.of(2022, 6, 13), businessDateArgumentCaptor.getValue().getDate()); + assertEquals(BusinessDateType.BUSINESS_DATE, businessDateArgumentCaptor.getValue().getType()); + } + + @Test + public void cobDateSetNew() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.COB_DATE, LocalDate.of(2022, 6, 13)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.FALSE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newEntity = Optional.empty(); + given(businessDateRepository.findByType(BusinessDateType.COB_DATE)).willReturn(newEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + LocalDate resultData = (LocalDate) result.getChanges().get("COB_DATE"); + assertEquals(LocalDate.of(2022, 6, 13), resultData); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.COB_DATE); + verify(businessDateRepository, times(1)).save(businessDateArgumentCaptor.capture()); + assertEquals(LocalDate.of(2022, 6, 13), businessDateArgumentCaptor.getValue().getDate()); + assertEquals(BusinessDateType.COB_DATE, businessDateArgumentCaptor.getValue().getType()); + } + + @Test + public void businessDateSetModifyExistingWhenItWasAfter() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 11)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.FALSE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newEntity = Optional.of(BusinessDate.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 12))); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + LocalDate resultData = (LocalDate) result.getChanges().get("BUSINESS_DATE"); + assertEquals(LocalDate.of(2022, 6, 11), resultData); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(businessDateRepository, times(1)).save(businessDateArgumentCaptor.capture()); + assertEquals(LocalDate.of(2022, 6, 11), businessDateArgumentCaptor.getValue().getDate()); + assertEquals(BusinessDateType.BUSINESS_DATE, businessDateArgumentCaptor.getValue().getType()); + } + + @Test + public void businessDateSetModifyExistingWhenItWasBefore() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.FALSE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newEntity = Optional.of(BusinessDate.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 12))); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + LocalDate resultData = (LocalDate) result.getChanges().get("BUSINESS_DATE"); + assertEquals(LocalDate.of(2022, 6, 13), resultData); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(businessDateRepository, times(1)).save(businessDateArgumentCaptor.capture()); + assertEquals(LocalDate.of(2022, 6, 13), businessDateArgumentCaptor.getValue().getDate()); + assertEquals(BusinessDateType.BUSINESS_DATE, businessDateArgumentCaptor.getValue().getType()); + } + + @Test + public void businessDateSetModifyExistingButNoChanges() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.FALSE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newEntity = Optional.of(BusinessDate.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13))); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + assertNull(result.getChanges()); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(businessDateRepository, times(0)).save(businessDateArgumentCaptor.capture()); + } + + @Test + public void cobDateSetNewAutomatically() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.TRUE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newEntity = Optional.empty(); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + LocalDate businessDate = (LocalDate) result.getChanges().get("BUSINESS_DATE"); + assertEquals(LocalDate.of(2022, 6, 13), businessDate); + LocalDate cobDate = (LocalDate) result.getChanges().get("COB_DATE"); + assertEquals(LocalDate.of(2022, 6, 12), cobDate); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.COB_DATE); + verify(businessDateRepository, times(2)).save(businessDateArgumentCaptor.capture()); + assertEquals(LocalDate.of(2022, 6, 13), businessDateArgumentCaptor.getAllValues().get(0).getDate()); + assertEquals(BusinessDateType.BUSINESS_DATE, businessDateArgumentCaptor.getAllValues().get(0).getType()); + assertEquals(LocalDate.of(2022, 6, 12), businessDateArgumentCaptor.getAllValues().get(1).getDate()); + assertEquals(BusinessDateType.COB_DATE, businessDateArgumentCaptor.getAllValues().get(1).getType()); + } + + @Test + public void businessDateAndCobDateSetModifyExistingButNoChanges() { + JsonCommand command = JsonCommand.from(""); + BusinessDateData businessDateData = BusinessDateData.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13)); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.TRUE); + given(businessDateDataParserAndValidator.validateAndParseUpdate(command)).willReturn(businessDateData); + Optional newBusinessEntity = Optional + .of(BusinessDate.instance(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 13))); + Optional newCOBEntity = Optional.of(BusinessDate.instance(BusinessDateType.COB_DATE, LocalDate.of(2022, 6, 12))); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newBusinessEntity); + given(businessDateRepository.findByType(BusinessDateType.COB_DATE)).willReturn(newCOBEntity); + CommandProcessingResult result = underTest.updateBusinessDate(command); + assertNull(result.getChanges()); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.BUSINESS_DATE); + verify(businessDateRepository, times(1)).findByType(BusinessDateType.COB_DATE); + verify(businessDateRepository, times(0)).save(Mockito.any()); + } + + @Test + public void businessDateIsNotEnabledTriggeredByJob() { + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.FALSE); + assertThrows(JobExecutionException.class, () -> underTest.increaseBusinessDateByOneDay()); + } + + @Test + public void businessDateSetNewTriggeredByJob() throws JobExecutionException { + LocalDate localDate = DateUtils.getLocalDateOfTenant(); + LocalDate localDatePlus1 = localDate.plusDays(1); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.TRUE); + Optional newEntity = Optional.empty(); + given(businessDateRepository.findByType(BusinessDateType.BUSINESS_DATE)).willReturn(newEntity); + underTest.increaseBusinessDateByOneDay(); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(2)).save(businessDateArgumentCaptor.capture()); + assertEquals(localDatePlus1, businessDateArgumentCaptor.getAllValues().get(0).getDate()); + assertEquals(BusinessDateType.BUSINESS_DATE, businessDateArgumentCaptor.getAllValues().get(0).getType()); + assertEquals(localDate, businessDateArgumentCaptor.getAllValues().get(1).getDate()); + assertEquals(BusinessDateType.COB_DATE, businessDateArgumentCaptor.getAllValues().get(1).getType()); + } + + @Test + public void cobDateModifyExistingTriggeredByJob() throws JobExecutionException { + Optional newCOBEntity = Optional.of(BusinessDate.instance(BusinessDateType.COB_DATE, LocalDate.of(2022, 6, 12))); + given(businessDateRepository.findByType(BusinessDateType.COB_DATE)).willReturn(newCOBEntity); + LocalDate localDate = LocalDate.of(2022, 6, 12).plusDays(1); + given(configurationDomainService.isBusinessDateEnabled()).willReturn(Boolean.TRUE); + given(configurationDomainService.isCOBDateAdjustmentEnabled()).willReturn(Boolean.TRUE); + underTest.increaseCOBDateByOneDay(); + verify(configurationDomainService, times(1)).isBusinessDateEnabled(); + verify(configurationDomainService, times(1)).isCOBDateAdjustmentEnabled(); + verify(businessDateRepository, times(1)).save(businessDateArgumentCaptor.capture()); + assertEquals(localDate, businessDateArgumentCaptor.getValue().getDate()); + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateValidatorTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateValidatorTest.java new file mode 100644 index 000000000..206c85bdf --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validator/BusinessDateValidatorTest.java @@ -0,0 +1,120 @@ +/** + * 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.infrastructure.businessdate.validator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.time.LocalDate; +import org.apache.fineract.infrastructure.businessdate.data.BusinessDateData; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.junit.Test; + +public class BusinessDateValidatorTest { + + private BusinessDateDataParserAndValidator businessDateDataParserAndValidator = new BusinessDateDataParserAndValidator( + new FromJsonHelper()); + + @Test + public void validateAndParseUpdateWithEmptyRequest() { + String json = "{}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals( + "PlatformApiDataValidationException{errors=[The parameter `type` is mandatory., The parameter `locale` is mandatory., The parameter `dateFormat` is mandatory., The parameter `date` is mandatory.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithBlankFieldsInRequest() { + String json = "{\"type\":\"\", \"locale\":\"\",\"dateFormat\":\"\",\"date\":\"\"}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals( + "PlatformApiDataValidationException{errors=[The parameter `type` is mandatory., The parameter `locale` is mandatory., The parameter `dateFormat` is mandatory., The parameter `date` is mandatory.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithInvalidLocale() { + String json = "{\"type\":\"BUSINESS_DATE\", \"locale\":\"invalid\",\"dateFormat\":\"yyyy-MM-dd\",\"date\":\"2022-06-11\"}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals("PlatformApiDataValidationException{errors=[The parameter `locale` has an invalid language value: `invalid`.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithInvalidBusinessType() { + String json = "{\"type\":\"invalid\", \"locale\":\"hu\",\"dateFormat\":\"yyyy-MM-dd\",\"date\":\"2022-06-11\"}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals("PlatformApiDataValidationException{errors=[Failed data validation due to: Invalid Business Type value: `invalid`.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithInvalidDateFormat() { + String json = "{\"type\":\"BUSINESS_DATE\", \"locale\":\"hu\",\"dateFormat\":\"y2yyy-MM-dd\",\"date\":\"2022-06-11\"}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals( + "PlatformApiDataValidationException{errors=[The parameter `date` (value=2022-06-11) is invalid based on the dateFormat: `y2yyy-MM-dd` and locale: `hu` provided.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithInvalidDate() { + String json = "{\"type\":\"BUSINESS_DATE\", \"locale\":\"hu\",\"dateFormat\":\"yyyy-MM-dd\",\"date\":\"2y22-06-11\"}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals( + "PlatformApiDataValidationException{errors=[The parameter `date` (value=2y22-06-11) is invalid based on the dateFormat: `yyyy-MM-dd` and locale: `hu` provided.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithWrongDate() { + String json = "{\"type\":\"BUSINESS_DATE\", \"locale\":\"hu\",\"dateFormat\":\"yyyy-MM-dd\",\"date\":\"11-06-2022\"}"; + JsonCommand command = JsonCommand.from(json); + PlatformApiDataValidationException exception = assertThrows(PlatformApiDataValidationException.class, + () -> businessDateDataParserAndValidator.validateAndParseUpdate(command)); + assertEquals( + "PlatformApiDataValidationException{errors=[The parameter `date` (value=11-06-2022) is invalid based on the dateFormat: `yyyy-MM-dd` and locale: `hu` provided.]}", + exception.toString()); + } + + @Test + public void validateAndParseUpdateWithRighDate() { + String json = "{\"type\":\"COB_DATE\", \"locale\":\"hu\",\"dateFormat\":\"yyyy-MM-dd\",\"date\":\"2022-06-11\"}"; + JsonCommand command = JsonCommand.from(json); + BusinessDateData result = businessDateDataParserAndValidator.validateAndParseUpdate(command); + assertEquals("COB_DATE", result.getType()); + assertEquals("Close of Business Date", result.getDescription()); + assertEquals(LocalDate.of(2022, 6, 11), result.getDate()); + } +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java index c36a2db90..4af42f74a 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java @@ -22,14 +22,17 @@ import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import io.restassured.builder.RequestSpecBuilder; +import io.restassured.builder.ResponseSpecBuilder; import io.restassured.http.ContentType; import io.restassured.specification.RequestSpecification; +import io.restassured.specification.ResponseSpecification; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper; import org.apache.fineract.integrationtests.common.SchedulerJobHelper; import org.apache.fineract.integrationtests.common.Utils; import org.junit.jupiter.api.AfterEach; @@ -134,6 +137,8 @@ public class SchedulerJobsTest { @Test public void testTriggeringManualExecutionOfAllSchedulerJobs() { + ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE); for (String jobName : schedulerJobHelper.getAllSchedulerJobNames()) { schedulerJobHelper.executeAndAwaitJob(jobName); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java index 1499b61aa..6300c8c25 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java @@ -99,8 +99,8 @@ public class GlobalConfigurationHelper { ArrayList actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec); // There are currently 37 global configurations. - Assertions.assertEquals(38, expectedGlobalConfigurations.size()); - Assertions.assertEquals(38, actualGlobalConfigurations.size()); + Assertions.assertEquals(40, expectedGlobalConfigurations.size()); + Assertions.assertEquals(40, actualGlobalConfigurations.size()); for (int i = 0; i < expectedGlobalConfigurations.size(); i++) { @@ -440,6 +440,22 @@ public class GlobalConfigurationHelper { isPrincipalCompoundingDisabled.put("trapDoor", false); defaults.add(isPrincipalCompoundingDisabled); + HashMap isBusinessDateEnabled = new HashMap<>(); + isBusinessDateEnabled.put("id", 44); + isBusinessDateEnabled.put("name", "enable_business_date"); + isBusinessDateEnabled.put("value", 0); + isBusinessDateEnabled.put("enabled", true); + isBusinessDateEnabled.put("trapDoor", false); + defaults.add(isBusinessDateEnabled); + + HashMap isAutomaticCOBDateAdjustmentEnabled = new HashMap<>(); + isAutomaticCOBDateAdjustmentEnabled.put("id", 45); + isAutomaticCOBDateAdjustmentEnabled.put("name", "enable_automatic_cob_date_adjustment"); + isAutomaticCOBDateAdjustmentEnabled.put("value", 0); + isAutomaticCOBDateAdjustmentEnabled.put("enabled", true); + isAutomaticCOBDateAdjustmentEnabled.put("trapDoor", false); + defaults.add(isAutomaticCOBDateAdjustmentEnabled); + return defaults; } @@ -530,4 +546,9 @@ public class GlobalConfigurationHelper { return new Gson().toJson(map); } + public static Integer updateIsBusinessDateEnabled(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final boolean enabled) { + long configId = 44; + return updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, configId, enabled); + } }