KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > accounting > tax > SimpleTaxServices


1 /*
2  * $Id: OrderServices.java 6133 2005-11-17 03:56:16Z jaz $
3  *
4  * Copyright (c) 2001-2005 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */

24 package org.ofbiz.accounting.tax;
25
26 import java.sql.Timestamp JavaDoc;
27 import java.text.DecimalFormat JavaDoc;
28 import java.text.ParseException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35
36 import javolution.util.FastSet;
37
38 import org.ofbiz.base.util.Debug;
39 import org.ofbiz.base.util.GeneralException;
40 import org.ofbiz.base.util.UtilDateTime;
41 import org.ofbiz.base.util.UtilMisc;
42 import org.ofbiz.base.util.UtilProperties;
43 import org.ofbiz.base.util.UtilValidate;
44 import org.ofbiz.entity.GenericDelegator;
45 import org.ofbiz.entity.GenericEntityException;
46 import org.ofbiz.entity.GenericValue;
47 import org.ofbiz.entity.condition.EntityCondition;
48 import org.ofbiz.entity.condition.EntityConditionList;
49 import org.ofbiz.entity.condition.EntityExpr;
50 import org.ofbiz.entity.condition.EntityOperator;
51 import org.ofbiz.entity.util.EntityUtil;
52 import org.ofbiz.service.DispatchContext;
53
54 /**
55  * Order Processing Services
56  *
57  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
58  * @author <a HREF="mailto:jonesde@ofbiz.org">David E. Jones</a>
59  * @version $Rev: 6202 $
60  * @since 2.0
61  */

