View Javadoc

1   /*
2    * $Source: /usr/cvsroot/org/paneris/melati/shopping/ShoppingTrolley.java,v $
3    * $Revision: 1.15 $
4    *
5    * Copyright (C) 2000 Tim Joyce
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     Tim Joyce <timj@paneris.org>
42   *     http://paneris.org/
43   *     68 Sandbanks Rd, Poole, Dorset. BH14 8BY. UK
44   */
45  
46  package org.paneris.jammyjoes.shopping;
47  
48  import java.text.NumberFormat;
49  import java.util.Enumeration;
50  import java.util.Hashtable;
51  import java.util.Locale;
52  import java.util.Vector;
53  
54  import javax.servlet.http.HttpSession;
55  
56  import org.melati.Melati;
57  import org.melati.poem.AccessPoemException;
58  import org.melati.poem.AccessToken;
59  import org.melati.poem.Capability;
60  import org.melati.poem.PoemThread;
61  import org.melati.util.InstantiationPropertyException;
62  import org.paneris.jammyjoes.model.JammyjoesDatabase;
63  import org.paneris.jammyjoes.util.JammyJoesUtil;
64  
65   /**
66   * <p> A Shopping Trolley stored information in the user's Shopping Trolley.<p>
67   * <p> It does this by storing itself in the session.</p>
68   * <p> For this reason, the constructors are private, and you will be expected to
69   * always get the Shopping Trolley using getInstance();</p>
70   *
71   * usage example:
72   *
73   * ShoppingTrolley trolley = ShoppingTrolley.getInstance(Melati melati);
74   * context.put("trolley", trolley);
75   *
76   **/
77  
78  public abstract class ShoppingTrolley {
79  
80    private static String TROLLEY = "org.paneris.melati.shopping.DefaultShoppingTrolley";
81    protected Locale locale;
82    protected String address;
83    protected String name;
84    protected String tel;
85    protected String town;
86    protected String county;
87    protected String country;
88    protected String postcode;
89    protected String message;
90    protected String email;
91    protected boolean hasDetails = false;
92    Vector orderedItems = new Vector();
93    Hashtable items = new Hashtable();
94    public MelatiShoppingConfig config;
95    public Melati melati;
96  
97    /**
98     * private Constructor to build an empty ShoppingTrolley
99    **/
100   protected void initialise(Melati melati, MelatiShoppingConfig config) {
101     this.config = config;
102     this.melati = melati;
103   }
104 
105   /**
106    * public Constructor to build a trolley from some id
107   **/
108   public void initialise(Melati melati, MelatiShoppingConfig config, Integer id)
109    throws InstantiationPropertyException {
110     initialise(melati,config);
111     load(id);
112     HttpSession session = melati.getSession();
113     session.setAttribute(name(),this);
114   }
115 
116   /**
117    * remove any trolley from the session
118    */
119   public void remove(Melati melati) {
120     HttpSession session = melati.getSession();
121     session.removeAttribute(name());
122   }
123 
124 
125   /**
126    * Returns the single instance, creating one if it can't be found.
127    */
128   public static synchronized ShoppingTrolley getInstance(Melati melati, MelatiShoppingConfig config)
129    throws InstantiationPropertyException {
130     HttpSession session = melati.getSession();
131     ShoppingTrolley instance = (ShoppingTrolley) session.getAttribute(name());
132     if (instance == null || instance.isPaid()) {
133       instance = newTrolley(config);
134       instance.initialise(melati,config);
135       session.setAttribute(name(),instance);
136     }
137     instance.configureRequest(melati);
138     return instance;
139   }
140 
141   private boolean isPaid() {
142     return false;
143   }
144 
145   public static synchronized ShoppingTrolley newTrolley(MelatiShoppingConfig config)
146    throws InstantiationPropertyException {
147     return config.getShoppingTrolley();
148   }
149 
150   /* get the Locale for this trolley
151   */
152   public abstract Locale getLocale();
153 
154 
155   /* set the Locale for this trolley
156   */
157   public void setLocale(Locale locale){
158     this.locale = locale;
159   }
160 
161   /* confirm payment of this trolley
162   */
163   public abstract void confirmPayment(Melati melati);
164 
165   /* load a trolley from something persistent
166   */
167   public abstract void load(Integer id) throws InstantiationPropertyException;
168 
169   /* save a trolley to something persistent
170   */
171   public abstract void save();
172 
173   /* this is done for each request, so anything special that needs to be done
174    * can be put in here
175   */
176   public void configureRequest(Melati melati) {
177     this.melati = melati;
178   }
179 
180   /* do something to force users to login
181    * you could perhaps throw an access poem exception in order to let the
182    * servlet generate the login page
183    */
184   public void assertLogin(Melati melati) {
185     JammyjoesDatabase db = (JammyjoesDatabase) melati.getDatabase();
186     Capability operator = db.getOperatorCapability();
187     AccessToken token = PoemThread.accessToken();
188     if (!token.givesCapability(operator))
189       throw new AccessPoemException(token, operator);
190   }
191 
192   /* set the user's detault details into this trolley.  this is useful
193    * if users have already logged in, and we don't want them to reenter their
194    * details
195    */
196   public abstract void setDefaultDetails(Melati melati);
197 
198 
199   /* return the name of the trolley (for storing in the session
200   */
201   public static String name() {
202     return TROLLEY;
203   }
204 
205   /* get the items from the trolley
206   */
207   public Enumeration getItems() {
208     return orderedItems.elements();
209   }
210 
211   /* have we got anything in the trolley
212   */
213   public boolean isEmpty() {
214     return items.isEmpty();
215   }
216 
217   /* have we entered any personal information
218   */
219   public boolean hasDetails() {
220     return hasDetails;
221   }
222 
223   /* get an item from the trolley
224   */
225   public ShoppingTrolleyItem getItem(Integer id) {
226     return (ShoppingTrolleyItem)items.get(id);
227   }
228 
229   /* remove an item from the trolley
230   */
231   public void removeItem(ShoppingTrolleyItem item) {
232     items.remove(item.getId());
233     orderedItems.removeElement(item);
234   }
235 
236   /* add an item to the trolley
237   */
238   public void addItem(ShoppingTrolleyItem item) {
239     // don't add it if it's already there
240     if (!items.containsKey(item.getId())) {
241       orderedItems.add(item);
242     }
243     items.put(item.getId(),item);
244   }
245 
246   public ShoppingTrolleyItem newItem(Integer id, String description, Double price)
247    throws InstantiationPropertyException {
248     ShoppingTrolleyItem item = ShoppingTrolleyItem.newTrolleyItem(config);
249     item.initialise(this, melati, id, description, price);
250     addItem(item);
251     return item;
252   }
253 
254   /* calculate the value of the items in the trolley
255   */
256   public double getValue() {
257     double value = 0;
258     for (Enumeration en = items.elements(); en.hasMoreElements();) {
259       ShoppingTrolleyItem product = (ShoppingTrolleyItem) en.nextElement();
260       value += product.getValue();
261     }
262     return value;
263   }
264 
265   /* format the order value for display
266      this value does not include discount or delivery, but does invlude VAT
267   */
268   public String getValueDisplay() {
269     return displayCurrency(getValue());
270   }
271 
272   /* calculate the total value of this order
273   */
274   public double getTotalValue() {
275     return getValue() + getTotalDeliveryValue() + getDiscountValue() + getVATValue();
276   }
277 
278   /* format the total order value for display
279      this value includes discount, delivery and VAT
280   */
281   public String getTotalValueDisplay() {
282     return displayCurrency(getTotalValue());
283   }
284 
285   /* format the total order value in pence, typically ecomerce sites
286      accept the values in pence not pounds
287   */
288 
289   public String getTotalValuePence() {
290     return getValuePence(getTotalValue());
291   }
292 
293   public static String getValuePence(double totalValue) {
294     return (new Double(roundTo2dp(totalValue * 100))).intValue() + "";
295   }
296 
297   /* provide a mechanism for working out if
298      this order should include a delivery charge
299   */
300   public abstract boolean hasDelivery();
301 
302   /* you need to provide some mechanism for calculating the delivery
303      value for the order (item delivery values are calculated individually
304   */
305   public abstract double getDeliveryValue();
306 
307   /* the delivery charge for the order is the sum of the charges on the items
308      and an overall charge
309   */
310   public double getTotalDeliveryValue() {
311     double value = 0;
312     if (hasDelivery()) {
313       value = getDeliveryValue();
314       for (Enumeration en = items.elements(); en.hasMoreElements();) {
315         ShoppingTrolleyItem item = (ShoppingTrolleyItem) en.nextElement();
316         value += item.getDeliveryValue();
317       }
318     }
319     return value;
320   }
321 
322   /* format the devliery value for display
323   */
324   public String getDeliveryDisplay() {
325     return displayCurrency(getTotalDeliveryValue());
326   }
327 
328   /* provide a mechanism for working out if
329      this order should include a discount
330   */
331   public abstract boolean hasDiscount();
332 
333   /* if you want to apply a discount to this order, do it here
334   */
335   public abstract double getDiscountRate();
336 
337   /* work out the value of the discout applied to this order
338      (returns a negative value)
339   */
340   public double getDiscountValue() {
341     double value = 0;
342     if (hasDiscount()) {
343       value = 0 - roundTo2dp(getValue()*getDiscountRate());
344     }
345     return value;
346   }
347 
348   /* display the discount (if present)
349   */
350   public String getDiscountRateDisplay() {
351     if (hasDiscount()) {
352       try {
353         return (new Double(getDiscountRate())).intValue() + "%";
354       } catch (NumberFormatException e) {
355         return getDiscountRate() + "%";
356       }
357     } else {
358       return "";
359     }
360   }
361 
362   /* format the discount value for display
363   */
364   public String getDiscountValueDisplay() throws Exception {
365     return displayCurrency(getDiscountValue());
366   }
367 
368   /* provide a mechanism for working out if
369      this order should include VAT (default should be true)
370   */
371   public abstract boolean hasVAT();
372 
373   /* calculate the VAT value of the order
374      typically items are priced inclusive of VAT and orders
375      are therefor also inclusive of VAT.  If this order is
376      for someone who should not be charged VAT, we need to subtract VAT
377      from the order value
378   */
379   public double getVATValue() {
380     if (!hasVAT()) {
381       return roundTo2dp(getValue() * -0.14894);
382     } else {
383       return 0;
384     }
385   }
386 
387   /* format the vat value for display
388   */
389   public String getVATDisplay() {
390     return displayCurrency(getVATValue());
391   }
392 
393   /* set the address
394   */
395   public void setDeliveryAddress(String a) {
396     address = a;
397   }
398   /* get the address
399   */
400   public String getDeliveryAddress() {
401     return address;
402   }
403 
404   /* set the name
405   */
406   public void setName(String a) {
407     name = a;
408   }
409   /* get the name
410   */
411   public String getName() {
412     return name;
413   }
414 
415   /* set the email address
416   */
417   public void setEmail(String a) {
418     email = a;
419   }
420   /* get the email address
421   */
422   public String getEmail() {
423     return email;
424   }
425 
426   /* set the postcode
427   */
428   public void setPostcode(String a) {
429     postcode = a;
430   }
431   /* get the postcode
432   */
433   public String getPostcode() {
434     return postcode;
435   }
436 
437   /* set the telephone number
438   */
439   public void setTel(String a) {
440     tel = a;
441   }
442   /* get the telephone number
443   */
444   public String getTel() {
445     return tel;
446   }
447 
448   /* set the town
449   */
450   public void setTown(String a) {
451     town = a;
452   }
453   /* get the town
454   */
455   public String getTown() {
456     return town;
457   }
458 
459   /* set the county
460   */
461   public void setCounty(String a) {
462     county = a;
463   }
464   /* get the county
465   */
466   public String getCounty() {
467     return county;
468   }
469 
470   /* set the country
471   */
472   public void setCountry(String a) {
473     country = a;
474   }
475   /* get the country
476   */
477   public String getCountry() {
478     return country;
479   }
480 
481   /* set the delivery message
482   */
483   public void setMessage(String a) {
484     message = a;
485   }
486   /* get the delivery message
487   */
488   public String getMessage() {
489     return message;
490   }
491 
492   /* format a number in the locale currency
493   */
494   public String displayCurrency(double value) {
495     return new String(NumberFormat.getCurrencyInstance(getLocale()).format(value));
496   }
497 
498   /* format a number in the locale currency
499   */
500   public String displayCurrency(Double value) {
501     return displayCurrency(value.doubleValue());
502   }
503 
504   public String baseURL() {
505     String result = "";
506     if (JammyJoesUtil.telesales(melati)) {
507       result = "/telesales/";
508     } else {
509       result = "/jammyjoes/";
510     }
511     result += "Trolley/" + melati.getPoemContext().getLogicalDatabase() + "/";
512     return result;
513   }
514 
515   public String viewURL() {
516     return baseURL() + "View/";
517   }
518 
519   public String detailsURL() {
520     return baseURL() + "Details/";
521   }
522 
523   public String confirmURL() {
524     return baseURL() + "Confirm/";
525   }
526 
527   public String abandonURL() {
528     return baseURL() + "Abandon/";
529   }
530 
531   public String updateURL() {
532     return baseURL() + "Update/";
533   }
534 
535   public String paidURL() {
536     return baseURL() + "Paid/";
537   }
538 
539   public String catalogueURL() {
540     return baseURL() + "CatalogueEntry/";
541   }
542 
543   public static double roundTo2dp(double num) {
544     int a = Math.round(new Float(num * 100).floatValue());
545     double b = new Double(a).doubleValue();
546     return (b/100);
547   }
548 
549 }
550