FINERACT-1932: Fineract modularization - fineract-tax

This commit is contained in:
Jose Alberto Hernandez
2024-06-17 23:06:07 +02:00
committed by Adam Saghy
parent 35ff4363a8
commit 0292171f6f
28 changed files with 289 additions and 20 deletions
@@ -0,0 +1,38 @@
/**
* 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;
public interface TaxApiConstants {
String nameParamName = "name";
String percentageParamName = "percentage";
String debitAccountTypeParamName = "debitAccountType";
String debitAcountIdParamName = "debitAcountId";
String creditAccountTypeParamName = "creditAccountType";
String creditAcountIdParamName = "creditAcountId";
String startDateParamName = "startDate";
String endDateParamName = "endDate";
String chargeIncludesTaxParamName = "chargeIncludesTax";
String taxComponentsParamName = "taxComponents";
String idParamName = "id";
String taxComponentIdParamName = "taxComponentId";
}
@@ -0,0 +1,206 @@
/**
* 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.domain;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
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
@Table(name = "m_tax_component")
public class TaxComponent extends AbstractAuditableCustom {
@Column(name = "name", length = 100)
private String name;
@Column(name = "percentage", scale = 6, precision = 19, nullable = false)
private BigDecimal percentage;
@Column(name = "debit_account_type_enum")
private Integer debitAccountType;
@ManyToOne
@JoinColumn(name = "debit_account_id")
private GLAccount debitAcount;
@Column(name = "credit_account_type_enum")
private Integer creditAccountType;
@ManyToOne
@JoinColumn(name = "credit_account_id")
private GLAccount creditAcount;
@Column(name = "start_date", nullable = false)
private LocalDate startDate;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JoinColumn(name = "tax_component_id", referencedColumnName = "id", nullable = false)
private Set<TaxComponentHistory> taxComponentHistories = new HashSet<>();
@OneToMany(cascade = CascadeType.DETACH, mappedBy = "taxComponent", orphanRemoval = false, fetch = FetchType.EAGER)
private Set<TaxGroupMappings> taxGroupMappings = new HashSet<>();
protected TaxComponent() {
}
private TaxComponent(final String name, final BigDecimal percentage, final GLAccountType debitAccountType, final GLAccount debitAcount,
final GLAccountType creditAccountType, final GLAccount creditAcount, final LocalDate startDate) {
this.name = name;
this.percentage = percentage;
if (debitAccountType != null) {
this.debitAccountType = debitAccountType.getValue();
}
this.debitAcount = debitAcount;
if (creditAccountType != null) {
this.creditAccountType = creditAccountType.getValue();
}
this.creditAcount = creditAcount;
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);
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> changes = new HashMap<>();
if (command.isChangeInStringParameterNamed(TaxApiConstants.nameParamName, this.name)) {
final String newValue = command.stringValueOfParameterNamed(TaxApiConstants.nameParamName);
changes.put(TaxApiConstants.nameParamName, newValue);
this.name = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInBigDecimalParameterNamed(TaxApiConstants.percentageParamName, this.percentage)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(TaxApiConstants.percentageParamName);
changes.put(TaxApiConstants.percentageParamName, newValue);
LocalDate oldStartDate = this.startDate;
updateStartDate(command, changes, true);
LocalDate newStartDate = this.startDate;
TaxComponentHistory history = TaxComponentHistory.createTaxComponentHistory(this.percentage, oldStartDate, newStartDate);
this.taxComponentHistories.add(history);
this.percentage = newValue;
}
return changes;
}
private void updateStartDate(final JsonCommand command, final Map<String, Object> changes, boolean setAsCurrentDate) {
LocalDate startDate = DateUtils.getBusinessLocalDate();
if (command.parameterExists(TaxApiConstants.startDateParamName)) {
LocalDate startDateFromUI = command.localDateValueOfParameterNamed(TaxApiConstants.startDateParamName);
if (startDateFromUI != null) {
startDate = startDateFromUI;
}
this.startDate = startDate;
changes.put(TaxApiConstants.startDateParamName, startDate);
} else if (setAsCurrentDate) {
changes.put(TaxApiConstants.startDateParamName, startDate);
this.startDate = startDate;
}
}
public BigDecimal getPercentage() {
return this.percentage;
}
public LocalDate startDate() {
return this.startDate;
}
public BigDecimal getApplicablePercentage(final LocalDate date) {
BigDecimal percentage = null;
if (occursOnDayFrom(date)) {
percentage = getPercentage();
} else {
for (TaxComponentHistory componentHistory : taxComponentHistories) {
if (componentHistory.occursOnDayFromAndUpToAndIncluding(date)) {
percentage = componentHistory.getPercentage();
break;
}
}
}
return percentage;
}
private boolean occursOnDayFrom(final LocalDate target) {
return DateUtils.isAfter(target, startDate());
}
public Set<TaxComponentHistory> getTaxComponentHistories() {
return this.taxComponentHistories;
}
public Set<TaxGroupMappings> getTaxGroupMappings() {
return this.taxGroupMappings;
}
public Collection<LocalDate> allStartDates() {
List<LocalDate> dates = new ArrayList<>();
dates.add(startDate());
for (TaxComponentHistory componentHistory : taxComponentHistories) {
dates.add(componentHistory.startDate());
}
return dates;
}
public Integer getDebitAccountType() {
return this.debitAccountType;
}
public GLAccount getDebitAcount() {
return this.debitAcount;
}
public Integer getCreditAccountType() {
return this.creditAccountType;
}
public GLAccount getCreditAcount() {
return this.creditAcount;
}
}
@@ -0,0 +1,73 @@
/**
* 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.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@Entity
@Table(name = "m_tax_component_history")
public class TaxComponentHistory extends AbstractAuditableCustom {
@Column(name = "percentage", scale = 6, precision = 19, nullable = false)
private BigDecimal percentage;
@Column(name = "start_date", nullable = false)
private LocalDate startDate;
@Column(name = "end_date", nullable = false)
private LocalDate endDate;
protected TaxComponentHistory() {
}
private TaxComponentHistory(final BigDecimal percentage, final LocalDate startDate, final LocalDate endDate) {
this.percentage = percentage;
this.startDate = startDate;
this.endDate = endDate;
}
public static TaxComponentHistory createTaxComponentHistory(final BigDecimal percentage, final LocalDate startDate,
final LocalDate endDate) {
return new TaxComponentHistory(percentage, startDate, endDate);
}
public LocalDate startDate() {
return this.startDate;
}
public LocalDate endDate() {
return this.endDate;
}
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
return DateUtils.isAfter(target, startDate()) && (endDate == null || !DateUtils.isAfter(target, endDate()));
}
public BigDecimal getPercentage() {
return this.percentage;
}
}
@@ -0,0 +1,26 @@
/**
* 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.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface TaxComponentRepository extends JpaRepository<TaxComponent, Long>, JpaSpecificationExecutor<TaxComponent> {
}
@@ -0,0 +1,39 @@
/**
* 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.domain;
import org.apache.fineract.portfolio.tax.exception.TaxComponentNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TaxComponentRepositoryWrapper {
private final TaxComponentRepository repository;
@Autowired
public TaxComponentRepositoryWrapper(final TaxComponentRepository repository) {
this.repository = repository;
}
public TaxComponent findOneWithNotFoundDetection(final Long id) {
return this.repository.findById(id).orElseThrow(() -> new TaxComponentNotFoundException(id));
}
}
@@ -0,0 +1,115 @@
/**
* 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.domain;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
import org.apache.fineract.portfolio.tax.exception.TaxMappingNotFoundException;
@Entity
@Table(name = "m_tax_group")
public class TaxGroup extends AbstractAuditableCustom {
@Column(name = "name", length = 100)
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "taxGroup")
private Set<TaxGroupMappings> taxGroupMappings = new HashSet<>();
protected TaxGroup() {
}
private TaxGroup(final String name, final Set<TaxGroupMappings> taxGroupMappings) {
this.name = name;
this.taxGroupMappings = taxGroupMappings;
taxGroupMappings.forEach(m -> m.setTaxGroup(this));
}
public static TaxGroup createTaxGroup(final String name, final Set<TaxGroupMappings> taxGroupMappings) {
return new TaxGroup(name, taxGroupMappings);
}
public Map<String, Object> update(final JsonCommand command, final Set<TaxGroupMappings> taxGroupMappings) {
final Map<String, Object> changes = new HashMap<>();
if (command.isChangeInStringParameterNamed(TaxApiConstants.nameParamName, this.name)) {
final String newValue = command.stringValueOfParameterNamed(TaxApiConstants.nameParamName);
changes.put(TaxApiConstants.nameParamName, newValue);
this.name = StringUtils.defaultIfEmpty(newValue, null);
}
List<Long> taxComponentList = new ArrayList<>();
final List<Map<String, Object>> modifications = new ArrayList<>();
for (TaxGroupMappings groupMappings : taxGroupMappings) {
TaxGroupMappings mappings = findOneBy(groupMappings);
if (mappings == null) {
this.taxGroupMappings.add(groupMappings);
taxComponentList.add(groupMappings.getTaxComponent().getId());
} else {
mappings.update(groupMappings.getEndDate(), modifications);
}
}
if (!taxComponentList.isEmpty()) {
changes.put("addComponents", taxComponentList);
}
if (!modifications.isEmpty()) {
changes.put("modifiedComponents", modifications);
}
return changes;
}
public TaxGroupMappings findOneBy(final TaxGroupMappings groupMapping) {
if (groupMapping.getId() != null) {
for (TaxGroupMappings groupMappings : this.taxGroupMappings) {
if (groupMappings.getId().equals(groupMapping.getId())) {
return groupMappings;
}
}
throw new TaxMappingNotFoundException(groupMapping.getId());
}
return null;
}
public Set<TaxGroupMappings> getTaxGroupMappings() {
return this.taxGroupMappings;
}
public String getName() {
return this.name;
}
}
@@ -0,0 +1,111 @@
/**
* 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.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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
@Table(name = "m_tax_group_mappings")
public class TaxGroupMappings extends AbstractAuditableCustom {
@ManyToOne
@JoinColumn(name = "tax_group_id", nullable = false)
private TaxGroup taxGroup;
@ManyToOne
@JoinColumn(name = "tax_component_id", nullable = false)
private TaxComponent taxComponent;
@Column(name = "start_date", nullable = false)
private LocalDate startDate;
@Column(name = "end_date", nullable = true)
private LocalDate endDate;
protected TaxGroupMappings() {}
private TaxGroupMappings(final TaxComponent taxComponent, final LocalDate startDate, final LocalDate endDate) {
this.taxComponent = taxComponent;
this.startDate = startDate;
this.endDate = endDate;
}
public static TaxGroupMappings createTaxGroupMappings(final TaxComponent taxComponent, final LocalDate startDate) {
final LocalDate endDate = null;
return new TaxGroupMappings(taxComponent, startDate, endDate);
}
public static TaxGroupMappings createTaxGroupMappings(final Long id, final TaxComponent taxComponent, final LocalDate endDate) {
final LocalDate startDate = null;
TaxGroupMappings groupMappings = new TaxGroupMappings(taxComponent, startDate, endDate);
groupMappings.setId(id);
return groupMappings;
}
public void update(final LocalDate endDate, final List<Map<String, Object>> changes) {
if (endDate != null && this.endDate == null) {
this.endDate = endDate;
Map<String, Object> map = new HashMap<>(2);
map.put(TaxApiConstants.endDateParamName, endDate);
map.put(TaxApiConstants.taxComponentIdParamName, this.getTaxComponent().getId());
changes.add(map);
}
}
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
return DateUtils.isAfter(target, startDate()) && (endDate == null || !DateUtils.isAfter(target, endDate()));
}
public TaxComponent getTaxComponent() {
return this.taxComponent;
}
public LocalDate getEndDate() {
return this.endDate;
}
public LocalDate startDate() {
return this.startDate;
}
public LocalDate endDate() {
return this.endDate;
}
public TaxGroup getTaxGroup() {
return taxGroup;
}
public void setTaxGroup(TaxGroup taxGroup) {
this.taxGroup = taxGroup;
}
}
@@ -0,0 +1,26 @@
/**
* 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.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface TaxGroupRepository extends JpaRepository<TaxGroup, Long>, JpaSpecificationExecutor<TaxGroup> {
}
@@ -0,0 +1,38 @@
/**
* 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.domain;
import org.apache.fineract.portfolio.tax.exception.TaxGroupNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TaxGroupRepositoryWrapper {
private final TaxGroupRepository repository;
@Autowired
public TaxGroupRepositoryWrapper(final TaxGroupRepository repository) {
this.repository = repository;
}
public TaxGroup findOneWithNotFoundDetection(final Long id) {
return this.repository.findById(id).orElseThrow(() -> new TaxGroupNotFoundException(id));
}
}
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.tax.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class TaxComponentNotFoundException extends AbstractPlatformResourceNotFoundException {
public TaxComponentNotFoundException(final Long id) {
super("error.msg.tax.component.id.invalid", "tax component with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.tax.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class TaxGroupNotFoundException extends AbstractPlatformResourceNotFoundException {
public TaxGroupNotFoundException(final Long id) {
super("error.msg.tax.group.id.invalid", "tax group with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,33 @@
/**
* 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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when tax group mapping does not exist.
*/
public class TaxMappingNotFoundException extends AbstractPlatformResourceNotFoundException {
public TaxMappingNotFoundException(final Long id) {
super("error.msg.tax.group.id.invalid", "Tax group mapping with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,129 @@
/**
* 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.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
import org.apache.fineract.portfolio.tax.data.TaxGroupMappingsData;
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
public final class TaxUtils {
private TaxUtils() {
}
public static Map<TaxComponent, BigDecimal> splitTax(final BigDecimal amount, final LocalDate date,
final Set<TaxGroupMappings> taxGroupMappings, final int scale) {
Map<TaxComponent, BigDecimal> map = new HashMap<>(3);
if (amount != null) {
final double amountVal = amount.doubleValue();
double cent_percentage = Double.parseDouble("100.0");
for (TaxGroupMappings groupMappings : taxGroupMappings) {
if (groupMappings.occursOnDayFromAndUpToAndIncluding(date)) {
TaxComponent component = groupMappings.getTaxComponent();
BigDecimal percentage = component.getApplicablePercentage(date);
if (percentage != null) {
double percentageVal = percentage.doubleValue();
double tax = amountVal * percentageVal / cent_percentage;
map.put(component, BigDecimal.valueOf(tax).setScale(scale, MoneyHelper.getRoundingMode()));
}
}
}
}
return map;
}
public static Map<TaxComponentData, BigDecimal> splitTaxData(final BigDecimal amount, final LocalDate date,
final Set<TaxGroupMappingsData> taxGroupMappings, final int scale) {
Map<TaxComponentData, BigDecimal> map = new HashMap<>(3);
if (amount != null) {
final double amountVal = amount.doubleValue();
double cent_percentage = Double.parseDouble("100.0");
for (TaxGroupMappingsData groupMappings : taxGroupMappings) {
if (groupMappings.occursOnDayFromAndUpToAndIncluding(date)) {
TaxComponentData component = groupMappings.getTaxComponent();
BigDecimal percentage = component.getApplicablePercentage(date);
if (percentage != null) {
double percentageVal = percentage.doubleValue();
double tax = amountVal * percentageVal / cent_percentage;
map.put(component, BigDecimal.valueOf(tax).setScale(scale, MoneyHelper.getRoundingMode()));
}
}
}
}
return map;
}
public static BigDecimal incomeAmount(final BigDecimal amount, final LocalDate date, final Set<TaxGroupMappings> taxGroupMappings,
final int scale) {
Map<TaxComponent, BigDecimal> map = splitTax(amount, date, taxGroupMappings, scale);
return incomeAmount(amount, map);
}
public static BigDecimal incomeAmount(final BigDecimal amount, final Map<TaxComponent, BigDecimal> map) {
BigDecimal totalTax = totalTaxAmount(map);
return amount.subtract(totalTax);
}
public static BigDecimal totalTaxAmount(final Map<TaxComponent, BigDecimal> map) {
BigDecimal totalTax = BigDecimal.ZERO;
for (BigDecimal tax : map.values()) {
totalTax = totalTax.add(tax);
}
return totalTax;
}
public static BigDecimal totalTaxDataAmount(final Map<TaxComponentData, BigDecimal> map) {
BigDecimal totalTax = BigDecimal.ZERO;
for (BigDecimal tax : map.values()) {
totalTax = totalTax.add(tax);
}
return totalTax;
}
public static BigDecimal addTax(final BigDecimal amount, final LocalDate date, final List<TaxGroupMappings> taxGroupMappings,
final int scale) {
BigDecimal totalAmount = null;
if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) {
double percentageVal = 0;
double amountVal = amount.doubleValue();
double cent_percentage = Double.parseDouble("100.0");
for (TaxGroupMappings groupMappings : taxGroupMappings) {
if (groupMappings.occursOnDayFromAndUpToAndIncluding(date)) {
TaxComponent component = groupMappings.getTaxComponent();
BigDecimal percentage = component.getApplicablePercentage(date);
if (percentage != null) {
percentageVal = percentageVal + percentage.doubleValue();
}
}
}
double total = amountVal * cent_percentage / (cent_percentage - percentageVal);
totalAmount = BigDecimal.valueOf(total).setScale(scale, MoneyHelper.getRoundingMode());
}
return totalAmount;
}
}
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<!-- This file is only used for static weaving, nothing more. -->
<!-- You can find the runtime configuration in the JPAConfig class -->
<persistence-unit name="jpa-pu" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<!-- Fineract core module -->
<class>org.apache.fineract.accounting.glaccount.domain.GLAccount</class>
<class>org.apache.fineract.accounting.journalentry.domain.JournalEntry</class>
<class>org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom</class>
<class>org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom</class>
<class>org.apache.fineract.infrastructure.codes.domain.Code</class>
<class>org.apache.fineract.infrastructure.codes.domain.CodeValue</class>
<class>org.apache.fineract.infrastructure.documentmanagement.domain.Image</class>
<class>org.apache.fineract.organisation.staff.domain.Staff</class>
<class>org.apache.fineract.organisation.office.domain.Office</class>
<class>org.apache.fineract.organisation.office.domain.OrganisationCurrency</class>
<class>org.apache.fineract.organisation.monetary.domain.ApplicationCurrency</class>
<class>org.apache.fineract.organisation.holiday.domain.Holiday</class>
<class>org.apache.fineract.organisation.workingdays.domain.WorkingDays</class>
<class>org.apache.fineract.portfolio.group.domain.Group</class>
<class>org.apache.fineract.portfolio.group.domain.GroupLevel</class>
<class>org.apache.fineract.portfolio.group.domain.StaffAssignmentHistory</class>
<class>org.apache.fineract.portfolio.group.domain.GroupRole</class>
<class>org.apache.fineract.portfolio.client.domain.Client</class>
<class>org.apache.fineract.portfolio.client.domain.ClientIdentifier</class>
<class>org.apache.fineract.portfolio.rate.domain.Rate</class>
<class>org.apache.fineract.portfolio.fund.domain.Fund</class>
<class>org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket</class>
<class>org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange</class>
<class>org.apache.fineract.portfolio.paymenttype.domain.PaymentType</class>
<class>org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail</class>
<class>org.apache.fineract.portfolio.tax.domain.TaxGroup</class>
<class>org.apache.fineract.portfolio.tax.domain.TaxGroupMappings</class>
<class>org.apache.fineract.portfolio.tax.domain.TaxComponent</class>
<class>org.apache.fineract.portfolio.tax.domain.TaxComponentHistory</class>
<class>org.apache.fineract.portfolio.floatingrates.domain.FloatingRate</class>
<class>org.apache.fineract.portfolio.floatingrates.domain.FloatingRatePeriod</class>
<class>org.apache.fineract.portfolio.calendar.domain.Calendar</class>
<class>org.apache.fineract.portfolio.calendar.domain.CalendarHistory</class>
<class>org.apache.fineract.portfolio.calendar.domain.CalendarInstance</class>
<class>org.apache.fineract.useradministration.domain.AppUser</class>
<class>org.apache.fineract.useradministration.domain.Role</class>
<class>org.apache.fineract.useradministration.domain.Permission</class>
<class>org.apache.fineract.useradministration.domain.AppUserClientMapping</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.weaving" value="static" />
</properties>
</persistence-unit>
</persistence>