http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
index 1e76d93..4865fa4 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
@@ -61,6 +61,7 @@ import 
org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import 
org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
 import org.apache.fineract.portfolio.savings.data.SavingsProductData;
 import 
org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
+import org.apache.fineract.portfolio.tax.data.TaxGroupData;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -269,7 +270,11 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             sqlBuilder.append("sa.min_balance_for_interest_calculation as 
minBalanceForInterestCalculation,");
             sqlBuilder.append("sa.min_required_balance as minRequiredBalance, 
");
             sqlBuilder.append("sa.enforce_min_required_balance as 
enforceMinRequiredBalance, ");
-            sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds ");
+            sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, ");
+            sqlBuilder.append("sa.withhold_tax as withHoldTax,");
+            sqlBuilder.append("sa.total_withhold_tax_derived as 
totalWithholdTax,");
+            sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
+
             sqlBuilder.append("from m_savings_account sa ");
             sqlBuilder.append("join m_savings_product sp ON sa.product_id = 
sp.id ");
             sqlBuilder.append("join m_currency curr on curr.code = 
sa.currency_code ");
@@ -282,6 +287,7 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             sqlBuilder.append("left join m_appuser abu on abu.id = 
sa.approvedon_userid ");
             sqlBuilder.append("left join m_appuser avbu on avbu.id = 
sa.activatedon_userid ");
             sqlBuilder.append("left join m_appuser cbu on cbu.id = 
sa.closedon_userid ");
+            sqlBuilder.append("left join m_tax_group tg on tg.id = 
sa.tax_group_id  ");
 
             this.schemaSql = sqlBuilder.toString();
         }
@@ -400,8 +406,10 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
 
             final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
             final BigDecimal overdraftLimit = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
-            final BigDecimal nominalAnnualInterestRateOverdraft = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, 
"nominalAnnualInterestRateOverdraft");
-            final BigDecimal minOverdraftForInterestCalculation = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, 
"minOverdraftForInterestCalculation");
+            final BigDecimal nominalAnnualInterestRateOverdraft = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                    "nominalAnnualInterestRateOverdraft");
+            final BigDecimal minOverdraftForInterestCalculation = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                    "minOverdraftForInterestCalculation");
 
             final BigDecimal minRequiredBalance = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
             final boolean enforceMinRequiredBalance = 
rs.getBoolean("enforceMinRequiredBalance");
@@ -431,22 +439,32 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             final BigDecimal accountBalance = 
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
             final BigDecimal totalFeeCharge = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge");
             final BigDecimal totalPenaltyCharge = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge");
-            final BigDecimal totalOverdraftInterestDerived = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, 
"totalOverdraftInterestDerived");
-            
+            final BigDecimal totalOverdraftInterestDerived = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                    "totalOverdraftInterestDerived");
+            final BigDecimal totalWithholdTax = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithholdTax");
+
             final BigDecimal minBalanceForInterestCalculation = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
                     "minBalanceForInterestCalculation");
             final BigDecimal onHoldFunds = rs.getBigDecimal("onHoldFunds");
 
             final SavingsAccountSummaryData summary = new 
SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
                     totalWithdrawalFees, totalAnnualFees, totalInterestEarned, 
totalInterestPosted, accountBalance, totalFeeCharge,
-                    totalPenaltyCharge, totalOverdraftInterestDerived);
+                    totalPenaltyCharge, totalOverdraftInterestDerived, 
totalWithholdTax);
+
+            final boolean withHoldTax = rs.getBoolean("withHoldTax");
+            final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
+            final String taxGroupName = rs.getString("taxGroupName");
+            TaxGroupData taxGroupData = null;
+            if (taxGroupId != null) {
+                taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
+            }
 
             return SavingsAccountData.instance(id, accountNo, depositType, 
externalId, groupId, groupName, clientId, clientName, productId,
                     productName, fieldOfficerId, fieldOfficerName, status, 
timeline, currency, nominalAnnualInterestRate,
                     interestCompoundingPeriodType, interestPostingPeriodType, 
interestCalculationType, interestCalculationDaysInYearType,
                     minRequiredOpeningBalance, lockinPeriodFrequency, 
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary,
                     allowOverdraft, overdraftLimit, minRequiredBalance, 
enforceMinRequiredBalance, minBalanceForInterestCalculation,
-                    onHoldFunds, nominalAnnualInterestRateOverdraft, 
minOverdraftForInterestCalculation);
+                    onHoldFunds, nominalAnnualInterestRateOverdraft, 
minOverdraftForInterestCalculation, withHoldTax, taxGroupData);
         }
     }
 
@@ -540,7 +558,8 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             final Collection<SavingsAccountChargeData> charges = 
fromChargesToSavingsCharges(productCharges);
 
             final boolean feeChargesOnly = false;
-            final Collection<ChargeData> chargeOptions = 
this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly);
+            final Collection<ChargeData> chargeOptions = 
this.chargeReadPlatformService
+                    .retrieveSavingsProductApplicableCharges(feeChargesOnly);
 
             Collection<StaffData> fieldOfficerOptions = null;
 
@@ -598,7 +617,8 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             final Collection<SavingsAccountChargeData> charges = null;
 
             final boolean feeChargesOnly = true;
-            final Collection<ChargeData> chargeOptions = 
this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly);
+            final Collection<ChargeData> chargeOptions = 
this.chargeReadPlatformService
+                    .retrieveSavingsProductApplicableCharges(feeChargesOnly);
 
             template = SavingsAccountData.withTemplateOptions(template, 
productOptions, fieldOfficerOptions,
                     interestCompoundingPeriodTypeOptions, 
interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
@@ -842,6 +862,9 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             sqlBuilder.append("sp.overdraft_limit as overdraftLimit, ");
             sqlBuilder.append("sp.nominal_annual_interest_rate_overdraft as 
nominalAnnualInterestRateOverdraft, ");
             sqlBuilder.append("sp.min_overdraft_for_interest_calculation as 
minOverdraftForInterestCalculation, ");
+            sqlBuilder.append("sp.withhold_tax as withHoldTax,");
+            sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, 
");
+
             // sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,");
             // sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, 
");
             // sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay ");
@@ -849,6 +872,7 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
             sqlBuilder.append("sp.enforce_min_required_balance as 
enforceMinRequiredBalance ");
             sqlBuilder.append("from m_savings_product sp ");
             sqlBuilder.append("join m_currency curr on curr.code = 
sp.currency_code ");
+            sqlBuilder.append("left join m_tax_group tg on tg.id = 
sp.tax_group_id  ");
 
             this.schemaSql = sqlBuilder.toString();
         }
@@ -912,8 +936,10 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
 
             final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
             final BigDecimal overdraftLimit = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
-            final BigDecimal nominalAnnualInterestRateOverdraft = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, 
"nominalAnnualInterestRateOverdraft");
-            final BigDecimal minOverdraftForInterestCalculation = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, 
"minOverdraftForInterestCalculation");
+            final BigDecimal nominalAnnualInterestRateOverdraft = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                    "nominalAnnualInterestRateOverdraft");
+            final BigDecimal minOverdraftForInterestCalculation = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                    "minOverdraftForInterestCalculation");
 
             final BigDecimal minRequiredBalance = 
JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
             final boolean enforceMinRequiredBalance = 
rs.getBoolean("enforceMinRequiredBalance");
@@ -933,6 +959,14 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
              * MonthDay(annualFeeOnMonth, annualFeeOnDay); }
              */
 
