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
@@ -23,9 +23,9 @@ public interface TaxApiConstants {
String nameParamName = "name";
String percentageParamName = "percentage";
String debitAccountTypeParamName = "debitAccountType";
String debitAcountIdParamName = "debitAcountId";
String debitAccountIdParamName = "debitAccountId";
String creditAccountTypeParamName = "creditAccountType";
String creditAcountIdParamName = "creditAcountId";
String creditAccountIdParamName = "creditAccountId";
String startDateParamName = "startDate";
String endDateParamName = "endDate";
@@ -0,0 +1,128 @@
/**
* 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, debitAccountId, creditAccountType, creditAccountId, 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);
}
}
@@ -0,0 +1,151 @@
/**
* 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 debitAccountType;
@Schema(example = "4")
public Long debitAccountId;
@Schema(example = "4")
public Integer creditAccountType;
@Schema(example = "4")
public Long creditAccountId;
@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;
}
}
@@ -0,0 +1,133 @@
/**
* 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);
}
}
@@ -0,0 +1,155 @@
/**
* 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;
}
}
@@ -35,6 +35,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
@@ -44,6 +45,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
@Entity
@Getter
@Table(name = "m_tax_component")
public class TaxComponent extends AbstractAuditableCustom {
@@ -58,14 +60,14 @@ public class TaxComponent extends AbstractAuditableCustom {
@ManyToOne
@JoinColumn(name = "debit_account_id")
private GLAccount debitAcount;
private GLAccount debitAccount;
@Column(name = "credit_account_type_enum")
private Integer creditAccountType;
@ManyToOne
@JoinColumn(name = "credit_account_id")
private GLAccount creditAcount;
private GLAccount creditAccount;
@Column(name = "start_date", nullable = false)
private LocalDate startDate;
@@ -81,24 +83,24 @@ public class TaxComponent extends AbstractAuditableCustom {
}
private TaxComponent(final String name, final BigDecimal percentage, final GLAccountType debitAccountType, final GLAccount debitAcount,
final GLAccountType creditAccountType, final GLAccount creditAcount, final LocalDate startDate) {
private TaxComponent(final String name, final BigDecimal percentage, final GLAccountType debitAccountType, final GLAccount debitAccount,
final GLAccountType creditAccountType, final GLAccount creditAccount, final LocalDate startDate) {
this.name = name;
this.percentage = percentage;
if (debitAccountType != null) {
this.debitAccountType = debitAccountType.getValue();
}
this.debitAcount = debitAcount;
this.debitAccount = debitAccount;
if (creditAccountType != null) {
this.creditAccountType = creditAccountType.getValue();
}
this.creditAcount = creditAcount;
this.creditAccount = creditAccount;
this.startDate = startDate;
}
public static TaxComponent createTaxComponent(final String name, final BigDecimal percentage, final GLAccountType debitAccountType,
final GLAccount debitAcount, final GLAccountType creditAccountType, final GLAccount creditAcount, final LocalDate startDate) {
return new TaxComponent(name, percentage, debitAccountType, debitAcount, creditAccountType, creditAcount, startDate);
final GLAccount debitAccount, final GLAccountType creditAccountType, final GLAccount creditAccount, final LocalDate startDate) {
return new TaxComponent(name, percentage, debitAccountType, debitAccount, creditAccountType, creditAccount, startDate);
}
public Map<String, Object> update(final JsonCommand command) {
@@ -193,7 +195,7 @@ public class TaxComponent extends AbstractAuditableCustom {
}
public GLAccount getDebitAcount() {
return this.debitAcount;
return this.debitAccount;
}
public Integer getCreditAccountType() {
@@ -201,6 +203,6 @@ public class TaxComponent extends AbstractAuditableCustom {
}
public GLAccount getCreditAcount() {
return this.creditAcount;
return this.creditAccount;
}
}
@@ -27,11 +27,13 @@ import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
@Entity
@Getter
@Table(name = "m_tax_group_mappings")
public class TaxGroupMappings extends AbstractAuditableCustom {
@@ -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.portfolio.tax.handler;
import lombok.AllArgsConstructor;
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.stereotype.Service;
@Service
@AllArgsConstructor
@CommandType(entity = "TAXCOMPONENT", action = "CREATE")
public class CreateTaxComponentCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.createTaxComponent(jsonCommand);
}
}
@@ -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.portfolio.tax.handler;
import lombok.AllArgsConstructor;
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.stereotype.Service;
@Service
@AllArgsConstructor
@CommandType(entity = "TAXGROUP", action = "CREATE")
public class CreateTaxGroupCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.createTaxGroup(jsonCommand);
}
}
@@ -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.portfolio.tax.handler;
import lombok.AllArgsConstructor;
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.stereotype.Service;
@Service
@AllArgsConstructor
@CommandType(entity = "TAXCOMPONENT", action = "UPDATE")
public class UpdateTaxComponentCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.updateTaxComponent(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.portfolio.tax.handler;
import lombok.AllArgsConstructor;
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.stereotype.Service;
@Service
@AllArgsConstructor
@CommandType(entity = "TAXGROUP", action = "UPDATE")
public class UpdateTaxGroupCommandHandler implements NewCommandSourceHandler {
private final TaxWritePlatformService taxWritePlatformService;
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.taxWritePlatformService.updateTaxGroup(jsonCommand.entityId(), jsonCommand);
}
}
@@ -0,0 +1,44 @@
/**
* 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.mapper;
import java.util.List;
import org.apache.fineract.accounting.glaccount.mapper.GlAccountMapper;
import org.apache.fineract.accounting.glaccount.mapper.GlAccountTypeMapper;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(config = MapstructMapperConfig.class, uses = { GlAccountMapper.class, GlAccountTypeMapper.class })
public interface TaxComponentMapper {
@Mapping(target = "creditAccount", source = "taxComponent.creditAccount")
@Mapping(target = "debitAccount", source = "taxComponent.debitAccount")
@Mapping(target = "creditAccountType", source = "taxComponent.creditAccountType")
@Mapping(target = "debitAccountType", source = "taxComponent.debitAccountType")
@Mapping(target = "glAccountOptions", ignore = true)
@Mapping(target = "glAccountTypeOptions", ignore = true)
@Mapping(target = "taxComponentHistories", ignore = true)
TaxComponentData map(TaxComponent taxComponent);
List<TaxComponentData> map(List<TaxComponent> taxComponents);
}
@@ -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.portfolio.tax.mapper;
import java.util.List;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(config = MapstructMapperConfig.class, uses = { TaxGroupMappingsMapper.class })
public interface TaxGroupMapper {
@Mapping(target = "taxAssociations", source = "taxGroup.taxGroupMappings")
@Mapping(target = "taxComponents", ignore = true)
TaxGroupData map(TaxGroup taxGroup);
List<TaxGroupData> map(List<TaxGroup> taxGroups);
}
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.tax.mapper;
import java.util.List;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.portfolio.tax.data.TaxGroupMappingsData;
import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
import org.mapstruct.Mapper;
@Mapper(config = MapstructMapperConfig.class, uses = { TaxComponentMapper.class })
public interface TaxGroupMappingsMapper {
TaxGroupMappingsData map(TaxGroupMappings taxGroupMapping);
List<TaxGroupMappingsData> map(List<TaxGroupMappings> taxGroupMappings);
}
@@ -0,0 +1,376 @@
/**
* 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.debitAccountIdParamName,
TaxApiConstants.creditAccountTypeParamName, TaxApiConstants.creditAccountIdParamName));
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.debitAccountIdParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountIdParamName).value(debitAccountId).longGreaterThanZero();
if (debitAccountType != null || debitAccountId != null) {
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType).notBlank();
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountIdParamName).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.creditAccountIdParamName, element);
baseDataValidator.reset().parameter(TaxApiConstants.creditAccountIdParamName).value(creditAccountId).longGreaterThanZero();
if (creditAccountType != null || creditAccountId != null) {
baseDataValidator.reset().parameter(TaxApiConstants.creditAccountIdParamName).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);
}
}
}
@@ -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.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();
}
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
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);
}