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 }