+            final boolean withHoldTax = rs.getBoolean("withHoldTax");
+            final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
+            final String taxGroupName = rs.getString("taxGroupName");
+            TaxGroupData taxGroupData = null;
+            if (taxGroupId != null) {
+                taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
+            }
+
             Long clientId = null;
             String clientName = null;
             if (this.client != null) {
@@ -961,7 +995,7 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
                     interestCompoundingPeriodType, interestPostingPeriodType, 
interestCalculationType, interestCalculationDaysInYearType,
                     minRequiredOpeningBalance, lockinPeriodFrequency, 
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary,
                     allowOverdraft, overdraftLimit, minRequiredBalance, 
enforceMinRequiredBalance, minBalanceForInterestCalculation,
-                    onHoldFunds, nominalAnnualInterestRateOverdraft, 
minOverdraftForInterestCalculation);
+                    onHoldFunds, nominalAnnualInterestRateOverdraft, 
minOverdraftForInterestCalculation, withHoldTax, taxGroupData);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
index 434ac09..3fe8aa9 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
@@ -79,4 +79,6 @@ public interface SavingsAccountWritePlatformService {
             Set<Long> existingReversedTransactionIds);
 
     void postInterest(SavingsAccount account);
+
+    CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, 
JsonCommand command);
 }
\ 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/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
index ae75bbf..f1b338b 100755
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -89,6 +89,7 @@ import org.springframework.util.CollectionUtils;
 import java.math.BigDecimal;
 import java.math.MathContext;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -102,6 +103,7 @@ import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.amountPa
 import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
 import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
 import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName;
+import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
 
 @Service
 public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements 
SavingsAccountWritePlatformService {
@@ -129,7 +131,6 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
     private final ConfigurationDomainService configurationDomainService;
     private final DepositAccountOnHoldTransactionRepository 
depositAccountOnHoldTransactionRepository;
 
-
     @Autowired
     public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final 
PlatformSecurityContext context,
             final SavingsAccountRepository savingAccountRepository,
@@ -141,8 +142,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
             final ApplicationCurrencyRepositoryWrapper 
applicationCurrencyRepositoryWrapper,
             final JournalEntryWritePlatformService 
journalEntryWritePlatformService,
             final SavingsAccountDomainService savingsAccountDomainService, 
final NoteRepository noteRepository,
-            final AccountTransfersReadPlatformService 
accountTransfersReadPlatformService, final HolidayRepositoryWrapper 
holidayRepository,
-            final WorkingDaysRepositoryWrapper workingDaysRepository,
+            final AccountTransfersReadPlatformService 
accountTransfersReadPlatformService,
+            final HolidayRepositoryWrapper holidayRepository, final 
WorkingDaysRepositoryWrapper workingDaysRepository,
             final AccountAssociationsReadPlatformService 
accountAssociationsReadPlatformService,
             final ChargeRepositoryWrapper chargeRepository, final 
SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
             final SavingsAccountDataValidator fromApiJsonDeserializer, final 
SavingsAccountRepositoryWrapper savingsRepository,
@@ -169,7 +170,7 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         this.savingsRepository = savingsRepository;
         this.staffRepository = staffRepository;
         this.configurationDomainService = configurationDomainService;
-        this.depositAccountOnHoldTransactionRepository= 
depositAccountOnHoldTransactionRepository;
+        this.depositAccountOnHoldTransactionRepository = 
depositAccountOnHoldTransactionRepository;
     }
 
     @Transactional
@@ -227,10 +228,12 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         }
         
account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd,
 financialYearBeginningMonth, user);
         List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions 
= null;
-        if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
-            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+        if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
+            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository
+                    
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
         }
-        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
+        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),
+                depositAccountOnHoldTransactions);
     }
 
     @Transactional
@@ -316,8 +319,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
 
         AppUser user = getAppUserIfPresent();
 
-        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository
-                .findOneWithNotFoundDetection(savingsAccountChargeId, 
accountId);
+        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+                savingsAccountChargeId, accountId);
 
         final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
 
@@ -360,7 +363,6 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                 .build();
     }
 
-
     @Override
     public CommandProcessingResult postInterest(final Long savingsId) {
 
@@ -385,7 +387,7 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         final Integer financialYearBeginningMonth = 
this.configurationDomainService.retrieveFinancialYearBeginningMonth();
 
         if (account.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) 
> 0
-                       || (account.allowOverdraft() && 
account.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 
0)) {
+                || (account.allowOverdraft() && 
account.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 
0)) {
             final Set<Long> existingTransactionIds = new HashSet<>();
             final Set<Long> existingReversedTransactionIds = new HashSet<>();
             updateExistingTransactionsDetails(account, existingTransactionIds, 
existingReversedTransactionIds);
@@ -426,16 +428,13 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         if (savingsAccountTransaction == null) { throw new 
SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
 
         if (!allowAccountTransferModification
-                && this.accountTransfersReadPlatformService
-                        .isAccountTransfer(transactionId,
-                                PortfolioAccountType.SAVINGS)) { throw new 
PlatformServiceUnavailableException(
-                                        
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings 
account transaction:"
-                                                + transactionId + " update not 
allowed as it involves in account transfer",
-                                        transactionId); }
+                && 
this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, 
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
+                
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings 
account transaction:" + transactionId
+                        + " update not allowed as it involves in account 
transfer", transactionId); }
 
-        if (!account
-                .allowModify()) { throw new 
PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
-                        "Savings account transaction:" + transactionId + " 
update not allowed for this savings type", transactionId); }
+        if (!account.allowModify()) { throw new 
PlatformServiceUnavailableException(
+                "error.msg.saving.account.transaction.update.not.allowed", 
"Savings account transaction:" + transactionId
+                        + " update not allowed for this savings type", 
transactionId); }
 
         final LocalDate today = DateUtils.getLocalDateOfTenant();
         final MathContext mc = new MathContext(15, 
MoneyHelper.getRoundingMode());
@@ -464,10 +463,11 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                     financialYearBeginningMonth);
         }
         List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions 
= null;
-        if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
-            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+        if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
+            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository
+                    
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
         }
