KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > order > thirdparty > zipsales > ZipSalesServices


1 /*
2  * $Id: ZipSalesServices.java 5462 2005-08-05 18:35:48Z jonesde $
3  *
4  * Copyright (c) 2001-2003 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.order.thirdparty.zipsales;
25
26 import java.net.URL JavaDoc;
27 import java.sql.Timestamp JavaDoc;
28 import java.text.DecimalFormat JavaDoc;
29 import java.text.ParseException JavaDoc;
30 import java.text.SimpleDateFormat JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Locale JavaDoc;
35 import java.util.Map JavaDoc;
36
37 import org.ofbiz.base.util.Debug;
38 import org.ofbiz.base.util.GeneralException;
39 import org.ofbiz.base.util.StringUtil;
40 import org.ofbiz.base.util.UtilHttp;
41 import org.ofbiz.base.util.UtilMisc;
42 import org.ofbiz.base.util.UtilProperties;
43 import org.ofbiz.base.util.UtilURL;
44 import org.ofbiz.datafile.DataFile;
45 import org.ofbiz.datafile.DataFileException;
46 import org.ofbiz.datafile.Record;
47 import org.ofbiz.datafile.RecordIterator;
48 import org.ofbiz.entity.GenericDelegator;
49 import org.ofbiz.entity.GenericEntityException;
50 import org.ofbiz.entity.GenericValue;
51 import org.ofbiz.entity.util.EntityUtil;
52 import org.ofbiz.security.Security;
53 import org.ofbiz.service.DispatchContext;
54 import org.ofbiz.service.ServiceUtil;
55
56 /**
57  * Zip-Sales Database Services
58  *
59  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
60  * @version $Rev: 5462 $
61  * @since 3.0
62  */

