http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
new file mode 100644
index 0000000..da02492
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
@@ -0,0 +1,40 @@
+/**
+ * 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.data;
+
+import org.joda.time.LocalDate;
+
+public class TaxGroupMappingsData {
+
+    @SuppressWarnings("unused")
+    private final Long id;
+    @SuppressWarnings("unused")
+    private final TaxComponentData taxComponent;
+    @SuppressWarnings("unused")
+    private final LocalDate startDate;
+    @SuppressWarnings("unused")
+    private final LocalDate endDate;
+
+    public TaxGroupMappingsData(final Long id, final TaxComponentData 
taxComponent, final LocalDate startDate, final LocalDate endDate) {
+        this.id = id;
+        this.taxComponent = taxComponent;
+        this.startDate = startDate;
+        this.endDate = endDate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponent.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponent.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponent.java
new file mode 100644
index 0000000..32ec96b
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponent.java
@@ -0,0 +1,222 @@
+/**
+ * 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 java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.commons.lang.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;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.hibernate.annotations.LazyCollection;
+import org.hibernate.annotations.LazyCollectionOption;
+import org.joda.time.LocalDate;
+
+@Entity
+@Table(name = "m_tax_component")
+public class TaxComponent extends AbstractAuditableCustom<AppUser, Long> {
+
+    @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)
+    @Temporal(TemporalType.DATE)
+    private Date startDate;
+
+    @LazyCollection(LazyCollectionOption.FALSE)
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+    @JoinColumn(name = "tax_component_id", referencedColumnName = "id", 
nullable = false)
+    private List<TaxComponentHistory> taxComponentHistories = new 
ArrayList<>();
+
+    @LazyCollection(LazyCollectionOption.TRUE)
+    @OneToMany(cascade = CascadeType.DETACH, mappedBy = "taxComponent", 
orphanRemoval = false)
+    private List<TaxGroupMappings> taxGroupMappings = new ArrayList<>();
+
+    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.toDate();
+    }
+
+    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 = new LocalDate(this.startDate);
+            updateStartDate(command, changes, true);
+            LocalDate newStartDate = new LocalDate(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.getLocalDateOfTenant();
+        if (command.parameterExists(TaxApiConstants.startDateParamName)) {
+            LocalDate startDateFromUI = 
command.localDateValueOfParameterNamed(TaxApiConstants.startDateParamName);
+            if (startDateFromUI != null) {
+                startDate = startDateFromUI;
+            }
+            this.startDate = startDate.toDate();
+            changes.put(TaxApiConstants.startDateParamName, startDate);
+        } else if (setAsCurrentDate) {
+            changes.put(TaxApiConstants.startDateParamName, startDate);
+            this.startDate = startDate.toDate();
+        }
+
+    }
+
+    public BigDecimal getPercentage() {
+        return this.percentage;
+    }
+
+    public LocalDate startDate() {
+        LocalDate startDate = null;
+        if (this.startDate != null) {
+            startDate = new LocalDate(this.startDate);
+        }
+        return 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 target != null && target.isAfter(startDate());
+    }
+
+    public List<TaxComponentHistory> getTaxComponentHistories() {
+        return this.taxComponentHistories;
+    }
+
+    public List<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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentHistory.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentHistory.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentHistory.java
new file mode 100644
index 0000000..9074895
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentHistory.java
@@ -0,0 +1,92 @@
+/**
+ * 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 java.math.BigDecimal;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.joda.time.LocalDate;
+
+@Entity
+@Table(name = "m_tax_component_history")
+public class TaxComponentHistory extends AbstractAuditableCustom<AppUser, 
Long> {
+
+    @Column(name = "percentage", scale = 6, precision = 19, nullable = false)
+    private BigDecimal percentage;
+
+    @Column(name = "start_date", nullable = false)
+    @Temporal(TemporalType.DATE)
+    private Date startDate;
+
+    @Column(name = "end_date", nullable = false)
+    @Temporal(TemporalType.DATE)
+    private Date endDate;
+
+    protected TaxComponentHistory() {
+
+    }
+
+    private TaxComponentHistory(final BigDecimal percentage, final LocalDate 
startDate, final LocalDate endDate) {
+        this.percentage = percentage;
+        this.startDate = startDate.toDate();
+        this.endDate = endDate.toDate();
+    }
+
+    public static TaxComponentHistory createTaxComponentHistory(final 
BigDecimal percentage, final LocalDate startDate,
+            final LocalDate endDate) {
+        return new TaxComponentHistory(percentage, startDate, endDate);
+    }
+    
+    public LocalDate startDate(){
+        LocalDate startDate = null;
+        if(this.startDate != null){
+            startDate = new LocalDate(this.startDate);
+        }
+        return startDate;
+    }
+    
+    public LocalDate endDate(){
+        LocalDate endDate = null;
+        if(this.endDate != null){
+            endDate = new LocalDate(this.endDate);
+        }
+        return endDate;
+    }
+    
+    public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
+        if(this.endDate == null){
+            return target != null && target.isAfter(startDate());
+        }
+        return target != null && target.isAfter(startDate()) && 
!target.isAfter(endDate());
+    }
+
+    
+    public BigDecimal getPercentage() {
+        return this.percentage;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepository.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepository.java
new file mode 100644
index 0000000..ee45584
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepository.java
@@ -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> {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepositoryWrapper.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepositoryWrapper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepositoryWrapper.java
new file mode 100644
index 0000000..ae590e7
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxComponentRepositoryWrapper.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.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) {
+
+        final TaxComponent taxComponent = this.repository.findOne(id);
+        if (taxComponent == null) { throw new 
TaxComponentNotFoundException(id); }
+
+        return taxComponent;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java
new file mode 100644
index 0000000..0e780f3
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java
@@ -0,0 +1,117 @@
+/**
+ * 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+import org.apache.commons.lang.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;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.hibernate.annotations.LazyCollection;
+import org.hibernate.annotations.LazyCollectionOption;
+
+@Entity
+@Table(name = "m_tax_group")
+public class TaxGroup extends AbstractAuditableCustom<AppUser, Long> {
+
+    @Column(name = "name", length = 100)
+    private String name;
+
+    @LazyCollection(LazyCollectionOption.FALSE)
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+    @JoinColumn(name = "tax_group_id", referencedColumnName = "id", nullable = 
false)
+    private List<TaxGroupMappings> taxGroupMappings = new ArrayList<>();
+
+    protected TaxGroup() {
+
+    }
+
+    private TaxGroup(final String name, final List<TaxGroupMappings> 
taxGroupMappings) {
+        this.name = name;
+        this.taxGroupMappings = taxGroupMappings;
+    }
+
+    public static TaxGroup createTaxGroup(final String name, final 
List<TaxGroupMappings> taxGroupMappings) {
+        return new TaxGroup(name, taxGroupMappings);
+    }
+
+    public Map<String, Object> update(final JsonCommand command, final 
List<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 List<TaxGroupMappings> getTaxGroupMappings() {
+        return this.taxGroupMappings;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupMappings.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupMappings.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupMappings.java
new file mode 100644
index 0000000..767e4ba
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupMappings.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.domain;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
+import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.joda.time.LocalDate;
+
+@Entity
+@Table(name = "m_tax_group_mappings")
+public class TaxGroupMappings extends AbstractAuditableCustom<AppUser, Long> {
+
+    @ManyToOne
+    @JoinColumn(name = "tax_component_id", nullable = false)
+    private TaxComponent taxComponent;
+
+    @Column(name = "start_date", nullable = false)
+    @Temporal(TemporalType.DATE)
+    private Date startDate;
+
+    @Column(name = "end_date", nullable = true)
+    @Temporal(TemporalType.DATE)
+    private Date endDate;
+
+    protected TaxGroupMappings() {}
+
+    private TaxGroupMappings(final TaxComponent taxComponent, final LocalDate 
startDate, final LocalDate endDate) {
+
+        this.taxComponent = taxComponent;
+        if (startDate != null) {
+            this.startDate = startDate.toDate();
+        }
+        if (endDate != null) {
+            this.endDate = endDate.toDate();
+        }
+    }
+
+    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 Date 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) {
+        if (this.endDate == null) { return target != null && 
target.isAfter(startDate()); }
+        return target != null && target.isAfter(startDate()) && 
!target.isAfter(endDate());
+    }
+
+    public TaxComponent getTaxComponent() {
+        return this.taxComponent;
+    }
+
+    public Date getEndDate() {
+        return this.endDate;
+    }
+
+    public LocalDate startDate() {
+        LocalDate startDate = null;
+        if (this.startDate != null) {
+            startDate = new LocalDate(this.startDate);
+        }
+        return startDate;
+    }
+
+    public LocalDate endDate() {
+        LocalDate endDate = null;
+        if (this.endDate != null) {
+            endDate = new LocalDate(this.endDate);
+        }
+        return endDate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepository.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepository.java
new file mode 100644
index 0000000..9934541
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepository.java
@@ -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> {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepositoryWrapper.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepositoryWrapper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepositoryWrapper.java
new file mode 100644
index 0000000..5ef1776
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroupRepositoryWrapper.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.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) {
+
+        final TaxGroup taxGroup = this.repository.findOne(id);
+        if (taxGroup == null) { throw new TaxGroupNotFoundException(id); }
+
+        return taxGroup;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxComponentNotFoundException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxComponentNotFoundException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxComponentNotFoundException.java
new file mode 100644
index 0000000..0cae305
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxComponentNotFoundException.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxGroupNotFoundException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxGroupNotFoundException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxGroupNotFoundException.java
new file mode 100644
index 0000000..76ff42f
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxGroupNotFoundException.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxMappingNotFoundException.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxMappingNotFoundException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxMappingNotFoundException.java
new file mode 100644
index 0000000..d9ce11f
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/exception/TaxMappingNotFoundException.java
@@ -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.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);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxComponentCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxComponentCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxComponentCommandHandler.java
new file mode 100644
index 0000000..cabf0ca
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxComponentCommandHandler.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@CommandType(entity = "TAXCOMPONENT", action = "CREATE")
+public class CreateTaxComponentCommandHandler implements 
NewCommandSourceHandler {
+
+    private final TaxWritePlatformService taxWritePlatformService;
+
+    @Autowired
+    public CreateTaxComponentCommandHandler(final TaxWritePlatformService 
taxWritePlatformService) {
+        this.taxWritePlatformService = taxWritePlatformService;
+    }
+
+    @Override
+    public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
+        return this.taxWritePlatformService.createTaxComponent(jsonCommand);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxGroupCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxGroupCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxGroupCommandHandler.java
new file mode 100644
index 0000000..e5b523d
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/CreateTaxGroupCommandHandler.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@CommandType(entity = "TAXGROUP", action = "CREATE")
+public class CreateTaxGroupCommandHandler implements NewCommandSourceHandler {
+
+    private final TaxWritePlatformService taxWritePlatformService;
+
+    @Autowired
+    public CreateTaxGroupCommandHandler(final TaxWritePlatformService 
taxWritePlatformService) {
+        this.taxWritePlatformService = taxWritePlatformService;
+    }
+
+    @Override
+    public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
+        return this.taxWritePlatformService.createTaxGroup(jsonCommand);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxComponentCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxComponentCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxComponentCommandHandler.java
new file mode 100644
index 0000000..b94e369
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxComponentCommandHandler.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@CommandType(entity = "TAXCOMPONENT", action = "UPDATE")
+public class UpdateTaxComponentCommandHandler implements 
NewCommandSourceHandler {
+
+    private final TaxWritePlatformService taxWritePlatformService;
+
+    @Autowired
+    public UpdateTaxComponentCommandHandler(final TaxWritePlatformService 
taxWritePlatformService) {
+        this.taxWritePlatformService = taxWritePlatformService;
+    }
+
+    @Override
+    public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
+        return 
this.taxWritePlatformService.updateTaxComponent(jsonCommand.entityId(), 
jsonCommand);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxGroupCommandHandler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxGroupCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxGroupCommandHandler.java
new file mode 100644
index 0000000..1229e46
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/handler/UpdateTaxGroupCommandHandler.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@CommandType(entity = "TAXGROUP", action = "UPDATE")
+public class UpdateTaxGroupCommandHandler implements NewCommandSourceHandler {
+
+    private final TaxWritePlatformService taxWritePlatformService;
+
+    @Autowired
+    public UpdateTaxGroupCommandHandler(final TaxWritePlatformService 
taxWritePlatformService) {
+        this.taxWritePlatformService = taxWritePlatformService;
+    }
+
+    @Override
+    public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
+        return 
this.taxWritePlatformService.updateTaxGroup(jsonCommand.entityId(), 
jsonCommand);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/serialization/TaxValidator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/serialization/TaxValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/serialization/TaxValidator.java
new file mode 100644
index 0000000..6df786e
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/serialization/TaxValidator.java
@@ -0,0 +1,365 @@
+/**
+ * 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 java.lang.reflect.Type;
+import java.math.BigDecimal;
+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.lang.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.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+@Component
+public class TaxValidator {
+
+    final Set<String> supportedTaxComponentCreateParameters = new 
HashSet<>(Arrays.asList("dateFormat", "locale",
+            TaxApiConstants.nameParamName, 
TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName,
+            TaxApiConstants.debitAccountTypeParamName, 
TaxApiConstants.debitAcountIdParamName, 
TaxApiConstants.creditAccountTypeParamName,
+            TaxApiConstants.creditAcountIdParamName));
+
+    final Set<String> supportedTaxComponentUpdateParameters = new 
HashSet<>(Arrays.asList("dateFormat", "locale",
+            TaxApiConstants.nameParamName, 
TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName));
+
+    final Set<String> supportedTaxGroupParameters = new 
HashSet<>(Arrays.asList("dateFormat", "locale", TaxApiConstants.nameParamName,
+            TaxApiConstants.taxComponentsParamName));
+
+    final Set<String> supportedTaxGroupTaxComponentsCreateParameters = new 
HashSet<>(Arrays.asList(TaxApiConstants.taxComponentIdParamName,
+            TaxApiConstants.startDateParamName));
+
+    final Set<String> supportedTaxGroupTaxComponentsUpdateParameters = new 
HashSet<>(Arrays.asList(TaxApiConstants.idParamName,
+            TaxApiConstants.taxComponentIdParamName, 
TaxApiConstants.startDateParamName, TaxApiConstants.endDateParamName));
+
+    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, 
this.supportedTaxComponentCreateParameters);
+
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors).resource("tax.component");
+
+        final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+        final String name = 
this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, 
element);
+        
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
+
+        final BigDecimal percentage = 
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName,
 element);
+        
baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank().positiveAmount()
+                .notGreaterThanMax(BigDecimal.valueOf(100));
+
+        final Integer debitAccountType = 
this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName,
+                element);
+        baseDataValidator
+                .reset()
+                .parameter(TaxApiConstants.debitAccountTypeParamName)
+                .value(debitAccountType)
+                .ignoreIfNull()
+                .isOneOfTheseValues(GLAccountType.ASSET.getValue(), 
GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(),
+                        GLAccountType.INCOME.getValue(), 
GLAccountType.EXPENSE.getValue());
+
+        final Long debitAccountId = 
this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, 
element);
+        
baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId).longGreaterThanZero();
+        if (debitAccountType != null || debitAccountId != null) {
+            
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType).notBlank();
+            
baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId).notBlank();
+        }
+
+        final Integer creditAccountType = 
this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.creditAccountTypeParamName,
+                element);
+        baseDataValidator
+                .reset()
+                .parameter(TaxApiConstants.creditAccountTypeParamName)
+                .value(creditAccountType)
+                .ignoreIfNull()
+                .isOneOfTheseValues(GLAccountType.ASSET.getValue(), 
GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(),
+                        GLAccountType.INCOME.getValue(), 
GLAccountType.EXPENSE.getValue());
+
+        final Long creditAccountId = 
this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAcountIdParamName,
 element);
+        
baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId).longGreaterThanZero();
+        if (creditAccountType != null || creditAccountId != null) {
+            
baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId).notBlank();
+            
baseDataValidator.reset().parameter(TaxApiConstants.creditAccountTypeParamName).value(creditAccountType).notBlank();
+        }
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+    }
+
+    public void validateForTaxComponentUpdate(final String json) {
+        if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+        final Type typeOfMap = new TypeToken<Map<String, Object>>() 
{}.getType();
+        this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, 
this.supportedTaxComponentUpdateParameters);
+
+        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.getLocalDateOfTenant());
+        }
+        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, 
this.supportedTaxGroupParameters);
+
+        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,
+                        supportedTaxGroupTaxComponentsCreateParameters);
+                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, 
this.supportedTaxGroupParameters);
+
+        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,
+                            supportedTaxGroupTaxComponentsUpdateParameters);
+                    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 
+ "." + TaxApiConstants.taxComponentIdParamName
+                                                + ".at.index." + 
i).value(taxComponentId).notNull().longGreaterThanZero();
+                    } else {
+                        baseDataValidator
+                                .reset()
+                                .parameter(
+                                        TaxApiConstants.taxComponentsParamName 
+ "." + TaxApiConstants.taxComponentIdParamName
+                                                + ".at.index." + 
i).value(taxComponentId).longGreaterThanZero();
+                        baseDataValidator.reset()
+                                
.parameter(TaxApiConstants.taxComponentsParamName + "." + 
TaxApiConstants.idParamName + ".at.index." + i)
+                                .value(taxMappingId).longGreaterThanZero();
+                    }
+
+                    final LocalDate endDate = 
this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.endDateParamName, 
taxComponent,
+                            dateFormat, locale);
+                    baseDataValidator.reset()
+                            .parameter(TaxApiConstants.taxComponentsParamName 
+ "." + TaxApiConstants.endDateParamName + ".at.index." + i)
+                            
.value(endDate).ignoreIfNull().validateDateAfter(DateUtils.getLocalDateOfTenant());
+                    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 List<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 && 
!existing.endDate().isEqual(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");
+        List<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 List<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 
(groupMappingsOne.startDate().isAfter(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 List<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); }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxAssembler.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxAssembler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxAssembler.java
new file mode 100644
index 0000000..1e5ad92
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxAssembler.java
@@ -0,0 +1,163 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.service;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.fineract.accounting.glaccount.domain.GLAccount;
+import 
org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
+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.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.TaxComponentRepositoryWrapper;
+import org.apache.fineract.portfolio.tax.domain.TaxGroup;
+import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+@Component
+public class TaxAssembler {
+
+    private final FromJsonHelper fromApiJsonHelper;
+    private final GLAccountRepositoryWrapper glAccountRepositoryWrapper;
+    private final TaxComponentRepositoryWrapper taxComponentRepositoryWrapper;
+
+    @Autowired
+    public TaxAssembler(final FromJsonHelper fromApiJsonHelper, final 
GLAccountRepositoryWrapper glAccountRepositoryWrapper,
+            final TaxComponentRepositoryWrapper taxComponentRepositoryWrapper) 
{
+        this.fromApiJsonHelper = fromApiJsonHelper;
+        this.glAccountRepositoryWrapper = glAccountRepositoryWrapper;
+        this.taxComponentRepositoryWrapper = taxComponentRepositoryWrapper;
+    }
+
+    public TaxComponent assembleTaxComponentFrom(final JsonCommand command) {
+        final JsonElement element = command.parsedJson();
+
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors).resource("tax.component");
+
+        final String name = 
this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, 
element);
+        final BigDecimal percentage = 
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName,
 element);
+        final Integer debitAccountType = 
this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName,
+                element);
+        final Long debitAccountId = 
this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, 
element);
+        GLAccountType debitGlAccountType = null;
+        if (debitAccountType != null) {
+            debitGlAccountType = GLAccountType.fromInt(debitAccountType);
+        }
+        GLAccount debitGlAccount = null;
+        if (debitAccountId != null) {
+            debitGlAccount = 
this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(debitAccountId);
+            if (!debitGlAccount.getType().equals(debitAccountType) || 
debitGlAccount.isHeaderAccount()) {
+                
baseDataValidator.parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId)
+                        .failWithCode("not.a.valid.account");
+            }
+        }
+
+        final Integer creditAccountType = 
this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.creditAccountTypeParamName,
+                element);
+        GLAccountType creditGlAccountType = null;
+        if (creditAccountType != null) {
+            creditGlAccountType = GLAccountType.fromInt(creditAccountType);
+        }
+        final Long creditAccountId = 
this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAcountIdParamName,
 element);
+        GLAccount creditGlAccount = null;
+        if (creditAccountId != null) {
+            creditGlAccount = 
this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(creditAccountId);
+            if (!creditGlAccount.getType().equals(creditAccountType) || 
creditGlAccount.isHeaderAccount()) {
+                
baseDataValidator.parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId)
+                        .failWithCode("not.a.valid.account");
+            }
+        }
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+        LocalDate startDate = 
this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName,
 element);
+        if (startDate == null) {
+            startDate = DateUtils.getLocalDateOfTenant();
+        }
+
+        return TaxComponent.createTaxComponent(name, percentage, 
debitGlAccountType, debitGlAccount, creditGlAccountType, creditGlAccount,
+                startDate);
+    }
+
+    public TaxGroup assembleTaxGroupFrom(final JsonCommand command) {
+        final JsonElement element = command.parsedJson();
+
+        final String name = 
this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, 
element);
+        boolean isUpdate = false;
+        final List<TaxGroupMappings> groupMappings = 
assembleTaxGroupMappingsFrom(command, isUpdate);
+        return TaxGroup.createTaxGroup(name, groupMappings);
+    }
+
+    public List<TaxGroupMappings> assembleTaxGroupMappingsFrom(final 
JsonCommand command, boolean isUpdate) {
+        List<TaxGroupMappings> groupMappings = new ArrayList<>();
+        final JsonElement element = command.parsedJson();
+
+        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 = 0; i < array.size(); i++) {
+                final JsonObject taxComponent = array.get(i).getAsJsonObject();
+                final Long mappingId = 
this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.idParamName, 
taxComponent);
+                final Long taxComponentId = 
this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName,
 taxComponent);
+                TaxComponent component =  null;
+                if(taxComponentId != null){
+                    component = 
this.taxComponentRepositoryWrapper.findOneWithNotFoundDetection(taxComponentId);
+                }
+                LocalDate startDate = 
this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName,
 taxComponent,
+                        dateFormat, locale);
+                final LocalDate endDate = 
this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.endDateParamName, 
taxComponent,
+                        dateFormat, locale);
+                if (endDate == null && startDate == null) {
+                    startDate = DateUtils.getLocalDateOfTenant();
+                }
+                TaxGroupMappings mappings = null;
+                if (isUpdate && mappingId != null) {
+                    mappings = 
TaxGroupMappings.createTaxGroupMappings(mappingId, component, endDate);
+                } else {
+                    mappings = 
TaxGroupMappings.createTaxGroupMappings(component, startDate);
+                }
+                groupMappings.add(mappings);
+
+            }
+        }
+
+        return groupMappings;
+    }
+
+    private void throwExceptionIfValidationWarningsExist(final 
List<ApiParameterError> dataValidationErrors) {
+        if (!dataValidationErrors.isEmpty()) { throw new 
PlatformApiDataValidationException(dataValidationErrors); }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformService.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformService.java
new file mode 100644
index 0000000..646255c
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformService.java
@@ -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.service;
+
+import java.util.Collection;
+
+import org.apache.fineract.portfolio.tax.data.TaxComponentData;
+import org.apache.fineract.portfolio.tax.data.TaxGroupData;
+
+public interface TaxReadPlatformService {
+
+    public TaxComponentData retrieveTaxComponentData(final Long id);
+
+    public TaxComponentData retrieveTaxComponentTemplate();
+
+    public TaxGroupData retrieveTaxGroupData(final Long id);
+
+    public TaxGroupData retrieveTaxGroupWithTemplate(final Long id);
+
+    public TaxGroupData retrieveTaxGroupTemplate();
+
+    Collection<TaxComponentData> retrieveAllTaxComponents();
+
+    Collection<TaxGroupData> retrieveAllTaxGroups();
+
+    Collection<TaxGroupData> retrieveTaxGroupsForLookUp();
+
+}

Reply via email to