-        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,depositAccountOnHoldTransactions);
+        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,
 depositAccountOnHoldTransactions);
         account.activateAccountBasedOnBalance();
         postJournalEntries(account, existingTransactionIds, 
existingReversedTransactionIds);
 
@@ -493,14 +493,12 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                 .findOneByIdAndSavingsAccountId(transactionId, savingsId);
         if (savingsAccountTransaction == null) { throw new 
SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
 
-        if (!(savingsAccountTransaction.isDeposit() || 
savingsAccountTransaction.isWithdrawal())
-                || savingsAccountTransaction.isReversed()) { throw new 
TransactionUpdateNotAllowedException(savingsId, transactionId); }
+        if (!(savingsAccountTransaction.isDeposit() || 
savingsAccountTransaction.isWithdrawal()) || 
savingsAccountTransaction.isReversed()) { throw new 
TransactionUpdateNotAllowedException(
+                savingsId, transactionId); }
 
-        if 
(this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
-                PortfolioAccountType.SAVINGS)) { throw new 
PlatformServiceUnavailableException(
-                        
"error.msg.saving.account.transfer.transaction.update.not.allowed",
-                        "Savings account transaction:" + transactionId + " 
update not allowed as it involves in account transfer",
-                        transactionId); }
+        if 
(this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, 
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
+                
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings 
account transaction:" + transactionId
+                        + " update not allowed as it involves in account 
transfer", transactionId); }
 
         this.savingsAccountTransactionDataValidator.validate(command);
 
@@ -510,9 +508,9 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         if (account.isNotActive()) {
             
throwValidationForActiveStatus(SavingsApiConstants.adjustTransactionAction);
         }
-        if (!account
-                .allowModify()) { throw new 
PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
-                        "Savings account transaction:" + transactionId + " 
update not allowed for this savings type", transactionId); }
+        if (!account.allowModify()) { throw new 
PlatformServiceUnavailableException(
+                "error.msg.saving.account.transaction.update.not.allowed", 
"Savings account transaction:" + transactionId
+                        + " update not allowed for this savings type", 
transactionId); }
         final Set<Long> existingTransactionIds = new HashSet<>();
         final Set<Long> existingReversedTransactionIds = new HashSet<>();
         updateExistingTransactionsDetails(account, existingTransactionIds, 
existingReversedTransactionIds);
@@ -553,10 +551,11 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                     financialYearBeginningMonth);
         }
         List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions 
= null;
-        if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
-            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+        if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
+            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository
+                    
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
         }
-        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,depositAccountOnHoldTransactions);
+        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,
 depositAccountOnHoldTransactions);
         account.activateAccountBasedOnBalance();
         postJournalEntries(account, existingTransactionIds, 
existingReversedTransactionIds);
         return new CommandProcessingResultBuilder() //
@@ -878,8 +877,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                 .isSavingsInterestPostingAtCurrentPeriodEnd();
         final Integer financialYearBeginningMonth = 
this.configurationDomainService.retrieveFinancialYearBeginningMonth();
 
-        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository
-                .findOneWithNotFoundDetection(savingsAccountChargeId, 
savingsAccountId);
+        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+                savingsAccountChargeId, savingsAccountId);
 
         // Get Savings account from savings charge
         final SavingsAccount account = savingsAccountCharge.savingsAccount();
@@ -901,11 +900,13 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                     financialYearBeginningMonth);
         }
         List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions 
= null;
-        if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
-            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+        if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
+            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository
+                    
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
         }
 
-        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,depositAccountOnHoldTransactions);
+        
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,
+                depositAccountOnHoldTransactions);
 
         this.savingAccountRepository.saveAndFlush(account);
 
@@ -928,8 +929,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
 
         final SavingsAccount savingsAccount = 
this.savingAccountAssembler.assembleFrom(savingsAccountId);
         checkClientOrGroupActive(savingsAccount);
-        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository
-                .findOneWithNotFoundDetection(savingsAccountChargeId, 
savingsAccountId);
+        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+                savingsAccountChargeId, savingsAccountId);
 
         savingsAccount.removeCharge(savingsAccountCharge);
         this.savingAccountRepository.saveAndFlush(savingsAccount);
@@ -954,8 +955,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         final BigDecimal amountPaid = 
command.bigDecimalValueOfParameterNamed(amountParamName);
         final LocalDate transactionDate = 
command.localDateValueOfParameterNamed(dueAsOfDateParamName);
 
-        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository
-                .findOneWithNotFoundDetection(savingsAccountChargeId, 
savingsAccountId);
+        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+                savingsAccountChargeId, savingsAccountId);
 
         final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
         final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors)
@@ -994,8 +995,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         AppUser user = null;
 
         final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
-        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository
-                .findOneWithNotFoundDetection(savingsAccountChargeId, 
accountId);
+        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+                savingsAccountChargeId, accountId);
 
         final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
         fmt.withZone(DateUtils.getDateTimeZoneOfTenant());
@@ -1031,11 +1032,13 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                     financialYearBeginningMonth);
         }
         List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions 
= null;
-        if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
-            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+        if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
+            depositAccountOnHoldTransactions = 
this.depositAccountOnHoldTransactionRepository
+                    
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
         }
 
-        account.validateAccountBalanceDoesNotBecomeNegative("." + 
SavingsAccountTransactionType.PAY_CHARGE.getCode(),depositAccountOnHoldTransactions);
+        account.validateAccountBalanceDoesNotBecomeNegative("." + 
SavingsAccountTransactionType.PAY_CHARGE.getCode(),
+                depositAccountOnHoldTransactions);
 
         this.savingAccountRepository.save(account);
 
@@ -1064,8 +1067,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
 
         this.context.authenticatedUser();
 
