001/* 002 * Copyright 2008 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 */ 016package org.kuali.ole.sys.service.impl; 017 018import java.math.BigDecimal; 019import java.sql.Date; 020import java.util.ArrayList; 021import java.util.List; 022 023import org.apache.commons.lang.StringUtils; 024import org.kuali.ole.fp.businessobject.SalesTax; 025import org.kuali.ole.sys.businessobject.TaxDetail; 026import org.kuali.ole.sys.businessobject.TaxRegion; 027import org.kuali.ole.sys.service.TaxRegionService; 028import org.kuali.ole.sys.service.TaxService; 029import org.kuali.rice.core.api.util.type.KualiDecimal; 030import org.kuali.rice.coreservice.framework.parameter.ParameterService; 031import org.kuali.rice.krad.util.ObjectUtils; 032import org.springframework.transaction.annotation.Transactional; 033 034@Transactional 035public class TaxServiceImpl implements TaxService { 036 037 protected static final String POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE = "POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE"; 038 039 protected TaxRegionService taxRegionService; 040 protected ParameterService parameterService; 041 042 /** 043 * @see org.kuali.ole.sys.service.TaxService#getSalesTaxDetails(java.lang.String, java.lang.String, 044 * org.kuali.rice.core.api.util.type.KualiDecimal) 045 */ 046 @Override 047 public List<TaxDetail> getSalesTaxDetails(Date dateOfTransaction, String postalCode, KualiDecimal amount) { 048 List<TaxDetail> salesTaxDetails = new ArrayList<TaxDetail>(); 049 050 if (StringUtils.isNotEmpty(postalCode)) { 051 List<TaxRegion> salesTaxRegions = taxRegionService.getSalesTaxRegions(postalCode); 052 TaxDetail newTaxDetail = null; 053 for (TaxRegion taxRegion : salesTaxRegions) { 054 if (taxRegion.isActive()) { 055 newTaxDetail = populateTaxDetail(taxRegion, dateOfTransaction, amount); 056 salesTaxDetails.add(newTaxDetail); 057 } 058 } 059 } 060 061 return salesTaxDetails; 062 } 063 064 /** 065 * @see org.kuali.ole.sys.service.TaxService#getUseTaxDetails(java.lang.String, java.lang.String, 066 * org.kuali.rice.core.api.util.type.KualiDecimal) 067 */ 068 @Override 069 public List<TaxDetail> getUseTaxDetails(Date dateOfTransaction, String postalCode, KualiDecimal amount) { 070 List<TaxDetail> useTaxDetails = new ArrayList<TaxDetail>(); 071 072 if (StringUtils.isNotEmpty(postalCode)) { 073 // strip digits from the postal code before passing it to the sales tax regions 074 // if the parameters indicate to do so. 075 postalCode = truncatePostalCodeForSalesTaxRegionService(postalCode); 076 077 for (TaxRegion taxRegion : taxRegionService.getUseTaxRegions(postalCode)) { 078 useTaxDetails.add(populateTaxDetail(taxRegion, dateOfTransaction, amount)); 079 } 080 } 081 082 return useTaxDetails; 083 } 084 085 /** 086 * @see org.kuali.ole.sys.service.TaxService#getTotalSalesTaxAmount(java.lang.String, java.lang.String, 087 * org.kuali.rice.core.api.util.type.KualiDecimal) 088 */ 089 @Override 090 public KualiDecimal getTotalSalesTaxAmount(Date dateOfTransaction, String postalCode, KualiDecimal amount) { 091 KualiDecimal totalSalesTaxAmount = KualiDecimal.ZERO; 092 093 if (StringUtils.isNotEmpty(postalCode)) { 094 095 // strip digits from the postal code before passing it to the sales tax regions 096 // if the parameters indicate to do so. 097 postalCode = truncatePostalCodeForSalesTaxRegionService(postalCode); 098 099 List<TaxDetail> salesTaxDetails = getSalesTaxDetails(dateOfTransaction, postalCode, amount); 100 KualiDecimal newTaxAmount = KualiDecimal.ZERO; 101 for (TaxDetail taxDetail : salesTaxDetails) { 102 newTaxAmount = taxDetail.getTaxAmount(); 103 totalSalesTaxAmount = totalSalesTaxAmount.add(newTaxAmount); 104 } 105 } 106 107 return totalSalesTaxAmount; 108 } 109 110 /** 111 * This method returns a preTax amount 112 * 113 * @param dateOfTransaction 114 * @param postalCode 115 * @param amountWithTax 116 * @return 117 */ 118 119 @Override 120 public KualiDecimal getPretaxAmount(Date dateOfTransaction, String postalCode, KualiDecimal amountWithTax) { 121 BigDecimal totalTaxRate = BigDecimal.ZERO; 122 123 // there is not tax amount 124 if (StringUtils.isEmpty(postalCode)) 125 return amountWithTax; 126 127 // strip digits from the postal code before passing it to the sales tax regions 128 // if the parameters indicate to do so. 129 postalCode = truncatePostalCodeForSalesTaxRegionService(postalCode); 130 131 List<TaxRegion> salesTaxRegions = taxRegionService.getSalesTaxRegions(postalCode); 132 if (salesTaxRegions.size() == 0) 133 return amountWithTax; 134 135 for (TaxRegion taxRegion : salesTaxRegions) { 136 if (ObjectUtils.isNotNull((taxRegion.getEffectiveTaxRegionRate(dateOfTransaction)))) 137 totalTaxRate = totalTaxRate.add(taxRegion.getEffectiveTaxRegionRate(dateOfTransaction).getTaxRate()); 138 } 139 140 KualiDecimal divisor = new KualiDecimal(totalTaxRate.add(BigDecimal.ONE)); 141 KualiDecimal pretaxAmount = amountWithTax.divide(divisor); 142 143 return pretaxAmount; 144 } 145 146 /** 147 * This method returns a populated Tax Detail BO based on the Tax Region BO and amount 148 * 149 * @param taxRegion 150 * @param amount 151 * @return 152 */ 153 protected TaxDetail populateTaxDetail(TaxRegion taxRegion, Date dateOfTransaction, KualiDecimal amount) { 154 TaxDetail taxDetail = new TaxDetail(); 155 taxDetail.setAccountNumber(taxRegion.getAccountNumber()); 156 taxDetail.setChartOfAccountsCode(taxRegion.getChartOfAccountsCode()); 157 taxDetail.setFinancialObjectCode(taxRegion.getFinancialObjectCode()); 158 taxDetail.setRateCode(taxRegion.getTaxRegionCode()); 159 taxDetail.setRateName(taxRegion.getTaxRegionName()); 160 taxDetail.setTypeCode(taxRegion.getTaxRegionTypeCode()); 161 if (ObjectUtils.isNotNull((taxRegion.getEffectiveTaxRegionRate(dateOfTransaction)))) { 162 taxDetail.setTaxRate(taxRegion.getEffectiveTaxRegionRate(dateOfTransaction).getTaxRate()); 163 if (amount != null) { 164 taxDetail.setTaxAmount(new KualiDecimal((amount.bigDecimalValue().multiply(taxDetail.getTaxRate())).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR))); 165 } 166 } 167 return taxDetail; 168 } 169 170 protected String truncatePostalCodeForSalesTaxRegionService(String postalCode) { 171 Integer digitsToUse = postalCodeDigitsToUse(); 172 if (digitsToUse != null) { 173 return postalCode.substring(0, digitsToUse.intValue()); 174 } else { 175 return postalCode; // unchanged 176 } 177 } 178 179 protected Integer postalCodeDigitsToUse() { 180 String digits = parameterService.getParameterValueAsString(SalesTax.class, 181 POSTAL_CODE_DIGITS_PASSED_TO_SALES_TAX_REGION_SERVICE); 182 if (StringUtils.isBlank(digits)) { return null; } 183 Integer digitsToUse; 184 try { 185 digitsToUse = new Integer(digits); 186 } 187 catch (NumberFormatException ex) { 188 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); 189 } 190 return digitsToUse; 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}