FINERACT-2425: Tax component and Tax Group refactoring

This commit is contained in:
Jose Alberto Hernandez
2026-01-03 01:56:47 +01:00
committed by Adam Saghy
parent cbf8a4f5ab
commit b7306f19d0
33 changed files with 525 additions and 295 deletions
@@ -1,128 +0,0 @@
/**
* 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.portfolio.tax.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 jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
import org.apache.fineract.portfolio.tax.request.TaxComponentRequest;
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
import org.springframework.stereotype.Component;
@Path("/v1/taxes/component")
@Component
@Tag(name = "Tax Components", description = "This defines the Tax Components")
@RequiredArgsConstructor
public class TaxComponentApiResource {
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "TAXCOMPONENT";
private final PlatformSecurityContext context;
private final TaxReadPlatformService readPlatformService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<String> toApiJsonSerializer;
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Tax Components", description = "List Tax Components")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = TaxComponentApiResourceSwagger.GetTaxesComponentsResponse.class)))) })
public List<TaxComponentData> retrieveAllTaxComponents() {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return readPlatformService.retrieveAllTaxComponents();
}
@GET
@Path("{taxComponentId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Tax Component", description = "Retrieve Tax Component")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = TaxComponentApiResourceSwagger.GetTaxesComponentsResponse.class))) })
public TaxComponentData retrieveTaxComponent(
@PathParam("taxComponentId") @Parameter(description = "taxComponentId") final Long taxComponentId) {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return readPlatformService.retrieveTaxComponentData(taxComponentId);
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public TaxComponentData retrieveTemplate() {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return readPlatformService.retrieveTaxComponentTemplate();
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a new Tax Component", description = "Creates a new Tax Component\n\n"
+ "Mandatory Fields: name, percentage\n\n"
+ "Optional Fields: debitAccountType, debitAcountId, creditAccountType, creditAcountId, startDate")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = TaxComponentApiResourceSwagger.PostTaxesComponentsRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = TaxComponentApiResourceSwagger.PostTaxesComponentsResponse.class))) })
public CommandProcessingResult createTaxComponent(@Parameter(hidden = true) TaxComponentRequest taxComponentRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createTaxComponent()
.withJson(toApiJsonSerializer.serialize(taxComponentRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
@PUT
@Path("{taxComponentId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update Tax Component", description = "Updates Tax component. Debit and credit account details cannot be modified. All the future tax components would be replaced with the new percentage.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = TaxComponentApiResourceSwagger.PutTaxesComponentsTaxComponentIdRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = TaxComponentApiResourceSwagger.PutTaxesComponentsTaxComponentIdResponse.class))) })
public CommandProcessingResult updateTaxCompoent(
@PathParam("taxComponentId") @Parameter(description = "taxComponentId") final Long taxComponentId,
@Parameter(hidden = true) TaxComponentRequest taxComponentRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateTaxComponent(taxComponentId)
.withJson(toApiJsonSerializer.serialize(taxComponentRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
}
@@ -1,147 +0,0 @@
/**
* 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.portfolio.tax.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.util.Set;
/**
* Created by Chirag Gupta on 12/20/17.
*/
final class TaxComponentApiResourceSwagger {
private TaxComponentApiResourceSwagger() {}
@Schema(description = "GetTaxesComponentsResponse")
public static final class GetTaxesComponentsResponse {
private GetTaxesComponentsResponse() {}
static final class GetTaxesComponentsCreditAccountType {
private GetTaxesComponentsCreditAccountType() {}
@Schema(example = "2")
public Integer id;
@Schema(example = "accountType.liability")
public String code;
@Schema(example = "LIABILITY")
public String description;
}
static final class GetTaxesComponentsCreditAccount {
private GetTaxesComponentsCreditAccount() {}
@Schema(example = "4")
public Long id;
@Schema(example = "ACCOUNT_NAME_7BR9C")
public String name;
@Schema(example = "LIABILITY_PA1460364665046")
public String glCode;
}
static final class GetTaxesComponentsHistories {
private GetTaxesComponentsHistories() {}
}
@Schema(example = "1")
public Long id;
@Schema(example = "tax component 1")
public String name;
@Schema(example = "10.000000")
public Float percentage;
public GetTaxesComponentsCreditAccountType creditAccountType;
public GetTaxesComponentsCreditAccount creditAccount;
@Schema(example = "[2016, 4, 11]")
public LocalDate startDate;
public Set<GetTaxesComponentsHistories> taxComponentsHistories;
}
@Schema(description = "PostTaxesComponentsRequest")
public static final class PostTaxesComponentsRequest {
private PostTaxesComponentsRequest() {}
@Schema(example = "tax component 1")
public String name;
@Schema(example = "10")
public Float percentage;
@Schema(example = "2")
public Integer creditAccountType;
@Schema(example = "4")
public Long creditAcountId;
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "11 April 2016")
public String startDate;
}
@Schema(description = "PostTaxesComponentsResponse")
public static final class PostTaxesComponentsResponse {
private PostTaxesComponentsResponse() {}
@Schema(example = "1")
public Long resourceId;
}
@Schema(description = "PutTaxesComponentsTaxComponentIdRequest")
public static final class PutTaxesComponentsTaxComponentIdRequest {
private PutTaxesComponentsTaxComponentIdRequest() {}
@Schema(example = "tax component 2")
public String name;
@Schema(example = "15")
public Float percentage;
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "15 April 2016")
public String startDate;
}
@Schema(description = "PutTaxesComponentsTaxComponentIdResponse")
public static final class PutTaxesComponentsTaxComponentIdResponse {
private PutTaxesComponentsTaxComponentIdResponse() {}
static final class PutTaxesComponentsChanges {
private PutTaxesComponentsChanges() {}
@Schema(example = "15")
public Float percentage;
@Schema(example = "tax component 2")
public String name;
@Schema(example = "[2016, 4, 15]")
public LocalDate startDate;
}
@Schema(example = "1")
public Long resourceId;
public PutTaxesComponentsChanges changes;
}
}
@@ -1,133 +0,0 @@
/**
* 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.portfolio.tax.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 jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.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.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.portfolio.tax.request.TaxGroupRequest;
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
import org.springframework.stereotype.Component;
@Path("/v1/taxes/group")
@Component
@Tag(name = "Tax Group", description = "This defines the Tax Group")
@RequiredArgsConstructor
public class TaxGroupApiResource {
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "TAXGROUP";
private final PlatformSecurityContext context;
private final TaxReadPlatformService readPlatformService;
private final DefaultToApiJsonSerializer<String> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Tax Group", description = "List Tax Group")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = TaxGroupApiResourceSwagger.GetTaxesGroupResponse.class)))) })
public List<TaxGroupData> retrieveAllTaxGroups() {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return readPlatformService.retrieveAllTaxGroups();
}
@GET
@Path("{taxGroupId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Tax Group", description = "Retrieve Tax Group")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = TaxGroupApiResourceSwagger.GetTaxesGroupResponse.class))) })
public TaxGroupData retrieveTaxGroup(@PathParam("taxGroupId") @Parameter(description = "taxGroupId") final Long taxGroupId,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return settings.isTemplate() ? readPlatformService.retrieveTaxGroupWithTemplate(taxGroupId)
: readPlatformService.retrieveTaxGroupData(taxGroupId);
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public TaxGroupData retrieveTemplate() {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return readPlatformService.retrieveTaxGroupTemplate();
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a new Tax Group", description = "Create a new Tax Group\n" + "Mandatory Fields: name and taxComponents\n"
+ "Mandatory Fields in taxComponents: taxComponentId\n" + "Optional Fields in taxComponents: id, startDate and endDate")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = TaxGroupApiResourceSwagger.PostTaxesGroupRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = TaxGroupApiResourceSwagger.PostTaxesGroupResponse.class))) })
public CommandProcessingResult createTaxGroup(@Parameter(hidden = true) TaxGroupRequest taxGroupRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createTaxGroup()
.withJson(toApiJsonSerializer.serialize(taxGroupRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
@PUT
@Path("{taxGroupId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update Tax Group", description = "Updates Tax Group. Only end date can be up-datable and can insert new tax components.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = TaxGroupApiResourceSwagger.PutTaxesGroupTaxGroupIdRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = TaxGroupApiResourceSwagger.PutTaxesGroupTaxGroupIdResponse.class))) })
public CommandProcessingResult updateTaxGroup(@PathParam("taxGroupId") @Parameter(description = "taxGroupId") final Long taxGroupId,
@Parameter(hidden = true) TaxGroupRequest taxGroupRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateTaxGroup(taxGroupId)
.withJson(toApiJsonSerializer.serialize(taxGroupRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
}
@@ -1,155 +0,0 @@
/**
* 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.portfolio.tax.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.util.List;
import java.util.Set;
/**
* Created by Chirag Gupta on 12/14/17.
*/
final class TaxGroupApiResourceSwagger {
private TaxGroupApiResourceSwagger() {}
@Schema(description = "GetTaxesGroupResponse")
public static final class GetTaxesGroupResponse {
private GetTaxesGroupResponse() {}
static final class GetTaxesGroupTaxAssociations {
private GetTaxesGroupTaxAssociations() {}
static final class GetTaxesGroupTaxComponent {
private GetTaxesGroupTaxComponent() {}
@Schema(example = "7")
public Long id;
@Schema(example = "tax component 2")
public String name;
}
@Schema(example = "7")
public Long id;
public GetTaxesGroupTaxComponent taxComponent;
@Schema(example = "[2016, 4, 11]")
public LocalDate startDate;
}
@Schema(example = "7")
public Long id;
@Schema(example = "tax group 1")
public String name;
public Set<GetTaxesGroupTaxAssociations> taxAssociations;
}
@Schema(description = "PostTaxesGroupRequest")
public static final class PostTaxesGroupRequest {
private PostTaxesGroupRequest() {}
static final class PostTaxesGroupTaxComponents {
private PostTaxesGroupTaxComponents() {}
@Schema(example = "7")
public Long taxComponentId;
@Schema(example = "11 April 2016")
public String startDate;
}
@Schema(example = "tax group 1")
public String name;
@Schema(example = "en")
public String locale;
public Set<PostTaxesGroupTaxComponents> taxComponents;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
}
@Schema(description = "PostTaxesGroupResponse")
public static final class PostTaxesGroupResponse {
private PostTaxesGroupResponse() {}
@Schema(example = "1")
public Long resourceId;
}
@Schema(description = "PutTaxesGroupTaxGroupIdRequest")
public static final class PutTaxesGroupTaxGroupIdRequest {
private PutTaxesGroupTaxGroupIdRequest() {}
static final class PutTaxesGroupTaxComponents {
private PutTaxesGroupTaxComponents() {}
@Schema(example = "7")
public Long id;
@Schema(example = "7")
public Long taxComponentId;
@Schema(example = "22 April 2016")
public String endDate;
}
@Schema(example = "tax group 2")
public String name;
@Schema(example = "en")
public String locale;
public Set<PutTaxesGroupTaxComponents> taxComponents;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
}
@Schema(description = "PutTaxesGroupTaxGroupIdResponse")
public static final class PutTaxesGroupTaxGroupIdResponse {
private PutTaxesGroupTaxGroupIdResponse() {}
static final class PutTaxesGroupChanges {
private PutTaxesGroupChanges() {}
static final class PutTaxesGroupModifiedComponents {
private PutTaxesGroupModifiedComponents() {}
@Schema(example = "Apr 22, 2016 12:00:00 AM")
public String endDate;
@Schema(example = "7")
public Long taxComponentId;
}
@Schema(example = "[6]")
public List<Long> addComponents;
public Set<PutTaxesGroupModifiedComponents> modifiedComponents;
@Schema(example = "tax group 2")
public String name;
}
@Schema(example = "7")
public Long resourceId;
public PutTaxesGroupChanges changes;
}
}
@@ -1,45 +0,0 @@
/**
* 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.portfolio.tax.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "TAXCOMPONENT", action = "CREATE")
public class CreateTaxComponentCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Autowired
public CreateTaxComponentCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
this.taxWritePlatformService = taxWritePlatformService;
}
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.createTaxComponent(jsonCommand);
}
}
@@ -1,45 +0,0 @@
/**
* 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.portfolio.tax.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "TAXGROUP", action = "CREATE")
public class CreateTaxGroupCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Autowired
public CreateTaxGroupCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
this.taxWritePlatformService = taxWritePlatformService;
}
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.createTaxGroup(jsonCommand);
}
}
@@ -1,45 +0,0 @@
/**
* 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.portfolio.tax.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "TAXCOMPONENT", action = "UPDATE")
public class UpdateTaxComponentCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Autowired
public UpdateTaxComponentCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
this.taxWritePlatformService = taxWritePlatformService;
}
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.updateTaxComponent(jsonCommand.entityId(), jsonCommand);
}
}
@@ -1,45 +0,0 @@
/**
* 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.portfolio.tax.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "TAXGROUP", action = "UPDATE")
public class UpdateTaxGroupCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Autowired
public UpdateTaxGroupCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
this.taxWritePlatformService = taxWritePlatformService;
}
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.updateTaxGroup(jsonCommand.entityId(), jsonCommand);
}
}
@@ -1,376 +0,0 @@
/**
* 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.portfolio.tax.serialization;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
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.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TaxValidator {
public static final String DATE_FORMAT = "dateFormat";
public static final String LOCALE = "locale";
public static final String TAX_COMPONENT = "tax.component";
public static final String TAX_GROUP = "tax.group";
public static final String TOTAL_PERCENTAGE = "total.percentage";
public static final String GROUP_TOTAL = "group.total.";
public static final String COMPONENT = "component";
public static final String DATES_ARE_OVERLAPPING = "dates.are.overlapping";
public static final String COMPONENT_START_DATE = "component.start.date";
private static final Set<String> SUPPORTED_TAX_COMPONENT_CREATE_PARAMETERS = new HashSet<>(
Arrays.asList(DATE_FORMAT, LOCALE, TaxApiConstants.nameParamName, TaxApiConstants.percentageParamName,
TaxApiConstants.startDateParamName, TaxApiConstants.debitAccountTypeParamName, TaxApiConstants.debitAcountIdParamName,
TaxApiConstants.creditAccountTypeParamName, TaxApiConstants.creditAcountIdParamName));
private static final Set<String> SUPPORTED_TAX_COMPONENT_UPDATE_PARAMETERS = new HashSet<>(Arrays.asList(DATE_FORMAT, LOCALE,
TaxApiConstants.nameParamName, TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName));
private static final Set<String> SUPPORTED_TAX_GROUP_PARAMETERS = new HashSet<>(
Arrays.asList(DATE_FORMAT, LOCALE, TaxApiConstants.nameParamName, TaxApiConstants.taxComponentsParamName));
private static final Set<String> SUPPORTED_TAX_GROUP_TAX_COMPONENTS_CREATE_PARAMETERS = new HashSet<>(
Arrays.asList(TaxApiConstants.taxComponentIdParamName, TaxApiConstants.startDateParamName));
private static final Set<String> SUPPORTED_TAX_GROUP_TAX_COMPONENTS_UPDATE_PARAMETERS = new HashSet<>(
Arrays.asList(TaxApiConstants.idParamName, TaxApiConstants.taxComponentIdParamName, TaxApiConstants.startDateParamName,
TaxApiConstants.endDateParamName));
public static final String DOT = ".";
public static final String AT_INDEX = ".at.index.";
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public TaxValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForTaxComponentCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_TAX_COMPONENT_CREATE_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_COMPONENT);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
final BigDecimal percentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank().positiveAmount()
.notGreaterThanMax(BigDecimal.valueOf(100));
final Integer debitAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName,
element);
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType).ignoreIfNull()
.isOneOfTheseValues(GLAccountType.ASSET.getValue(), GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(),
GLAccountType.INCOME.getValue(), GLAccountType.EXPENSE.getValue());
final Long debitAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId).longGreaterThanZero();
if (debitAccountType != null || debitAccountId != null) {
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType).notBlank();
baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId).notBlank();
}
final Integer creditAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.creditAccountTypeParamName,
element);
baseDataValidator.reset().parameter(TaxApiConstants.creditAccountTypeParamName).value(creditAccountType).ignoreIfNull()
.isOneOfTheseValues(GLAccountType.ASSET.getValue(), GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(),
GLAccountType.INCOME.getValue(), GLAccountType.EXPENSE.getValue());
final Long creditAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAcountIdParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId).longGreaterThanZero();
if (creditAccountType != null || creditAccountId != null) {
baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId).notBlank();
baseDataValidator.reset().parameter(TaxApiConstants.creditAccountTypeParamName).value(creditAccountType).notBlank();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForTaxComponentUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_TAX_COMPONENT_UPDATE_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_COMPONENT);
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.nameParamName, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.percentageParamName, element)) {
final BigDecimal percentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName,
element);
baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank().positiveAmount()
.notGreaterThanMax(BigDecimal.valueOf(100));
}
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.startDateParamName, element)) {
final LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.startDateParamName).value(startDate)
.validateDateAfter(DateUtils.getBusinessLocalDate());
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForTaxGroupCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_TAX_GROUP_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_GROUP);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
final JsonArray taxComponents = this.fromApiJsonHelper.extractJsonArrayNamed(TaxApiConstants.taxComponentsParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(taxComponents).notBlank();
final JsonObject topLevelJsonElement = element.getAsJsonObject();
if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).getAsJsonArray();
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(array.size()).integerGreaterThanZero();
for (int i = 1; i <= array.size(); i++) {
final JsonObject taxComponent = array.get(i - 1).getAsJsonObject();
final String arrayObjectJson = this.fromApiJsonHelper.toJson(taxComponent);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, arrayObjectJson,
SUPPORTED_TAX_GROUP_TAX_COMPONENTS_CREATE_PARAMETERS);
final Long taxComponentId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent);
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName)
.parameterAtIndexArray(TaxApiConstants.taxComponentIdParamName, i).value(taxComponentId).notNull()
.longGreaterThanZero();
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForTaxGroupUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_TAX_GROUP_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_GROUP);
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.nameParamName, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.taxComponentsParamName, element)) {
final JsonArray taxComponents = this.fromApiJsonHelper.extractJsonArrayNamed(TaxApiConstants.taxComponentsParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(taxComponents).notBlank();
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).getAsJsonArray();
for (int i = 1; i <= array.size(); i++) {
final JsonObject taxComponent = array.get(i - 1).getAsJsonObject();
final String arrayObjectJson = this.fromApiJsonHelper.toJson(taxComponent);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, arrayObjectJson,
SUPPORTED_TAX_GROUP_TAX_COMPONENTS_UPDATE_PARAMETERS);
final Long taxComponentId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName,
taxComponent);
final Long taxMappingId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName,
taxComponent);
if (taxMappingId == null) {
baseDataValidator.reset().parameter(
TaxApiConstants.taxComponentsParamName + DOT + TaxApiConstants.taxComponentIdParamName + AT_INDEX + i)
.value(taxComponentId).notNull().longGreaterThanZero();
} else {
baseDataValidator.reset().parameter(
TaxApiConstants.taxComponentsParamName + DOT + TaxApiConstants.taxComponentIdParamName + AT_INDEX + i)
.value(taxComponentId).longGreaterThanZero();
baseDataValidator.reset()
.parameter(TaxApiConstants.taxComponentsParamName + DOT + TaxApiConstants.idParamName + AT_INDEX + i)
.value(taxMappingId).longGreaterThanZero();
}
final LocalDate endDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.endDateParamName, taxComponent,
dateFormat, locale);
baseDataValidator.reset()
.parameter(TaxApiConstants.taxComponentsParamName + DOT + TaxApiConstants.endDateParamName + AT_INDEX + i)
.value(endDate).ignoreIfNull().validateDateAfter(DateUtils.getBusinessLocalDate());
final LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName,
taxComponent, dateFormat, locale);
if (endDate != null && startDate != null) {
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName + AT_INDEX + i)
.failWithCode("start.date.end.date.both.should.not.be.present", startDate, endDate);
}
}
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateTaxGroupEndDateAndTaxComponent(final TaxGroup taxGroup, final Set<TaxGroupMappings> groupMappings) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_GROUP);
for (TaxGroupMappings mapping : groupMappings) {
if (mapping.getId() != null) {
TaxGroupMappings existing = taxGroup.findOneBy(mapping);
if (existing.endDate() != null && mapping.endDate() != null && !DateUtils.isEqual(existing.endDate(), mapping.endDate())) {
baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName)
.failWithCode("can.not.modify.end.date.once.updated");
} else {
baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName).value(mapping.endDate()).ignoreIfNull()
.validateDateAfter(existing.startDate());
}
if (mapping.getTaxComponent() != null && !existing.getTaxComponent().getId().equals(mapping.getTaxComponent().getId())) {
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentIdParamName).failWithCode("update.not.supported");
}
} else if (mapping.endDate() != null) {
baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName).failWithCode("not.supported.for.new.association");
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateTaxGroup(final TaxGroup taxGroup) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_GROUP);
Set<TaxGroupMappings> groupMappings = taxGroup.getTaxGroupMappings();
validateGroupTotal(groupMappings, baseDataValidator, TOTAL_PERCENTAGE);
validateOverlappingComponents(groupMappings, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateTaxComponentForUpdate(final TaxComponent taxComponent) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_COMPONENT);
validateGroupTotal(taxComponent.getTaxGroupMappings(), baseDataValidator, GROUP_TOTAL + TaxApiConstants.percentageParamName);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateStartDate(final LocalDate existingStartDate, final JsonCommand command) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(TAX_COMPONENT);
validateStartDate(existingStartDate, command.localDateValueOfParameterNamed(TaxApiConstants.startDateParamName), baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateStartDate(final LocalDate existingStartDate, final LocalDate startDate,
final DataValidatorBuilder baseDataValidator) {
baseDataValidator.reset().parameter(TaxApiConstants.startDateParamName).value(startDate).validateDateAfter(existingStartDate);
}
private void validateOverlappingComponents(final Set<TaxGroupMappings> taxMappings, final DataValidatorBuilder baseDataValidator) {
for (TaxGroupMappings groupMappingsOne : taxMappings) {
final List<TaxGroupMappings> mappings = new ArrayList<>(taxMappings);
mappings.remove(groupMappingsOne);
for (TaxGroupMappings groupMappings : mappings) {
if (groupMappingsOne.getTaxComponent().equals(groupMappings.getTaxComponent())) {
if (groupMappingsOne.endDate() == null && groupMappings.endDate() == null) {
baseDataValidator.reset().parameter(COMPONENT).failWithCode(DATES_ARE_OVERLAPPING);
} else if (DateUtils.isAfter(groupMappingsOne.startDate(), groupMappings.startDate())) {
baseDataValidator.reset().parameter(COMPONENT_START_DATE).value(groupMappingsOne.startDate())
.validateDateAfter(groupMappings.endDate());
} else {
baseDataValidator.reset().parameter(COMPONENT_START_DATE).value(groupMappings.startDate())
.validateDateAfter(groupMappingsOne.endDate());
}
}
}
}
}
private void validateGroupTotal(final Set<TaxGroupMappings> taxMappings, final DataValidatorBuilder baseDataValidator,
final String paramenter) {
for (TaxGroupMappings groupMappingsOne : taxMappings) {
Collection<LocalDate> dates = groupMappingsOne.getTaxComponent().allStartDates();
for (LocalDate date : dates) {
LocalDate applicableDate = date.plusDays(1);
BigDecimal total = BigDecimal.ZERO;
for (TaxGroupMappings groupMappings : taxMappings) {
if (groupMappings.occursOnDayFromAndUpToAndIncluding(applicableDate)) {
BigDecimal applicablePercentage = groupMappings.getTaxComponent().getApplicablePercentage(applicableDate);
if (applicablePercentage != null) {
total = total.add(applicablePercentage);
}
}
}
baseDataValidator.reset().parameter(paramenter).value(total).notGreaterThanMax(BigDecimal.valueOf(100));
}
}
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -61,7 +61,7 @@ public class TaxAssembler {
final BigDecimal percentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName, element);
final Integer debitAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName,
element);
final Long debitAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, element);
final Long debitAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAccountIdParamName, element);
GLAccountType debitGlAccountType = null;
if (debitAccountType != null) {
debitGlAccountType = GLAccountType.fromInt(debitAccountType);
@@ -70,7 +70,7 @@ public class TaxAssembler {
if (debitAccountId != null) {
debitGlAccount = this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(debitAccountId);
if (!debitGlAccount.getType().equals(debitAccountType) || debitGlAccount.isHeaderAccount()) {
baseDataValidator.parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId)
baseDataValidator.parameter(TaxApiConstants.debitAccountIdParamName).value(debitAccountId)
.failWithCode("not.a.valid.account");
}
}
@@ -81,12 +81,12 @@ public class TaxAssembler {
if (creditAccountType != null) {
creditGlAccountType = GLAccountType.fromInt(creditAccountType);
}
final Long creditAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAcountIdParamName, element);
final Long creditAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAccountIdParamName, element);
GLAccount creditGlAccount = null;
if (creditAccountId != null) {
creditGlAccount = this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(creditAccountId);
if (!creditGlAccount.getType().equals(creditAccountType) || creditGlAccount.isHeaderAccount()) {
baseDataValidator.parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId)
baseDataValidator.parameter(TaxApiConstants.creditAccountIdParamName).value(creditAccountId)
.failWithCode("not.a.valid.account");
}
}
@@ -1,43 +0,0 @@
/**
* 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.portfolio.tax.service;
import java.util.List;
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
public interface TaxReadPlatformService {
TaxComponentData retrieveTaxComponentData(Long id);
TaxComponentData retrieveTaxComponentTemplate();
TaxGroupData retrieveTaxGroupData(Long id);
TaxGroupData retrieveTaxGroupWithTemplate(Long id);
TaxGroupData retrieveTaxGroupTemplate();
List<TaxComponentData> retrieveAllTaxComponents();
List<TaxGroupData> retrieveAllTaxGroups();
List<TaxGroupData> retrieveTaxGroupsForLookUp();
}
@@ -18,47 +18,34 @@
*/
package org.apache.fineract.portfolio.tax.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
import org.apache.fineract.accounting.common.AccountingEnumerations;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
import org.apache.fineract.portfolio.tax.data.TaxComponentHistoryData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.portfolio.tax.data.TaxGroupMappingsData;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.apache.fineract.portfolio.tax.domain.TaxComponentRepository;
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepository;
import org.apache.fineract.portfolio.tax.mapper.TaxComponentMapper;
import org.apache.fineract.portfolio.tax.mapper.TaxGroupMapper;
@RequiredArgsConstructor
public class TaxReadPlatformServiceImpl implements TaxReadPlatformService {
private static final TaxComponentMapper TAX_COMPONENT_MAPPER = new TaxComponentMapper();
private static final TaxGroupMapper TAX_GROUP_MAPPER = new TaxGroupMapper();
private static final TaxComponentLookUpMapper TAX_COMPONENT_LOOK_UP_MAPPER = new TaxComponentLookUpMapper();
private static final TaxGroupLookUpMapper TAX_GROUP_LOOK_UP_MAPPER = new TaxGroupLookUpMapper();
private final JdbcTemplate jdbcTemplate;
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
private final TaxComponentRepository taxComponentRepository;
private final TaxComponentMapper taxComponentMapper;
private final TaxGroupRepository taxGroupRepository;
private final TaxGroupMapper taxGroupMapper;
@Override
public List<TaxComponentData> retrieveAllTaxComponents() {
String sql = "select " + TAX_COMPONENT_MAPPER.getSchema();
return this.jdbcTemplate.query(sql, TAX_COMPONENT_MAPPER); // NOSONAR
return taxComponentMapper.map(taxComponentRepository.findAll());
}
@Override
public TaxComponentData retrieveTaxComponentData(final Long id) {
String sql = "select " + TAX_COMPONENT_MAPPER.getSchema() + " where tc.id=?";
return this.jdbcTemplate.queryForObject(sql, TAX_COMPONENT_MAPPER, id); // NOSONAR
return taxComponentMapper.map(taxComponentRepository.getReferenceById(id));
}
@Override
@@ -69,14 +56,12 @@ public class TaxReadPlatformServiceImpl implements TaxReadPlatformService {
@Override
public List<TaxGroupData> retrieveAllTaxGroups() {
String sql = "select " + TAX_GROUP_MAPPER.getSchema();
return this.jdbcTemplate.query(sql, TAX_GROUP_MAPPER); // NOSONAR
return taxGroupMapper.map(taxGroupRepository.findAll());
}
@Override
public TaxGroupData retrieveTaxGroupData(final Long id) {
String sql = "select " + TAX_GROUP_MAPPER.getSchema() + " where tg.id=?";
return this.jdbcTemplate.queryForObject(sql, TAX_GROUP_MAPPER, id); // NOSONAR
return taxGroupMapper.map(taxGroupRepository.getReferenceById(id));
}
@Override
@@ -92,205 +77,12 @@ public class TaxReadPlatformServiceImpl implements TaxReadPlatformService {
}
private Collection<TaxComponentData> retrieveTaxComponentsForLookUp() {
String sql = "select " + TAX_COMPONENT_LOOK_UP_MAPPER.getSchema();
return this.jdbcTemplate.query(sql, TAX_COMPONENT_LOOK_UP_MAPPER); // NOSONAR
return taxComponentMapper.map(taxComponentRepository.findAll());
}
@Override
public List<TaxGroupData> retrieveTaxGroupsForLookUp() {
String sql = "select " + TAX_GROUP_LOOK_UP_MAPPER.getSchema();
return this.jdbcTemplate.query(sql, TAX_GROUP_LOOK_UP_MAPPER); // NOSONAR
}
private static final class TaxComponentMapper implements RowMapper<TaxComponentData> {
private final String schema;
private final TaxComponentHistoryDataMapper componentHistoryDataMapper = new TaxComponentHistoryDataMapper();
TaxComponentMapper() {
StringBuilder sb = new StringBuilder();
sb.append("tc.id as id, tc.name as name,");
sb.append("tc.percentage as percentage, tc.start_date as startDate,");
sb.append("tc.debit_account_type_enum as debitAccountTypeEnum,");
sb.append("dgl.id as debitAccountId, dgl.name as debitAccountName, dgl.gl_code as debitAccountGlCode,");
sb.append("tc.credit_account_type_enum as creditAccountTypeEnum,");
sb.append("cgl.id as creditAccountId, cgl.name as creditAccountName, cgl.gl_code as creditAccountGlCode,");
sb.append("history.percentage as historyPercentage, history.start_date as historyStartDate,");
sb.append("history.end_date as historyEndDate");
sb.append(" from m_tax_component tc ");
sb.append(" left join acc_gl_account dgl on dgl.id = tc.debit_account_id");
sb.append(" left join acc_gl_account cgl on cgl.id = tc.credit_account_id");
sb.append(" left join m_tax_component_history history on history.tax_component_id = tc.id");
this.schema = sb.toString();
}
@Override
public TaxComponentData mapRow(ResultSet rs, int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final BigDecimal percentage = rs.getBigDecimal("percentage");
final Integer debitAccountTypeEnum = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "debitAccountTypeEnum");
EnumOptionData debitAccountType = null;
if (debitAccountTypeEnum != null) {
debitAccountType = AccountingEnumerations.gLAccountType(debitAccountTypeEnum);
}
GLAccountData debitAccountData = null;
if (debitAccountTypeEnum != null && debitAccountTypeEnum > 0) {
final Long debitAccountId = rs.getLong("debitAccountId");
final String debitAccountName = rs.getString("debitAccountName");
final String debitAccountGlCode = rs.getString("debitAccountGlCode");
debitAccountData = new GLAccountData().setId(debitAccountId).setName(debitAccountName).setGlCode(debitAccountGlCode);
}
final Integer creditAccountTypeEnum = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "creditAccountTypeEnum");
EnumOptionData creditAccountType = null;
if (creditAccountTypeEnum != null) {
creditAccountType = AccountingEnumerations.gLAccountType(creditAccountTypeEnum);
}
GLAccountData creditAccountData = null;
if (creditAccountTypeEnum != null && creditAccountTypeEnum > 0) {
final Long creditAccountId = rs.getLong("creditAccountId");
final String creditAccountName = rs.getString("creditAccountName");
final String creditAccountGlCode = rs.getString("creditAccountGlCode");
creditAccountData = new GLAccountData().setId(creditAccountId).setName(creditAccountName).setGlCode(creditAccountGlCode);
}
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
Collection<TaxComponentHistoryData> historyDatas = new ArrayList<>();
historyDatas.add(componentHistoryDataMapper.mapRow(rs, rowNum));
while (rs.next()) {
if (id.equals(rs.getLong("id"))) {
historyDatas.add(componentHistoryDataMapper.mapRow(rs, rowNum));
} else {
rs.previous();
break;
}
}
return TaxComponentData.instance(id, name, percentage, debitAccountType, debitAccountData, creditAccountType, creditAccountData,
startDate, historyDatas);
}
public String getSchema() {
return this.schema;
}
}
private static final class TaxComponentHistoryDataMapper implements RowMapper<TaxComponentHistoryData> {
@Override
public TaxComponentHistoryData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final BigDecimal percentage = rs.getBigDecimal("historyPercentage");
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "historyStartDate");
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "historyEndDate");
return new TaxComponentHistoryData(percentage, startDate, endDate);
}
}
private static final class TaxGroupMapper implements RowMapper<TaxGroupData> {
private final String schema;
private final TaxGroupMappingsDataMapper taxGroupMappingsDataMapper = new TaxGroupMappingsDataMapper();
TaxGroupMapper() {
StringBuilder sb = new StringBuilder();
sb.append("tg.id as id, tg.name as name,");
sb.append("tgm.id as mappingId,");
sb.append("tc.id as taxComponentId, tc.name as taxComponentName,");
sb.append("tgm.start_date as startDate, tgm.end_date as endDate ");
sb.append(" from m_tax_group tg ");
sb.append(" inner join m_tax_group_mappings tgm on tgm.tax_group_id = tg.id ");
sb.append(" inner join m_tax_component tc on tc.id = tgm.tax_component_id ");
this.schema = sb.toString();
}
@Override
public TaxGroupData mapRow(ResultSet rs, int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final Collection<TaxGroupMappingsData> taxAssociations = new ArrayList<>();
taxAssociations.add(this.taxGroupMappingsDataMapper.mapRow(rs, rowNum));
while (rs.next()) {
if (id.equals(rs.getLong("id"))) {
taxAssociations.add(this.taxGroupMappingsDataMapper.mapRow(rs, rowNum));
} else {
rs.previous();
break;
}
}
return new TaxGroupData(id, name, taxAssociations, null);
}
public String getSchema() {
return this.schema;
}
}
private static final class TaxGroupMappingsDataMapper implements RowMapper<TaxGroupMappingsData> {
@Override
public TaxGroupMappingsData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final Long mappingId = rs.getLong("mappingId");
final Long id = rs.getLong("taxComponentId");
final String name = rs.getString("taxComponentName");
TaxComponentData componentData = TaxComponentData.lookup(id, name);
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate");
return new TaxGroupMappingsData(mappingId, componentData, startDate, endDate);
}
}
private static final class TaxComponentLookUpMapper implements RowMapper<TaxComponentData> {
private final String schema;
TaxComponentLookUpMapper() {
StringBuilder sb = new StringBuilder();
sb.append("tc.id as id, tc.name as name ");
sb.append(" from m_tax_component tc ");
this.schema = sb.toString();
}
public String getSchema() {
return this.schema;
}
@Override
public TaxComponentData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
return TaxComponentData.lookup(id, name);
}
}
private static final class TaxGroupLookUpMapper implements RowMapper<TaxGroupData> {
private final String schema;
TaxGroupLookUpMapper() {
StringBuilder sb = new StringBuilder();
sb.append("tg.id as id, tg.name as name ");
sb.append(" from m_tax_group tg ");
this.schema = sb.toString();
}
public String getSchema() {
return this.schema;
}
@Override
public TaxGroupData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
return TaxGroupData.lookup(id, name);
}
return taxGroupMapper.map(taxGroupRepository.findAll());
}
}
@@ -1,34 +0,0 @@
/**
* 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.portfolio.tax.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface TaxWritePlatformService {
CommandProcessingResult createTaxComponent(JsonCommand command);
CommandProcessingResult updateTaxComponent(Long id, JsonCommand command);
CommandProcessingResult createTaxGroup(JsonCommand command);
CommandProcessingResult updateTaxGroup(Long id, JsonCommand command);
}
@@ -25,6 +25,8 @@ import org.apache.fineract.portfolio.tax.domain.TaxComponentRepository;
import org.apache.fineract.portfolio.tax.domain.TaxComponentRepositoryWrapper;
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepository;
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper;
import org.apache.fineract.portfolio.tax.mapper.TaxComponentMapper;
import org.apache.fineract.portfolio.tax.mapper.TaxGroupMapper;
import org.apache.fineract.portfolio.tax.serialization.TaxValidator;
import org.apache.fineract.portfolio.tax.service.TaxAssembler;
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
@@ -34,7 +36,6 @@ import org.apache.fineract.portfolio.tax.service.TaxWritePlatformServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class TaxConfiguration {
@@ -48,9 +49,11 @@ public class TaxConfiguration {
@Bean
@ConditionalOnMissingBean(TaxReadPlatformService.class)
public TaxReadPlatformService taxReadPlatformService(JdbcTemplate jdbcTemplate,
public TaxReadPlatformService taxReadPlatformService(final TaxComponentRepository taxComponentRepository,
final TaxComponentMapper taxComponentMapper, final TaxGroupRepository taxGroupRepository, final TaxGroupMapper taxGroupMapper,
AccountingDropdownReadPlatformService accountingDropdownReadPlatformService) {
return new TaxReadPlatformServiceImpl(jdbcTemplate, accountingDropdownReadPlatformService);
return new TaxReadPlatformServiceImpl(accountingDropdownReadPlatformService, taxComponentRepository, taxComponentMapper,
taxGroupRepository, taxGroupMapper);
}
@Bean
@@ -44729,7 +44729,7 @@ No Request Body:
</td>
</tr>
<tr class=alt>
<td>debitAcountId</td>
<td>debitAccountId</td>
</tr>
<tr>
<td class=fielddesc>
@@ -44742,7 +44742,7 @@ No Request Body:
</td>
</tr>
<tr class=alt>
<td>debitAcountId</td>
<td>debitAccountId</td>
</tr>
<tr>
<td class=fielddesc>
@@ -44786,13 +44786,13 @@ No Request Body:
<td>debitAccountType</td>
</tr>
<tr class=alt>
<td>debitAcountId</td>
<td>debitAccountId</td>
</tr>
<tr class=alt>
<td>creditAccountType</td>
</tr>
<tr class=alt>
<td>creditAcountId</td>
<td>creditAccountId</td>
</tr>
<tr class=alt>
<td>startDate</td>
@@ -44811,7 +44811,7 @@ No Request Body:
"name": "tax component 1",
"percentage": "10",
"creditAccountType": 2,
"creditAcountId": 4,
"creditAccountId": 4,
"locale": "en",
"dateFormat": "dd MMMM yyyy",
"startDate": "11 April 2016"