-        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository
-                .findOneWithNotFoundDetection(savingsAccountChargeId, 
savingsAccountId);
+        final SavingsAccountCharge savingsAccountCharge = 
this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
+                savingsAccountChargeId, savingsAccountId);
 
         final SavingsAccount account = savingsAccountCharge.savingsAccount();
         this.savingAccountAssembler.assignSavingAccountHelpers(account);
@@ -1143,16 +1146,16 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         final LocalDate dateOfSavingsOfficerAssignment = 
command.localDateValueOfParameterNamed("assignmentDate");
 
         if (fromSavingsOfficerId != null) {
-            fromSavingsOfficer = 
this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId,
-                    savingsForUpdate.office().getHierarchy());
+            fromSavingsOfficer = 
this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId,
 savingsForUpdate
+                    .office().getHierarchy());
         }
         if (toSavingsOfficerId != null) {
-            toSavingsOfficer = 
this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId,
-                    savingsForUpdate.office().getHierarchy());
+            toSavingsOfficer = 
this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId,
 savingsForUpdate
+                    .office().getHierarchy());
             actualChanges.put("toSavingsOfficerId", toSavingsOfficer.getId());
         }
-        if (!savingsForUpdate.hasSavingsOfficer(
-                fromSavingsOfficer)) { throw new 
SavingsOfficerAssignmentException(savingsAccountId, fromSavingsOfficerId); }
+        if (!savingsForUpdate.hasSavingsOfficer(fromSavingsOfficer)) { throw 
new SavingsOfficerAssignmentException(savingsAccountId,
+                fromSavingsOfficerId); }
 
         savingsForUpdate.reassignSavingsOfficer(toSavingsOfficer, 
dateOfSavingsOfficerAssignment);
 
@@ -1196,6 +1199,30 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                 .build();
     }
 
+    @Override
+    public CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, 
JsonCommand command) {
+        final Map<String, Object> actualChanges = new HashMap<>(1);
+        final SavingsAccount savingsForUpdate = 
this.savingsRepository.findOneWithNotFoundDetection(savingsAccountId);
+        if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, 
savingsForUpdate.withHoldTax())) {
+            final boolean newValue = 
command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
+            actualChanges.put(withHoldTaxParamName, newValue);
+            savingsForUpdate.setWithHoldTax(newValue);
+            if (savingsForUpdate.getTaxGroup() == null) {
+                final List<ApiParameterError> dataValidationErrors = new 
ArrayList<>();
+                final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors).resource("account");
+                
baseDataValidator.reset().parameter(withHoldTaxParamName).failWithCode("not.supported");
+                throw new 
PlatformApiDataValidationException(dataValidationErrors);
+            }
+        }
+
+        return new CommandProcessingResultBuilder() //
+                .withCommandId(command.commandId()) //
+                .withEntityId(savingsAccountId) //
+                .withSavingsId(savingsAccountId) //
+                .with(actualChanges) //
+                .build();
+    }
+
     private AppUser getAppUserIfPresent() {
         AppUser user = null;
         if (this.context != null) {

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java
index ae40555..c295561 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java
@@ -179,6 +179,10 @@ public class SavingsEnumerations {
                 optionData = new 
SavingsAccountTransactionEnumData(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue().longValue(),
                         
SavingsAccountTransactionType.OVERDRAFT_INTEREST.getCode(), "Overdraft 
Interest");
             break;
+            case WITHHOLD_TAX:
+                optionData = new 
SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHHOLD_TAX.getValue().longValue(),
+                        SavingsAccountTransactionType.WITHHOLD_TAX.getCode(), 
"Withhold Tax");
+            break;
         }
         return optionData;
     }
@@ -198,70 +202,71 @@ public class SavingsEnumerations {
         final boolean isPrematureClosed = type.isPreMatureClosure();
         final boolean isTransferInProgress = type.isTransferInProgress();
         final boolean isTransferOnHold = type.isTransferOnHold();
+        final boolean matured = type.isMatured();
 
         SavingsAccountStatusEnumData optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
                 SavingsAccountStatusType.INVALID.getCode(), "Invalid", 
submittedAndPendingApproval, isApproved, isRejected,
-                isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, 
isTransferInProgress, isTransferOnHold);
+                isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, 
isTransferInProgress, isTransferOnHold, matured);
 
         switch (type) {
             case INVALID:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
                         SavingsAccountStatusType.INVALID.getCode(), "Invalid", 
submittedAndPendingApproval, isApproved, isRejected,
-                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             case SUBMITTED_AND_PENDING_APPROVAL:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue()
                         .longValue(), 
SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted 
and pending approval",
                         submittedAndPendingApproval, isApproved, isRejected, 
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed,
-                        isTransferInProgress, isTransferOnHold);
+                        isTransferInProgress, isTransferOnHold, matured);
             break;
             case REJECTED:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.REJECTED.getValue().longValue(),
                         SavingsAccountStatusType.REJECTED.getCode(), 
"Rejected", submittedAndPendingApproval, isApproved, isRejected,
-                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             case WITHDRAWN_BY_APPLICANT:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getValue().longValue(),
                         
SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getCode(), "Withdrawn by 
applicant", submittedAndPendingApproval,
                         isApproved, isRejected, isWithdrawnByApplicant, 
isActive, isClosed, isPrematureClosed, isTransferInProgress,
-                        isTransferOnHold);
+                        isTransferOnHold, matured);
             break;
             case APPROVED:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.APPROVED.getValue().longValue(),
                         SavingsAccountStatusType.APPROVED.getCode(), 
"Approved", submittedAndPendingApproval, isApproved, isRejected,
-                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             case ACTIVE:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.ACTIVE.getValue().longValue(),
                         SavingsAccountStatusType.ACTIVE.getCode(), "Active", 
submittedAndPendingApproval, isApproved, isRejected,
-                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             case CLOSED:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.CLOSED.getValue().longValue(),
                         SavingsAccountStatusType.CLOSED.getCode(), "Closed", 
submittedAndPendingApproval, isApproved, isRejected,
-                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             case TRANSFER_IN_PROGRESS:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue().longValue(),
                         
SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getCode(), "Transfer in 
progress", submittedAndPendingApproval,
                         isApproved, isRejected, isWithdrawnByApplicant, 
