001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.sys.service.impl; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.util.ArrayList; 021 import java.util.List; 022 023 import org.apache.commons.lang.StringUtils; 024 import org.kuali.kfs.fp.businessobject.SalesTax; 025 import org.kuali.kfs.sys.businessobject.TaxDetail; 026 import org.kuali.kfs.sys.businessobject.TaxRegion; 027 import org.kuali.kfs.sys.service.TaxRegionService; 028 import org.kuali.kfs.sys.service.TaxService; 029 import org.kuali.rice.kns.service.ParameterService; 030 import org.kuali.rice.kns.util.KualiDecimal; 031 import org.kuali.rice.kns.util.ObjectUtils; 032 import org.springframework.transaction.annotation.Transactional; 033 034 @Transactional 035 public class TaxServiceImpl implements TaxService { 036 037 private static final String POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE = "POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE"; 038 039 private TaxRegionService taxRegionService; 040 private ParameterService parameterService; 041 042 /** 043 * @see org.kuali.kfs.sys.service.TaxService#getSalesTaxDetails(java.lang.String, java.lang.String, 044 * org.kuali.rice.kns.util.KualiDecimal) 045 */ 046 public List<TaxDetail> getSalesTaxDetails(Date dateOfTransaction, String postalCode, KualiDecimal amount) { 047 List<TaxDetail> salesTaxDetails = new ArrayList<TaxDetail>(); 048 049 if (StringUtils.isNotEmpty(postalCode)) { 050 List<TaxRegion> salesTaxRegions = taxRegionService.getSalesTaxRegions(postalCode); 051 TaxDetail newTaxDetail = null; 052 for (TaxRegion taxRegion : salesTaxRegions) { 053 if (taxRegion.isActive()) { 054 newTaxDetail = populateTaxDetail(taxRegion, dateOfTransaction, amount); 055 salesTaxDetails.add(newTaxDetail); 056 } 057 } 058 } 059 060 return salesTaxDetails; 061 } 062 063 /** 064 * @see org.kuali.kfs.sys.service.TaxService#getUseTaxDetails(java.lang.String, java.lang.String, 065 * org.kuali.rice.kns.util.KualiDecimal) 066 */ 067 public List<TaxDetail> getUseTaxDetails(Date dateOfTransaction, String postalCode, KualiDecimal amount) { 068 List<TaxDetail> useTaxDetails = new ArrayList<TaxDetail>(); 069 070 if (StringUtils.isNotEmpty(postalCode)) { 071 // strip digits from the postal code before passing it to the sales tax regions 072 // if the parameters indicate to do so. 073 postalCode = truncatePostalCodeForSalesTaxRegionService(postalCode); 074 075 for (TaxRegion taxRegion : taxRegionService.getUseTaxRegions(postalCode)) { 076 useTaxDetails.add(populateTaxDetail(taxRegion, dateOfTransaction, amount)); 077 } 078 } 079 080 return useTaxDetails; 081 } 082 083 /** 084 * @see org.kuali.kfs.sys.service.TaxService#getTotalSalesTaxAmount(java.lang.String, java.lang.String, 085 * org.kuali.rice.kns.util.KualiDecimal) 086 */ 087 public KualiDecimal getTotalSalesTaxAmount(Date dateOfTransaction, String postalCode, KualiDecimal amount) { 088 KualiDecimal totalSalesTaxAmount = KualiDecimal.ZERO; 089 090 if (StringUtils.isNotEmpty(postalCode)) { 091 092 // strip digits from the postal code before passing it to the sales tax regions 093 // if the parameters indicate to do so. 094 postalCode = truncatePostalCodeForSalesTaxRegionService(postalCode); 095 096 List<TaxDetail> salesTaxDetails = getSalesTaxDetails(dateOfTransaction, postalCode, amount); 097 KualiDecimal newTaxAmount = KualiDecimal.ZERO; 098 for (TaxDetail taxDetail : salesTaxDetails) { 099 newTaxAmount = taxDetail.getTaxAmount(); 100 totalSalesTaxAmount = totalSalesTaxAmount.add(newTaxAmount); 101 } 102 } 103 104 return totalSalesTaxAmount; 105 } 106 107 /** 108 * This method returns a preTax amount 109 * 110 * @param dateOfTransaction 111 * @param postalCode 112 * @param amountWithTax 113 * @return 114 */ 115 116 public KualiDecimal getPretaxAmount(Date dateOfTransaction, String postalCode, KualiDecimal amountWithTax) { 117 BigDecimal totalTaxRate = BigDecimal.ZERO; 118 119 // there is not tax amount 120 if (StringUtils.isEmpty(postalCode)) 121 return amountWithTax; 122 123 // strip digits from the postal code before passing it to the sales tax regions 124 // if the parameters indicate to do so. 125 postalCode = truncatePostalCodeForSalesTaxRegionService(postalCode); 126 127 List<TaxRegion> salesTaxRegions = taxRegionService.getSalesTaxRegions(postalCode); 128 if (salesTaxRegions.size() == 0) 129 return amountWithTax; 130 131 for (TaxRegion taxRegion : salesTaxRegions) { 132 if (ObjectUtils.isNotNull((taxRegion.getEffectiveTaxRegionRate(dateOfTransaction)))) 133 totalTaxRate = totalTaxRate.add(taxRegion.getEffectiveTaxRegionRate(dateOfTransaction).getTaxRate()); 134 } 135 136 KualiDecimal divisor = new KualiDecimal(totalTaxRate.add(BigDecimal.ONE)); 137 KualiDecimal pretaxAmount = amountWithTax.divide(divisor); 138 139 return pretaxAmount; 140 } 141 142 /** 143 * This method returns a populated Tax Detail BO based on the Tax Region BO and amount 144 * 145 * @param taxRegion 146 * @param amount 147 * @return 148 */ 149 protected TaxDetail populateTaxDetail(TaxRegion taxRegion, Date dateOfTransaction, KualiDecimal amount) { 150 TaxDetail taxDetail = new TaxDetail(); 151 taxDetail.setAccountNumber(taxRegion.getAccountNumber()); 152 taxDetail.setChartOfAccountsCode(taxRegion.getChartOfAccountsCode()); 153 taxDetail.setFinancialObjectCode(taxRegion.getFinancialObjectCode()); 154 taxDetail.setRateCode(taxRegion.getTaxRegionCode()); 155 taxDetail.setRateName(taxRegion.getTaxRegionName()); 156 taxDetail.setTypeCode(taxRegion.getTaxRegionTypeCode()); 157 if (ObjectUtils.isNotNull((taxRegion.getEffectiveTaxRegionRate(dateOfTransaction)))) { 158 taxDetail.setTaxRate(taxRegion.getEffectiveTaxRegionRate(dateOfTransaction).getTaxRate()); 159 if (amount != null) { 160 taxDetail.setTaxAmount(new KualiDecimal((amount.bigDecimalValue().multiply(taxDetail.getTaxRate())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 161 } 162 } 163 return taxDetail; 164 } 165 166 protected String truncatePostalCodeForSalesTaxRegionService(String postalCode) { 167 Integer digitsToUse = postalCodeDigitsToUse(); 168 if (digitsToUse != null) { 169 return postalCode.substring(0, digitsToUse.intValue()); 170 } else { 171 return postalCode; // unchanged 172 } 173 } 174 175 protected Integer postalCodeDigitsToUse() { 176 String digits = parameterService.getParameterValue(SalesTax.class, 177 POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE); 178 if (StringUtils.isBlank(digits)) { return null; } 179 Integer digitsToUse; 180 try { 181 digitsToUse = new Integer(digits); 182 } 183 catch (NumberFormatException ex) { 184 throw new RuntimeException("The value returned for Parameter " + POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE + " was non-numeric and cannot be processed.", ex); 185 } 186 return digitsToUse; 187 } 188 189 public TaxRegionService getTaxRegionService() { 190 return taxRegionService; 191 } 192 193 public void setTaxRegionService(TaxRegionService taxRegionService) { 194 this.taxRegionService = taxRegionService; 195 } 196 197 public void setParameterService(ParameterService parameterService) { 198 this.parameterService = parameterService; 199 } 200 }