62
63 public class SimpleTaxServices {
64
65     public static final String JavaDoc module = SimpleTaxServices.class.getName();
66
67     /** Null tax calc service. */
68     public static Map JavaDoc nullTaxCalc(DispatchContext dctx, Map JavaDoc context) {
69         return UtilMisc.toMap("orderAdjustments", UtilMisc.toList(null), "itemAdjustments", UtilMisc.toList(null));
70     }
71
72     /** Simple tax calc service. */
73     public static Map JavaDoc simpleTaxCalc(DispatchContext dctx, Map JavaDoc context) {
74         GenericDelegator delegator = dctx.getDelegator();
75         String JavaDoc productStoreId = (String JavaDoc) context.get("productStoreId");
76         String JavaDoc billToPartyId = (String JavaDoc) context.get("billToPartyId");
77         List JavaDoc itemProductList = (List JavaDoc) context.get("itemProductList");
78         List JavaDoc itemAmountList = (List JavaDoc) context.get("itemAmountList");
79         List JavaDoc itemPriceList = (List JavaDoc) context.get("itemPriceList");
80         List JavaDoc itemShippingList = (List JavaDoc) context.get("itemShippingList");
81         Double JavaDoc orderShippingAmount = (Double JavaDoc) context.get("orderShippingAmount");
82         GenericValue shippingAddress = (GenericValue) context.get("shippingAddress");
83         //GenericValue userLogin = (GenericValue) context.get("userLogin");
84
//Locale locale = (Locale) context.get("locale");
85

86         // Simple Tax Calc only uses the state from the address and the SalesTaxLookup entity.
87

88         String JavaDoc countryCode = null;
89         String JavaDoc stateCode = null;
90
91         if (shippingAddress != null) {
92             countryCode = shippingAddress.getString("countryGeoId");
93             stateCode = shippingAddress.getString("stateProvinceGeoId");
94         }
95
96         // Setup the return lists.
97
List JavaDoc orderAdjustments = new ArrayList JavaDoc();
98         List JavaDoc itemAdjustments = new ArrayList JavaDoc();
99
100         // Loop through the products; get the taxCategory; and lookup each in the cache.
101
for (int i = 0; i < itemProductList.size(); i++) {
102             GenericValue product = (GenericValue) itemProductList.get(i);
103             Double JavaDoc itemAmount = (Double JavaDoc) itemAmountList.get(i);
104             Double JavaDoc itemPrice = (Double JavaDoc) itemPriceList.get(i);
105             Double JavaDoc shippingAmount = (Double JavaDoc) itemShippingList.get(i);
106             List JavaDoc taxList = null;
107             if (shippingAddress != null) {
108                 taxList = getTaxAmount(delegator, product, productStoreId, billToPartyId, countryCode, stateCode, itemPrice.doubleValue(), itemAmount.doubleValue(), shippingAmount.doubleValue());
109             }
110             itemAdjustments.add(taxList);
111         }
112         if (orderShippingAmount.doubleValue() > 0) {
113             List JavaDoc taxList = getTaxAmount(delegator, null, productStoreId, billToPartyId, countryCode, stateCode, 0.00, 0.00, orderShippingAmount.doubleValue());
114             orderAdjustments.addAll(taxList);
115         }
116
117         Map JavaDoc result = UtilMisc.toMap("orderAdjustments", orderAdjustments, "itemAdjustments", itemAdjustments);
118
119         return result;
120     }
121
122     private static List JavaDoc getTaxAmount(GenericDelegator delegator, GenericValue item, String JavaDoc productStoreId, String JavaDoc billToPartyId, String JavaDoc countryCode, String JavaDoc stateCode, double itemPrice, double itemAmount, double shippingAmount) {
123         Timestamp JavaDoc nowTimestamp = UtilDateTime.nowTimestamp();
124         List JavaDoc adjustments = new ArrayList JavaDoc();
125
126         // store expr
127
EntityCondition storeCond = new EntityExpr("productStoreId", EntityOperator.EQUALS, productStoreId);
128
129         // build the country expressions
130
List JavaDoc countryExprs = UtilMisc.toList(new EntityExpr("countryGeoId", EntityOperator.EQUALS, countryCode), new EntityExpr("countryGeoId", EntityOperator.EQUALS, "_NA_"));
131         EntityCondition countryCond = new EntityConditionList(countryExprs, EntityOperator.OR);
132
133         // build the state expression
134
List JavaDoc stateExprs = UtilMisc.toList(new EntityExpr("stateProvinceGeoId", EntityOperator.EQUALS, stateCode), new EntityExpr("stateProvinceGeoId", EntityOperator.EQUALS, "_NA_"));
135         EntityCondition stateCond = new EntityConditionList(stateExprs, EntityOperator.OR);
136
137         // build the tax cat expression
138
List JavaDoc taxCatExprs = UtilMisc.toList(new EntityExpr("taxCategory", EntityOperator.EQUALS, "_NA_"));
139         if (item != null && item.get("taxCategory") != null) {
140             taxCatExprs.add(new EntityExpr("taxCategory", EntityOperator.EQUALS, item.getString("taxCategory")));
141         }
142         EntityCondition taxCatCond = new EntityConditionList(taxCatExprs, EntityOperator.OR);
143
144         // build the main condition clause
145
List JavaDoc mainExprs = UtilMisc.toList(storeCond, countryCond, stateCond);
146         if (taxCatExprs.size() > 1) {
147             mainExprs.add(taxCatCond);
148         } else {
149             mainExprs.add(taxCatExprs.get(0));
150         }
151         EntityCondition mainCondition = new EntityConditionList(mainExprs, EntityOperator.AND);
152
153         // create the orderby clause
154
List JavaDoc orderList = UtilMisc.toList("minItemPrice", "minPurchase", "fromDate");
155
156         try {
157             List JavaDoc lookupList = delegator.findByCondition("SimpleSalesTaxLookup", mainCondition, null, orderList);
158             List JavaDoc filteredList = EntityUtil.filterByDate(lookupList);
159
160             if (filteredList.size() == 0) {
161                 Debug.logWarning("SimpleTaxCalc: No State/TaxCategory pair found (with or without taxCat).", module);
162                 return adjustments;
163             }
164
165             // find the right entry(s) based on purchase amount
166
Iterator JavaDoc flIt = filteredList.iterator();
167             while (flIt.hasNext()) {
168                 GenericValue taxLookup = (GenericValue) flIt.next();
169                 double minPrice = taxLookup.get("minItemPrice") != null ? taxLookup.getDouble("minItemPrice").doubleValue() : 0.00;
170                 double minAmount = taxLookup.get("minPurchase") != null ? taxLookup.getDouble("minPurchase").doubleValue() : 0.00;
171
172                 // DEJ20050528 not sure why this is done like this, could put this condition in the query sent to the database, though perhaps it is this way because of issues with that of some sort?
173
if (itemPrice >= minPrice && itemAmount >= minAmount) {
174                     double taxRate = taxLookup.get("salesTaxPercentage") != null ? taxLookup.getDouble("salesTaxPercentage").doubleValue() : 0;
175                     double taxable = 0.00;
176
177                     if (item != null && (item.get("taxable") == null || (item.get("taxable") != null && item.getBoolean("taxable").booleanValue()))) {
178                         taxable += itemAmount;
179                     }
180                     if (taxLookup != null && (taxLookup.get("taxShipping") == null || (taxLookup.get("taxShipping") != null && taxLookup.getBoolean("taxShipping").booleanValue()))) {
181                         taxable += shippingAmount;
182                     }
183
184                     // TODO: DEJ20050528 this is an interesting way to round the number, according to the JavaDoc
185
//this uses the "ROUND_HALF_EVEN" method (as defined in the BigDecimal class, see JavaDoc
186
//of that for details); it seems we might want to use the ROUND_HALF_UP method...
187
String JavaDoc currencyFormat = UtilProperties.getPropertyValue("general.properties", "currency.decimal.format", "##0.00");
188                     DecimalFormat JavaDoc formatter = new DecimalFormat JavaDoc(currencyFormat);
189                     double taxTotal = taxable * taxRate;
190                     String JavaDoc amountStr = formatter.format(taxTotal);
191                     Double JavaDoc taxAmount = null;
192                     try {
193                         taxAmount = new Double JavaDoc(formatter.parse(amountStr).doubleValue());
194                     } catch (ParseException JavaDoc e) {
195                         throw new GeneralException("Problem getting parsed amount from string", e);
196                     }
197
198                     String JavaDoc primaryGeoId = taxLookup.getString("stateProvinceGeoId");
199                     String JavaDoc secondaryGeoId = taxLookup.getString("countryGeoId");
200                     String JavaDoc taxAuthPartyId = taxLookup.getString("taxAuthPartyId");
201                     String JavaDoc taxAuthGlAccountId = taxLookup.getString("taxAuthGlAccountId");
202
203                     // if no state/province, the country is the primary
204
if (primaryGeoId == null || "_NA_".equals(primaryGeoId)) {
205                         primaryGeoId = secondaryGeoId;
206                         secondaryGeoId = null;
207                     }
208
209                     Map JavaDoc adjMap = new HashMap JavaDoc();
210                     adjMap.put("amount", taxAmount);
211                     adjMap.put("sourcePercentage", new Double JavaDoc(taxRate));
212                     adjMap.put("orderAdjustmentTypeId", "SALES_TAX");
213                     // the primary Geo should be the main jurisdiction that the tax is for, and the secondary would just be to define a parent or wrapping jurisdiction of the primary
214
adjMap.put("primaryGeoId", primaryGeoId);
215                     if (secondaryGeoId != null) adjMap.put("secondaryGeoId", secondaryGeoId);
216                     adjMap.put("comments", taxLookup.getString("description"));
217                     if (taxAuthPartyId != null) adjMap.put("taxAuthPartyId", taxAuthPartyId);
218                     if (taxAuthGlAccountId != null) adjMap.put("overrideGlAccountId", taxAuthGlAccountId);
219                     if (primaryGeoId != null) adjMap.put("taxAuthGeoId", primaryGeoId);
220
221                     // check to see if this party has a tax ID for this, and if the party is tax exempt in the primary (most-local) jurisdiction
222
if (UtilValidate.isNotEmpty(billToPartyId) && primaryGeoId != null) {
223                         // see if partyId is a member of any groups , if so honor their tax exemptions
224
// look for PartyRelationship with partyRelationshipTypeId=GROUP_ROLLUP, the partyIdTo is the group member, so the partyIdFrom is the groupPartyId
225
Set JavaDoc billToPartyIdSet = FastSet.newInstance();
226                         billToPartyIdSet.add(billToPartyId);
227                         List JavaDoc partyRelationshipList = EntityUtil.filterByDate(delegator.findByAndCache("PartyRelationship", UtilMisc.toMap("partyIdTo", billToPartyId, "partyRelationshipTypeId", "GROUP_ROLLUP")), true);
228                         Iterator JavaDoc partyRelationshipIter = partyRelationshipList.iterator();
229                         while (partyRelationshipIter.hasNext()) {
230                             GenericValue partyRelationship = (GenericValue) partyRelationshipIter.next();
231                             billToPartyIdSet.add(partyRelationship.get("partyIdFrom"));
232                         }
233
234                         List JavaDoc ptiConditionList = UtilMisc.toList(
235                                 new EntityExpr("partyId", EntityOperator.IN, billToPartyIdSet),
236                                 new EntityExpr("taxAuthGeoId", EntityOperator.EQUALS, primaryGeoId));
237                         ptiConditionList.add(new EntityExpr("fromDate", EntityOperator.LESS_THAN_EQUAL_TO, nowTimestamp));
238                         ptiConditionList.add(new EntityExpr(new EntityExpr("thruDate", EntityOperator.EQUALS, null), EntityOperator.OR, new EntityExpr("thruDate", EntityOperator.GREATER_THAN, nowTimestamp)));
239                         EntityCondition ptiCondition = new EntityConditionList(ptiConditionList, EntityOperator.AND);
240                         // sort by -fromDate to get the newest (largest) first, just in case there is more than one, we only want the most recent valid one, should only be one per jurisdiction...
241
List JavaDoc partyTaxAuthInfos = delegator.findByCondition("PartyTaxAuthInfo", ptiCondition, null, UtilMisc.toList("-fromDate"));
242                         if (partyTaxAuthInfos.size() > 0) {
243                             GenericValue partyTaxAuthInfo = (GenericValue) partyTaxAuthInfos.get(0);
244                             adjMap.put("customerReferenceId", partyTaxAuthInfo.get("partyTaxId"));
245                             if ("Y".equals(partyTaxAuthInfo.getString("isExempt"))) {
246                                 adjMap.put("amount", new Double JavaDoc(0));
247                                 adjMap.put("exemptAmount", taxAmount);
248                             }
249                         }
250                     } else {
251                         Debug.logInfo("NOTE: A tax calculation was done without a billToPartyId or primaryGeoId, so no tax exemptions or tax IDs considered; billToPartyId=[" + billToPartyId + "] primaryGeoId=[" + primaryGeoId + "]", module);
252                     }
253
254                     adjustments.add(delegator.makeValue("OrderAdjustment", adjMap));
255                 }
256             }
257         } catch (GenericEntityException e) {
258             Debug.logError(e, "Problems looking up tax rates", module);
259             return new ArrayList JavaDoc();
260         } catch (GeneralException e) {
261             Debug.logError(e, "Problems looking up tax rates", module);
262             return new ArrayList JavaDoc();
263         }
264
265         return adjustments;
266     }
267 }
268
Popular Tags