isActive, isClosed, isPrematureClosed, isTransferInProgress,
-                        isTransferOnHold);
+                        isTransferOnHold, matured);
             break;
             case TRANSFER_ON_HOLD:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_ON_HOLD.getValue().longValue(),
                         SavingsAccountStatusType.TRANSFER_ON_HOLD.getCode(), 
"Transfer on hold", submittedAndPendingApproval,
                         isApproved, isRejected, isWithdrawnByApplicant, 
isActive, isClosed, isPrematureClosed, isTransferInProgress,
-                        isTransferOnHold);
+                        isTransferOnHold, matured);
             break;
             case PRE_MATURE_CLOSURE:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue().longValue(),
                         SavingsAccountStatusType.PRE_MATURE_CLOSURE.getCode(), 
"Premature Closed", submittedAndPendingApproval, isApproved,
-                        isRejected, isWithdrawnByApplicant, isActive, 
isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isRejected, isWithdrawnByApplicant, isActive, 
isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             case MATURED:
                 optionData = new 
SavingsAccountStatusEnumData(SavingsAccountStatusType.MATURED.getValue().longValue(),
                         SavingsAccountStatusType.MATURED.getCode(), "Matured", 
submittedAndPendingApproval, isApproved, isRejected,
-                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold);
+                        isWithdrawnByApplicant, isActive, isClosed, 
isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
             break;
             default:
             break;

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
index 9c82e16..1fc785e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
@@ -34,6 +34,7 @@ import 
org.apache.fineract.organisation.monetary.data.CurrencyData;
 import org.apache.fineract.portfolio.savings.DepositAccountType;
 import org.apache.fineract.portfolio.savings.data.SavingsProductData;
 import 
org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
+import org.apache.fineract.portfolio.tax.data.TaxGroupData;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.EmptyResultDataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -51,7 +52,7 @@ public class SavingsProductReadPlatformServiceImpl implements 
SavingsProductRead
 
     @Autowired
     public SavingsProductReadPlatformServiceImpl(final PlatformSecurityContext 
context, final RoutingDataSource dataSource,
-               final FineractEntityAccessUtil fineractEntityAccessUtil) {
+            final FineractEntityAccessUtil fineractEntityAccessUtil) {
         this.context = context;
         this.jdbcTemplate = new JdbcTemplate(dataSource);
         this.fineractEntityAccessUtil = fineractEntityAccessUtil;
@@ -63,14 +64,14 @@ public class SavingsProductReadPlatformServiceImpl 
implements SavingsProductRead
         this.context.authenticatedUser();
 
         String sql = "select " + this.savingsProductRowMapper.schema() + 
"where sp.deposit_type_enum = ?";
-        
-               // Check if branch specific products are enabled. If yes, fetch 
only products mapped to current user's office
-               String inClause = fineractEntityAccessUtil.
-                               
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
-                                               
FineractEntityType.SAVINGS_PRODUCT);
-               if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
-                       sql += " and sp.id in ( " + inClause + " ) ";
-               }
+
+        // Check if branch specific products are enabled. If yes, fetch only
+        // products mapped to current user's office
+        String inClause = fineractEntityAccessUtil
+                
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
+        if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
+            sql += " and sp.id in ( " + inClause + " ) ";
+        }
 
         return this.jdbcTemplate.query(sql, this.savingsProductRowMapper, new 
Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() });
     }
@@ -79,15 +80,14 @@ public class SavingsProductReadPlatformServiceImpl 
implements SavingsProductRead
     public Collection<SavingsProductData> retrieveAllForLookup() {
 
         String sql = "select " + this.savingsProductLookupsRowMapper.schema() 
+ " where sp.deposit_type_enum = ? ";
-        
-        // Check if branch specific products are enabled. If yes, fetch only 
products mapped to current user's office
-               String inClause = fineractEntityAccessUtil.
-                               
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
-                                               
FineractEntityType.SAVINGS_PRODUCT);
-       if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
-               sql += " and id in ( " + inClause + " ) ";
-       }
-    
+
+        // Check if branch specific products are enabled. If yes, fetch only
+        // products mapped to current user's office
+        String inClause = fineractEntityAccessUtil
+                
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
+        if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
+            sql += " and id in ( " + inClause + " ) ";
+        }
 
         return this.jdbcTemplate.query(sql, 
this.savingsProductLookupsRowMapper,
                 new Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() 
});
@@ -132,9 +132,12 @@ public class SavingsProductReadPlatformServiceImpl 
implements SavingsProductRead
             sqlBuilder.append("sp.min_required_balance as minRequiredBalance, 
");
             sqlBuilder.append("sp.enforce_min_required_balance as 
enforceMinRequiredBalance, ");
             sqlBuilder.append("sp.min_balance_for_interest_calculation as 
minBalanceForInterestCalculation,");
-            sqlBuilder.append("sp.accounting_type as accountingType ");
+            sqlBuilder.append("sp.accounting_type as accountingType, ");
+            sqlBuilder.append("sp.withhold_tax as withHoldTax,");
+            sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
             sqlBuilder.append("from m_savings_product sp ");
             sqlBuilder.append("join m_currency curr on curr.code = 
sp.currency_code ");
+            sqlBuilder.append("left join m_tax_group tg on tg.id = 
sp.tax_group_id  ");
 
             this.schemaSql = sqlBuilder.toString();
         }
@@ -200,11 +203,20 @@ public class SavingsProductReadPlatformServiceImpl 
implements SavingsProductRead
             final boolean enforceMinRequiredBalance = 
rs.getBoolean("enforceMinRequiredBalance");
             final BigDecimal minBalanceForInterestCalculation = 
rs.getBigDecimal("minBalanceForInterestCalculation");
 
+            final boolean withHoldTax = rs.getBoolean("withHoldTax");
+            final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
+            final String taxGroupName = rs.getString("taxGroupName");
+            TaxGroupData taxGroupData = null;
+            if (taxGroupId != null) {
+                taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
+            }
+
             return SavingsProductData.instance(id, name, shortName, 
description, currency, nominalAnnualInterestRate,
                     compoundingInterestPeriodType, interestPostingPeriodType, 
interestCalculationType, interestCalculationDaysInYearType,
                     minRequiredOpeningBalance, lockinPeriodFrequency, 
lockinPeriodFrequencyType, withdrawalFeeForTransfers,
                     accountingRuleType, allowOverdraft, overdraftLimit, 
minRequiredBalance, enforceMinRequiredBalance,
-                    minBalanceForInterestCalculation, 
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
+                    minBalanceForInterestCalculation, 
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, 
withHoldTax,
+                    taxGroupData);
         }
     }
 
@@ -229,22 +241,22 @@ public class SavingsProductReadPlatformServiceImpl 
implements SavingsProductRead
         String sql = "select " + this.savingsProductLookupsRowMapper.schema();
 
         boolean inClauseAdded = false;
-        
-        // Check if branch specific products are enabled. If yes, fetch only 
products mapped to current user's office
-               String inClause = fineractEntityAccessUtil.
-                               
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
-                                               
FineractEntityType.SAVINGS_PRODUCT);
-       if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
-               sql += " where id in ( " + inClause + " ) ";
-               inClauseAdded = true;
-       }
-        
+
+        // Check if branch specific products are enabled. If yes, fetch only
+        // products mapped to current user's office
+        String inClause = fineractEntityAccessUtil
+                
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
+        if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
+            sql += " where id in ( " + inClause + " ) ";
+            inClauseAdded = true;
+        }
+
         if (isOverdraftType != null) {
-               if (inClauseAdded) {
-                       sql += " and sp.allow_overdraft=?";
-               } else {
-                       sql += " where sp.allow_overdraft=?";
-               }
+            if (inClauseAdded) {
+                sql += " and sp.allow_overdraft=?";
+            } else {
+                sql += " where sp.allow_overdraft=?";
+            }
             return this.jdbcTemplate.query(sql, 
this.savingsProductLookupsRowMapper, isOverdraftType);
         }
 