63 public class ZipSalesServices {
64
65     public static final String JavaDoc module = ZipSalesServices.class.getName();
66     public static final String JavaDoc dataFile = "org/ofbiz/order/thirdparty/zipsales/ZipSalesTaxTables.xml";
67     public static final String JavaDoc flatTable = "FlatTaxTable";
68     public static final String JavaDoc ruleTable = "FreightRuleTable";
69     public static final String JavaDoc resource_error = "OrderErrorUiLabels";
70
71     // number formatting
72
private static String JavaDoc curFmtStr = UtilProperties.getPropertyValue("general.properties", "currency.decimal.format", "##0.00");
73     private static DecimalFormat JavaDoc curFormat = new DecimalFormat JavaDoc(curFmtStr);
74
75     // date formatting
76
private static SimpleDateFormat JavaDoc dateFormat = new SimpleDateFormat JavaDoc("yyyyMMdd");
77
78     // import table service
79
public static Map JavaDoc importFlatTable(DispatchContext dctx, Map JavaDoc context) {
80         GenericDelegator delegator = dctx.getDelegator();
81         Security security = dctx.getSecurity();
82         GenericValue userLogin = (GenericValue) context.get("userLogin");
83         String JavaDoc taxFileLocation = (String JavaDoc) context.get("taxFileLocation");
84         String JavaDoc ruleFileLocation = (String JavaDoc) context.get("ruleFileLocation");
85         Locale JavaDoc locale = (Locale JavaDoc) context.get("locale");
86
87         // do security check
88
if (!security.hasPermission("SERVICE_INVOKE_ANY", userLogin)) {
89             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderYouDoNotHavePermissionToLoadTaxTables",locale));
90         }
91
92         // get a now stamp (we'll use 2000-01-01)
93
Timestamp JavaDoc now = parseDate("20000101", null);
94
95         // load the data file
96
DataFile tdf = null;
97         try {
98             tdf = DataFile.makeDataFile(UtilURL.fromResource(dataFile), flatTable);
99         } catch (DataFileException e) {
100             Debug.logError(e, module);
101             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderUnableToReadZipSalesDataFile",locale));
102         }
103
104         // locate the file to be imported
105
URL JavaDoc tUrl = UtilURL.fromResource(taxFileLocation);
106         if (tUrl == null) {
107             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderUnableToLocateTaxFileAtLocation", UtilMisc.toMap("taxFileLocation",taxFileLocation), locale));
108         }
109
110         RecordIterator tri = null;
111         try {
112             tri = tdf.makeRecordIterator(tUrl);
113         } catch (DataFileException e) {
114             Debug.logError(e, module);
115             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderProblemGettingTheRecordIterator",locale));
116         }
117         if (tri != null) {
118             while (tri.hasNext()) {
119                 Record entry = null;
120                 try {
121                     entry = tri.next();
122                 } catch (DataFileException e) {
123                     Debug.logError(e, module);
124                 }
125                 GenericValue newValue = delegator.makeValue("ZipSalesTaxLookup", null);
126                 // PK fields
127
newValue.set("zipCode", entry.getString("zipCode").trim());
128                 newValue.set("stateCode", entry.get("stateCode") != null ? entry.getString("stateCode").trim() : "_NA_");
129                 newValue.set("city", entry.get("city") != null ? entry.getString("city").trim() : "_NA_");
130                 newValue.set("county", entry.get("county") != null ? entry.getString("county").trim() : "_NA_");
131                 newValue.set("fromDate", parseDate(entry.getString("effectiveDate"), now));
132
133                 // non-PK fields
134
newValue.set("countyFips", entry.get("countyFips"));
135                 newValue.set("countyDefault", entry.get("countyDefault"));
136                 newValue.set("generalDefault", entry.get("generalDefault"));
137                 newValue.set("insideCity", entry.get("insideCity"));
138                 newValue.set("geoCode", entry.get("geoCode"));
139                 newValue.set("stateSalesTax", entry.get("stateSalesTax"));
140                 newValue.set("citySalesTax", entry.get("citySalesTax"));
141                 newValue.set("cityLocalSalesTax", entry.get("cityLocalSalesTax"));
142                 newValue.set("countySalesTax", entry.get("countySalesTax"));
143                 newValue.set("countyLocalSalesTax", entry.get("countyLocalSalesTax"));
144                 newValue.set("comboSalesTax", entry.get("comboSalesTax"));
145                 newValue.set("stateUseTax", entry.get("stateUseTax"));
146                 newValue.set("cityUseTax", entry.get("cityUseTax"));
147                 newValue.set("cityLocalUseTax", entry.get("cityLocalUseTax"));
148                 newValue.set("countyUseTax", entry.get("countyUseTax"));
149                 newValue.set("countyLocalUseTax", entry.get("countyLocalUseTax"));
150                 newValue.set("comboUseTax", entry.get("comboUseTax"));
151
152                 try {
153                     delegator.createOrStore(newValue);
154                 } catch (GenericEntityException e) {
155                     Debug.logError(e, module);
156                     return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderErrorWritingRecordsToTheDatabase",locale));
157                 }
158
159                 // console log
160
Debug.log(newValue.get("zipCode") + "/" + newValue.get("stateCode") + "/" + newValue.get("city") + "/" + newValue.get("county") + "/" + newValue.get("fromDate"));
161             }
162         }
163
164         // load the data file
165
DataFile rdf = null;
166         try {
167             rdf = DataFile.makeDataFile(UtilURL.fromResource(dataFile), ruleTable);
168         } catch (DataFileException e) {
169             Debug.logError(e, module);
170             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderUnableToReadZipSalesDataFile",locale));
171         }
172
173         // locate the file to be imported
174
URL JavaDoc rUrl = UtilURL.fromResource(ruleFileLocation);
175         if (rUrl == null) {
176             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderUnableToLocateRuleFileFromLocation", UtilMisc.toMap("ruleFileLocation",ruleFileLocation), locale));
177         }
178
179         RecordIterator rri = null;
180         try {
181             rri = rdf.makeRecordIterator(rUrl);
182         } catch (DataFileException e) {
183             Debug.logError(e, module);
184             return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderProblemGettingTheRecordIterator",locale));
185         }
186         if (rri != null) {
187             while (rri.hasNext()) {
188                 Record entry = null;
189                 try {
190                     entry = rri.next();
191                 } catch (DataFileException e) {
192                     Debug.logError(e, module);
193                 }
194                 if (entry.get("stateCode") != null && entry.getString("stateCode").length() > 0) {
195                     GenericValue newValue = delegator.makeValue("ZipSalesRuleLookup", null);
196                     // PK fields
197
newValue.set("stateCode", entry.get("stateCode") != null ? entry.getString("stateCode").trim() : "_NA_");
198                     newValue.set("city", entry.get("city") != null ? entry.getString("city").trim() : "_NA_");
199                     newValue.set("county", entry.get("county") != null ? entry.getString("county").trim() : "_NA_");
200                     newValue.set("fromDate", parseDate(entry.getString("effectiveDate"), now));
201
202                     // non-PK fields
203
newValue.set("idCode", entry.get("idCode") != null ? entry.getString("idCode").trim() : null);
204                     newValue.set("taxable", entry.get("taxable") != null ? entry.getString("taxable").trim() : null);
205                     newValue.set("shipCond", entry.get("shipCond") != null ? entry.getString("shipCond").trim() : null);
206
207                     try {
208                         // using storeAll as an easy way to create/update
209
delegator.storeAll(UtilMisc.toList(newValue));
210                     } catch (GenericEntityException e) {
211                         Debug.logError(e, module);
212                         return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderErrorWritingRecordsToTheDatabase",locale));
213                     }
214
215                     // console log
216
Debug.log(newValue.get("stateCode") + "/" + newValue.get("city") + "/" + newValue.get("county") + "/" + newValue.get("fromDate"));
217                 }
218             }
219         }
220
221         return ServiceUtil.returnSuccess();
222     }
223
224     // tax calc service
225
public static Map JavaDoc flatTaxCalc(DispatchContext dctx, Map JavaDoc context) {
226         GenericDelegator delegator = dctx.getDelegator();
227         List JavaDoc itemProductList = (List JavaDoc) context.get("itemProductList");
228         List JavaDoc itemAmountList = (List JavaDoc) context.get("itemAmountList");
229         List JavaDoc itemShippingList = (List JavaDoc) context.get("itemShippingList");
230         Double JavaDoc orderShippingAmount = (Double JavaDoc) context.get("orderShippingAmount");
231         GenericValue shippingAddress = (GenericValue) context.get("shippingAddress");
232
233         // flatTaxCalc only uses the Zip + City from the address
234
String JavaDoc stateProvince = shippingAddress.getString("stateProvinceGeoId");
235         String JavaDoc postalCode = shippingAddress.getString("postalCode");
236         String JavaDoc city = shippingAddress.getString("city");
237
238         // setup the return lists.
239
List JavaDoc orderAdjustments = new ArrayList JavaDoc();
240         List JavaDoc itemAdjustments = new ArrayList JavaDoc();
241
242         // check for a valid state/province geo
243
String JavaDoc validStates = UtilProperties.getPropertyValue("zipsales.properties", "zipsales.valid.states");
244         if (validStates != null && validStates.length() > 0) {
245             List JavaDoc stateSplit = StringUtil.split(validStates, "|");
246             if (!stateSplit.contains(stateProvince)) {
247                 Map JavaDoc result = ServiceUtil.returnSuccess();
248                 result.put("orderAdjustments", orderAdjustments);
249                 result.put("itemAdjustments", itemAdjustments);
250                 return result;
251             }
252         }
253
254         try {
255             // loop through and get per item tax rates
256
for (int i = 0; i < itemProductList.size(); i++) {
257                 GenericValue product = (GenericValue) itemProductList.get(i);
258                 Double JavaDoc itemAmount = (Double JavaDoc) itemAmountList.get(i);
259                 Double JavaDoc shippingAmount = (Double JavaDoc) itemShippingList.get(i);
260                 itemAdjustments.add(getItemTaxList(delegator, product, postalCode, city, itemAmount.doubleValue(), shippingAmount.doubleValue(), false));
261             }
262             if (orderShippingAmount.doubleValue() > 0) {
263                 List JavaDoc taxList = getItemTaxList(delegator, null, postalCode, city, 0.00, orderShippingAmount.doubleValue(), false);
264                 orderAdjustments.addAll(taxList);
265             }
266         } catch (GeneralException e) {
267             return ServiceUtil.returnError(e.getMessage());
268         }
269
270         Map JavaDoc result = ServiceUtil.returnSuccess();
271         result.put("orderAdjustments", orderAdjustments);
272         result.put("itemAdjustments", itemAdjustments);
273         return result;
274     }
275
276     private static List JavaDoc getItemTaxList(GenericDelegator delegator, GenericValue item, String JavaDoc zipCode, String JavaDoc city, double itemAmount, double shippingAmount, boolean isUseTax) throws GeneralException {
277         List JavaDoc adjustments = new ArrayList JavaDoc();
278
279         // check the item for tax status
280
if (item != null && item.get("taxable") != null && "N".equals(item.getString("taxable"))) {
281             // item not taxable
282
return adjustments;
283         }
284
285         // lookup the records
286
List JavaDoc zipLookup = delegator.findByAnd("ZipSalesTaxLookup", UtilMisc.toMap("zipCode", zipCode), UtilMisc.toList("-fromDate"));
287         if (zipLookup == null || zipLookup.size() == 0) {
288             throw new GeneralException("The zip code entered is not valid.");
289         }
290
291         // the filtered list
292
List JavaDoc taxLookup = null;
293
294         // only do filtering if there are more then one zip code found
295
if (zipLookup != null && zipLookup.size() > 1) {
296             // first filter by city
297
List JavaDoc cityLookup = EntityUtil.filterByAnd(zipLookup, UtilMisc.toMap("city", city.toUpperCase()));
298             if (cityLookup != null && cityLookup.size() > 0) {
299                 if (cityLookup.size() > 1) {
300                     // filter by county
301
List JavaDoc countyLookup = EntityUtil.filterByAnd(taxLookup, UtilMisc.toMap("countyDefault", "Y"));
302                     if (countyLookup != null && countyLookup.size() > 0) {
303                         // use the county default
304
taxLookup = countyLookup;
305                     } else {
306                         // no county default; just use the first city
307
taxLookup = cityLookup;
308                     }
309                 } else {
310                     // just one city found; use that one
311
taxLookup = cityLookup;
312                 }
313             } else {
314                 // no city found; lookup default city
315
List JavaDoc defaultLookup = EntityUtil.filterByAnd(zipLookup, UtilMisc.toMap("generalDefault", "Y"));
316                 if (defaultLookup != null && defaultLookup.size() > 0) {
317                     // use the default city lookup
318
taxLookup = defaultLookup;
319                 } else {
320                     // no default found; just use the first from the zip lookup
321
taxLookup = zipLookup;
322                 }
323             }
324         } else {
325             // zero or 1 zip code found; use it
326
taxLookup = zipLookup;
327         }
328
329         // get the first one
330
GenericValue taxEntry = null;
331         if (taxLookup != null && taxLookup.size() > 0) {
332             taxEntry = (GenericValue) taxLookup.iterator().next();
333         }
334
335         if (taxEntry == null) {
336             Debug.logWarning("No tax entry found for : " + zipCode + " / " + city + " - " + itemAmount, module);
337             return adjustments;
338         }
339
340         String JavaDoc fieldName = "comboSalesTax";
341         if (isUseTax) {
342             fieldName = "comboUseTax";
343         }
344
345         Double JavaDoc comboTaxRate = taxEntry.getDouble(fieldName);
346         if (comboTaxRate == null) {
347             Debug.logWarning("No Combo Tax Rate In Field " + fieldName + " @ " + zipCode + " / " + city + " - " + itemAmount, module);
348             return adjustments;
349         }
350
351         // get state code
352
String JavaDoc stateCode = taxEntry.getString("stateCode");
353
354         // check if shipping is exempt
355
boolean taxShipping = true;
356
357         // look up the rules
358
List JavaDoc ruleLookup = null;
359         try {
360             ruleLookup = delegator.findByAnd("ZipSalesRuleLookup", UtilMisc.toMap("stateCode", stateCode), UtilMisc.toList("-fromDate"));
361         } catch (GenericEntityException e) {
362             Debug.logError(e, module);
363         }
364
365         // filter out city
366
if (ruleLookup != null && ruleLookup.size() > 1) {
367             ruleLookup = EntityUtil.filterByAnd(ruleLookup, UtilMisc.toMap("city", city.toUpperCase()));
368         }
369
370         // no county captured; so filter by date
371
if (ruleLookup != null && ruleLookup.size() > 1) {
372             ruleLookup = EntityUtil.filterByDate(ruleLookup);
373         }
374
375         if (ruleLookup != null) {
376             Iterator JavaDoc ruleIterator = ruleLookup.iterator();
377             while (ruleIterator.hasNext()) {
378                 if (!taxShipping) {
379                     // if we found an rule which passes no need to contine (all rules are ||)
380
break;
381                 }
382                 GenericValue rule = (GenericValue) ruleIterator.next();
383                 String JavaDoc idCode = rule.getString("idCode");
384                 String JavaDoc taxable = rule.getString("taxable");
385                 String JavaDoc condition = rule.getString("shipCond");
386                 if ("T".equals(taxable)) {
387                     // this record is taxable
388
continue;
389                 } else {
390                     // except if conditions are met
391
boolean qualify = false;
392                     if (condition != null && condition.length() > 0) {
393                         char[] conditions = condition.toCharArray();
394                         for (int i = 0; i < conditions.length; i++) {
395                             switch (conditions[i]) {
396                                 case 'A' :
397                                     // SHIPPING CHARGE SEPARATELY STATED ON INVOICE
398
qualify = true; // OFBiz does this by default
399
break;
400                                 case 'B' :
401                                     // SHIPPING CHARGE SEPARATED ON INVOICE FROM HANDLING OR SIMILAR CHARGES
402
qualify = false; // we do not support this currently
403
break;
404                                 case 'C' :
405                                     // ITEM NOT SOLD FOR GUARANTEED SHIPPED PRICE
406
qualify = false; // we don't support this currently
407
break;
408                                 case 'D' :
409                                     // SHIPPING CHARGE IS COST ONLY
410
qualify = false; // we assume a handling charge is included
411
break;
412                                 case 'E' :
413                                     // SHIPPED DIRECTLY TO PURCHASER
414
qualify = true; // this is true, unless gifts do not count?
415
break;
416                                 case 'F' :
417                                     // SHIPPED VIA COMMON CARRIER
418
qualify = true; // best guess default
419
break;
420                                 case 'G' :
421                                     // SHIPPED VIA CONTRACT CARRIER
422
qualify = false; // best guess default
423
break;
424                                 case 'H' :
425                                     // SHIPPED VIA VENDOR EQUIPMENT
426
qualify = false; // best guess default
427
break;
428                                 case 'I' :
429                                     // SHIPPED F.O.B. ORIGIN
430
qualify = false; // no clue
431
break;
432                                 case 'J' :
433                                     // SHIPPED F.O.B. DESTINATION
434
qualify = false; // no clue
435
break;
436                                 case 'K' :
437                                     // F.O.B. IS PURCHASERS OPTION
438
qualify = false; // no clue
439
break;
440                                 case 'L' :
441                                     // SHIPPING ORIGINATES OR TERMINATES IN DIFFERENT STATES
442
qualify = true; // not determined at order time, no way to know
443
break;
444                                 case 'M' :
445                                     // PROOF OF VENDOR ACTING AS SHIPPING AGENT FOR PURCHASER
446
qualify = false; // no clue
447
break;
448                                 case 'N' :
449                                     // SHIPPED FROM VENDOR LOCATION
450
qualify = true; // sure why not
451
break;
452                                 case 'O' :
453                                     // SHIPPING IS BY PURCHASER OPTION
454
qualify = false; // most online stores require shipping
455
break;
456                                 case 'P' :
457                                     // CREDIT ALLOWED FOR SHIPPING CHARGE PAID BY PURCHASER TO CARRIER
458
qualify = false; // best guess default
459
break;
460                                 default: break;
461                             }
462                         }
463                     }
464
465                     if (qualify) {
466                         if (isUseTax) {
467                             if (idCode.indexOf('U') > 0) {
468                                 taxShipping = false;
469                             }
470                         } else {
471                             if (idCode.indexOf('S') > 0) {
472                                 taxShipping = false;
473                             }
474                         }
475                     }
476                 }
477             }
478         }
479
480         double taxableAmount = itemAmount;
481         if (taxShipping) {
482             //Debug.log("Taxing shipping", module);
483
taxableAmount += shippingAmount;
484         } else {
485             Debug.log("Shipping is not taxable", module);
486         }
487
488         // calc tax amount
489
double taxRate = comboTaxRate.doubleValue();
490         double taxCalc = taxableAmount * taxRate;
491
492         // format the number
493
Double JavaDoc taxAmount = new Double JavaDoc(formatCurrency(taxCalc));
494         adjustments.add(delegator.makeValue("OrderAdjustment", UtilMisc.toMap("amount", taxAmount, "orderAdjustmentTypeId", "SALES_TAX", "comments", new Double JavaDoc(taxRate).toString(), "description", "Sales Tax (" + stateCode + ")")));
495
496         return adjustments;
497     }
498
499     // formatting methods
500
private static Timestamp JavaDoc parseDate(String JavaDoc dateString, Timestamp JavaDoc useWhenNull) {
501         Timestamp JavaDoc ts = null;
502         if (dateString != null) {
503             try {
504                 ts = new Timestamp JavaDoc(dateFormat.parse(dateString).getTime());
505             } catch (ParseException JavaDoc e) {
506                 Debug.logError(e, module);
507             }
508         }
509
510         if (ts != null) {
511             return ts;
512         } else {
513             return useWhenNull;
514         }
515     }
516
517     private static String JavaDoc formatCurrency(double currency) {
518         return curFormat.format(currency);
519     }
520 }
521
Popular Tags