@@ -258,14 +270,14 @@ public class SavingsProductReadPlatformServiceImpl 
implements SavingsProductRead
         this.context.authenticatedUser();
 
         String sql = "select " + this.savingsProductRowMapper.schema() + " 
where sp.currency_code='" + currencyCode + "'";
-        
-        // Check if branch specific products are enabled. If yes, fetch only 
products mapped to current user's office
-               String inClause = fineractEntityAccessUtil.
-                               
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
-                                               
FineractEntityType.SAVINGS_PRODUCT);
-       if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
-               sql += " and id in ( " + inClause + " ) ";
-       }
+
+        // Check if branch specific products are enabled. If yes, fetch only
+        // products mapped to current user's office
+        String inClause = fineractEntityAccessUtil
+                
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
+        if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
+            sql += " and id in ( " + inClause + " ) ";
+        }
 
         return this.jdbcTemplate.query(sql, this.savingsProductRowMapper);
     }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java
index 159d658..83cfb8a 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java
@@ -18,16 +18,23 @@
  */
 package org.apache.fineract.portfolio.savings.service;
 
+import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
 import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
 import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
+import static 
org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import 
org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import 
org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
 import 
org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessType;
 import 
org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityType;
@@ -40,6 +47,7 @@ import 
org.apache.fineract.portfolio.savings.domain.SavingsProduct;
 import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
 import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
 import 
org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
+import org.apache.fineract.portfolio.tax.domain.TaxGroup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -63,8 +71,7 @@ public class 
SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
             final SavingsProductRepository savingProductRepository, final 
SavingsProductDataValidator fromApiJsonDataValidator,
             final SavingsProductAssembler savingsProductAssembler,
             final ProductToGLAccountMappingWritePlatformService 
accountMappingWritePlatformService,
-            final FineractEntityAccessUtil fineractEntityAccessUtil
-            ) {
+            final FineractEntityAccessUtil fineractEntityAccessUtil) {
         this.context = context;
         this.savingProductRepository = savingProductRepository;
         this.fromApiJsonDataValidator = fromApiJsonDataValidator;
@@ -116,13 +123,12 @@ public class 
SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
             // save accounting mappings
             
this.accountMappingWritePlatformService.createSavingProductToGLAccountMapping(product.getId(),
 command,
                     DepositAccountType.SAVINGS_DEPOSIT);
-            
-            // check if the office specific products are enabled. If yes, then 
save this savings product against a specific office
+
+            // check if the office specific products are enabled. If yes, then
+            // save this savings product against a specific office
             // i.e. this savings product is specific for this office.
             
fineractEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(
-                       
FineractEntityAccessType.OFFICE_ACCESS_TO_SAVINGS_PRODUCTS, 
-                       FineractEntityType.SAVINGS_PRODUCT, 
-                       product.getId());
+                    
FineractEntityAccessType.OFFICE_ACCESS_TO_SAVINGS_PRODUCTS, 
FineractEntityType.SAVINGS_PRODUCT, product.getId());
 
             return new CommandProcessingResultBuilder() //
                     .withEntityId(product.getId()) //
@@ -155,6 +161,19 @@ public class 
SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
                 }
             }
 
+            if (changes.containsKey(taxGroupIdParamName)) {
+                final TaxGroup taxGroup = 
this.savingsProductAssembler.assembleTaxGroup(command);
+                product.setTaxGroup(taxGroup);
+                if (product.withHoldTax() && product.getTaxGroup() == null) {
+                    final List<ApiParameterError> dataValidationErrors = new 
ArrayList<>();
+                    final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors)
+                            .resource(SAVINGS_PRODUCT_RESOURCE_NAME);
+                    final Long taxGroupId = null;
+                    
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
+                    throw new 
PlatformApiDataValidationException(dataValidationErrors);
+                }
+            }
+
             // accounting related changes
             final boolean accountingTypeChanged = 
changes.containsKey(accountingRuleParamName);
             final Map<String, Object> accountingMappingChanges = 
this.accountMappingWritePlatformService

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxApiConstants.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxApiConstants.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxApiConstants.java
new file mode 100644
index 0000000..d44b5fe
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxApiConstants.java
@@ -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 {
+
+    public static final String nameParamName = "name";
+    public static final String percentageParamName = "percentage";
+    public static final String debitAccountTypeParamName = "debitAccountType";
+    public static final String debitAcountIdParamName = "debitAcountId";
+    public static final String creditAccountTypeParamName = 
"creditAccountType";
+    public static final String creditAcountIdParamName = "creditAcountId";
+
+    public static final String startDateParamName = "startDate";
+    public static final String endDateParamName = "endDate";
+
+    public static final String chargeIncludesTaxParamName = 
"chargeIncludesTax";
+    public static final String taxComponentsParamName = "taxComponents";
+    public static final String idParamName = "id";
+    public static final String taxComponentIdParamName = "taxComponentId";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxComponentApiResource.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxComponentApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxComponentApiResource.java
new file mode 100644
index 0000000..9d9b5e3
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxComponentApiResource.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.api;
+
+import java.util.Collection;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import 
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import 
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import 
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.tax.data.TaxComponentData;
+import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/taxes/component")
+@Component
+@Scope("singleton")
+public class TaxComponentApiResource {
+
+    private final String resourceNameForPermissions = "TAXCOMPONENT";
+
+    private final PlatformSecurityContext context;
+    private final TaxReadPlatformService readPlatformService;
+    private final DefaultToApiJsonSerializer<TaxComponentData> 
toApiJsonSerializer;
+    private final ApiRequestParameterHelper apiRequestParameterHelper;
+    private final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService;
+
+    @Autowired
+    public TaxComponentApiResource(final PlatformSecurityContext context, 
final TaxReadPlatformService readPlatformService,
+            final DefaultToApiJsonSerializer<TaxComponentData> 
toApiJsonSerializer,
+            final ApiRequestParameterHelper apiRequestParameterHelper,
+            final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService) {
+        this.context = context;
+        this.readPlatformService = readPlatformService;
+        this.toApiJsonSerializer = toApiJsonSerializer;
+        this.apiRequestParameterHelper = apiRequestParameterHelper;
+        this.commandsSourceWritePlatformService = 
commandsSourceWritePlatformService;
+    }
+
+    @GET
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveAllTaxComponents(@Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+        final Collection<TaxComponentData> TaxComponents = 
this.readPlatformService.retrieveAllTaxComponents();
+
+        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        return this.toApiJsonSerializer.serialize(settings, TaxComponents);
+    }
+
+    @GET
+    @Path("{taxComponentId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveTaxComponent(@PathParam("taxComponentId") final Long 
taxComponentId, @Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+
+        TaxComponentData taxComponentData = 
this.readPlatformService.retrieveTaxComponentData(taxComponentId);
+        return this.toApiJsonSerializer.serialize(settings, taxComponentData);
+    }
+
+    @GET
+    @Path("template")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveTemplate(@Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+        final TaxComponentData taxComponentData = 
this.readPlatformService.retrieveTaxComponentTemplate();
+
+        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        return this.toApiJsonSerializer.serialize(settings, taxComponentData);
+    }
+
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String createTaxCompoent(final String apiRequestBodyAsJson) {
+
+        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().createTaxComponent().withJson(apiRequestBodyAsJson).build();
+
+        final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+        return this.toApiJsonSerializer.serialize(result);
+    }
+
+    @PUT
+    @Path("{taxComponentId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String updateTaxCompoent(@PathParam("taxComponentId") final Long 
taxComponentId, final String apiRequestBodyAsJson) {
+
+        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().updateTaxComponent(taxComponentId).withJson(apiRequestBodyAsJson)
+                .build();
+
+        final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+        return this.toApiJsonSerializer.serialize(result);
+    }
+
+}
\ 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/api/TaxGroupApiResource.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxGroupApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxGroupApiResource.java
new file mode 100644
index 0000000..4464500
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/api/TaxGroupApiResource.java
@@ -0,0 +1,142 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.api;
+
+import java.util.Collection;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import 
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import 
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
+import 
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.tax.data.TaxGroupData;
+import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/taxes/group")
+@Component
+@Scope("singleton")
+public class TaxGroupApiResource {
+
+    private final String resourceNameForPermissions = "TAXGROUP";
+
+    private final PlatformSecurityContext context;
+    private final TaxReadPlatformService readPlatformService;
+    private final DefaultToApiJsonSerializer<TaxGroupData> toApiJsonSerializer;
+    private final ApiRequestParameterHelper apiRequestParameterHelper;
+    private final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService;
+
+    @Autowired
+    public TaxGroupApiResource(final PlatformSecurityContext context, final 
TaxReadPlatformService readPlatformService,
+            final DefaultToApiJsonSerializer<TaxGroupData> 
toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
+            final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService) {
+        this.context = context;
+        this.readPlatformService = readPlatformService;
+        this.toApiJsonSerializer = toApiJsonSerializer;
+        this.apiRequestParameterHelper = apiRequestParameterHelper;
+        this.commandsSourceWritePlatformService = 
commandsSourceWritePlatformService;
+    }
+
+    @GET
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveAllTaxGroups(@Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+        final Collection<TaxGroupData> taxGroupDatas = 
this.readPlatformService.retrieveAllTaxGroups();
+
+        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        return this.toApiJsonSerializer.serialize(settings, taxGroupDatas);
+    }
+
+    @GET
+    @Path("{taxGroupId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveTaxGroup(@PathParam("taxGroupId") final Long 
taxGroupId, @Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        TaxGroupData taxGroupData = null;
+        if (settings.isTemplate()) {
+            taxGroupData = 
this.readPlatformService.retrieveTaxGroupWithTemplate(taxGroupId);
+        } else {
+            taxGroupData = 
this.readPlatformService.retrieveTaxGroupData(taxGroupId);
+        }
+        return this.toApiJsonSerializer.serialize(settings, taxGroupData);
+    }
+
+    @GET
+    @Path("template")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveTemplate(@Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+
+        final TaxGroupData taxGroupData = 
this.readPlatformService.retrieveTaxGroupTemplate();
+
+        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        return this.toApiJsonSerializer.serialize(settings, taxGroupData);
+    }
+
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String createTaxGroup(final String apiRequestBodyAsJson) {
+
+        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().createTaxGroup().withJson(apiRequestBodyAsJson).build();
+
+        final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+        return this.toApiJsonSerializer.serialize(result);
+    }
+
+    @PUT
+    @Path("{taxGroupId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String updateTaxGroup(@PathParam("taxGroupId") final Long 
taxGroupId, final String apiRequestBodyAsJson) {
+
+        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().updateTaxGroup(taxGroupId).withJson(apiRequestBodyAsJson).build();
+
+        final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+        return this.toApiJsonSerializer.serialize(result);
+    }
+
+}
\ 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/data/TaxComponentData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java
new file mode 100644
index 0000000..a61ba33
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java
@@ -0,0 +1,112 @@
+/**
+ * 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 java.math.BigDecimal;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.fineract.accounting.glaccount.data.GLAccountData;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.joda.time.LocalDate;
+
+public class TaxComponentData {
+
+    @SuppressWarnings("unused")
+    private final Long id;
+    @SuppressWarnings("unused")
+    private final String name;
+    @SuppressWarnings("unused")
+    private final BigDecimal percentage;
+    @SuppressWarnings("unused")
+    private final EnumOptionData debitAccountType;
+    @SuppressWarnings("unused")
+    private final GLAccountData debitAccount;
+    @SuppressWarnings("unused")
+    private final EnumOptionData creditAccountType;
+    @SuppressWarnings("unused")
+    private final GLAccountData creditAccount;
+    @SuppressWarnings("unused")
+    private final LocalDate startDate;
+    @SuppressWarnings("unused")
+    private final Collection<TaxComponentHistoryData> taxComponentHistories;
+
+    // template options
+    @SuppressWarnings("unused")
+    private final Map<String, List<GLAccountData>> glAccountOptions;
+    @SuppressWarnings("unused")
+    private final Collection<EnumOptionData> glAccountTypeOptions;
+
+    public static TaxComponentData instance(final Long id, final String name, 
final BigDecimal percentage,
+            final EnumOptionData debitAccountType, final GLAccountData 
debitAcount, final EnumOptionData creditAccountType,
+            final GLAccountData creditAcount, final LocalDate startDate, final 
Collection<TaxComponentHistoryData> taxComponentHistories) {
+        final Map<String, List<GLAccountData>> glAccountOptions = null;
+        final Collection<EnumOptionData> glAccountTypeOptions = null;
+        return new TaxComponentData(id, name, percentage, debitAccountType, 
debitAcount, creditAccountType, creditAcount, startDate,
+                taxComponentHistories, glAccountOptions, glAccountTypeOptions);
+    }
+
+    public static TaxComponentData lookup(final Long id, final String name) {
+        final BigDecimal percentage = null;
+        final EnumOptionData debitAccountType = null;
+        final GLAccountData debitAcount = null;
+        final EnumOptionData creditAccountType = null;
+        final GLAccountData creditAcount = null;
+        final LocalDate startDate = null;
+        final Collection<TaxComponentHistoryData> taxComponentHistories = null;
+        final Map<String, List<GLAccountData>> glAccountOptions = null;
+        final Collection<EnumOptionData> glAccountTypeOptions = null;
+        return new TaxComponentData(id, name, percentage, debitAccountType, 
debitAcount, creditAccountType, creditAcount, startDate,
+                taxComponentHistories, glAccountOptions, glAccountTypeOptions);
+    }
+
+    public static TaxComponentData template(final Map<String, 
List<GLAccountData>> glAccountOptions,
+            final Collection<EnumOptionData> glAccountTypeOptions) {
+        final Long id = null;
+        final String name = null;
+        final BigDecimal percentage = null;
+        final EnumOptionData debitAccountType = null;
+        final GLAccountData debitAcount = null;
+        final EnumOptionData creditAccountType = null;
+        final GLAccountData creditAcount = null;
+        final LocalDate startDate = null;
+        final Collection<TaxComponentHistoryData> taxComponentHistories = null;
+        return new TaxComponentData(id, name, percentage, debitAccountType, 
debitAcount, creditAccountType, creditAcount, startDate,
+                taxComponentHistories, glAccountOptions, glAccountTypeOptions);
+    }
+
+    private TaxComponentData(final Long id, final String name, final 
BigDecimal percentage, final EnumOptionData debitAccountType,
+            final GLAccountData debitAcount, final EnumOptionData 
creditAccountType, final GLAccountData creditAcount,
+            final LocalDate startDate, final 
Collection<TaxComponentHistoryData> taxComponentHistories,
+            final Map<String, List<GLAccountData>> glAccountOptions, final 
Collection<EnumOptionData> glAccountTypeOptions) {
+        this.id = id;
+        this.percentage = percentage;
+        this.name = name;
+        this.debitAccountType = debitAccountType;
+        this.debitAccount = debitAcount;
+        this.creditAccountType = creditAccountType;
+        this.creditAccount = creditAcount;
+        this.startDate = startDate;
+        this.taxComponentHistories = taxComponentHistories;
+        this.glAccountOptions = glAccountOptions;
+        this.glAccountTypeOptions = glAccountTypeOptions;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/3015747f/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
new file mode 100644
index 0000000..4007566
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
@@ -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.data;
+
+import java.math.BigDecimal;
+
+import org.joda.time.LocalDate;
+
+public class TaxComponentHistoryData {
+
+    @SuppressWarnings("unused")
+    private final BigDecimal percentage;
+    @SuppressWarnings("unused")
+    private final LocalDate startDate;
+    @SuppressWarnings("unused")
+    private final LocalDate endDate;
+
+    public TaxComponentHistoryData(final BigDecimal percentage, final 
LocalDate startDate, final LocalDate endDate) {
+        this.percentage = percentage;
+        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/data/TaxGroupData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java
new file mode 100644
index 0000000..78a35f1
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.tax.data;
+
+import java.util.Collection;
+
+public class TaxGroupData {
+
+    private final Long id;
+    private final String name;
+    private final Collection<TaxGroupMappingsData> taxAssociations;
+
+    // Template options
+    @SuppressWarnings("unused")
+    private final Collection<TaxComponentData> taxComponents;
+
+    public static TaxGroupData instance(final Long id, final String name, 
final Collection<TaxGroupMappingsData> taxAssociations) {
+        final Collection<TaxComponentData> taxComponents = null;
+        return new TaxGroupData(id, name, taxAssociations, taxComponents);
+    }
+
+    public static TaxGroupData lookup(final Long id, final String name) {
+        final Collection<TaxComponentData> taxComponents = null;
+        final Collection<TaxGroupMappingsData> taxAssociations = null;
+        return new TaxGroupData(id, name, taxAssociations, taxComponents);
+    }
+
+    public static TaxGroupData template(final Collection<TaxComponentData> 
taxComponents) {
+        final Long id = null;
+        final String name = null;
+        final Collection<TaxGroupMappingsData> taxAssociations = null;
+        return new TaxGroupData(id, name, taxAssociations, taxComponents);
+    }
+
+    public static TaxGroupData template(final TaxGroupData taxGroupData, final 
Collection<TaxComponentData> taxComponents) {
+        return new TaxGroupData(taxGroupData.id, taxGroupData.name, 
taxGroupData.taxAssociations, taxComponents);
+    }
+
+    private TaxGroupData(final Long id, final String name, final 
Collection<TaxGroupMappingsData> taxAssociations,
+            final Collection<TaxComponentData> taxComponents) {
+        this.id = id;
+        this.name = name;
+        this.taxAssociations = taxAssociations;
+        this.taxComponents = taxComponents;
+    }
+
+}


Reply via email to