View Javadoc

1   package org.paneris.bibliomania.shopping;
2   
3   import java.io.PrintWriter;
4   import java.io.StringWriter;
5   import java.util.Enumeration;
6   import java.util.Hashtable;
7   import java.util.Locale;
8   
9   import org.melati.Melati;
10  import org.melati.servlet.Form;
11  import org.melati.poem.AccessToken;
12  import org.melati.poem.Field;
13  import org.melati.poem.PoemTask;
14  import org.melati.poem.PoemThread;
15  import org.melati.template.ServletTemplateContext;
16  import org.melati.template.TemplateEngine;
17  import org.melati.util.Email;
18  import org.melati.util.InstantiationPropertyException;
19  import org.melati.util.MelatiWriter;
20  import org.paneris.bibliomania.BibliomaniaDatabase;
21  import org.paneris.bibliomania.Country;
22  import org.paneris.bibliomania.Currency;
23  import org.paneris.bibliomania.DeliveryCharge;
24  import org.paneris.bibliomania.NotLoggedInException;
25  import org.paneris.bibliomania.ShopOrder;
26  import org.paneris.bibliomania.ShopOrderItem;
27  import org.paneris.bibliomania.User;
28  import org.paneris.bibliomania.UserTable;
29  import org.paneris.bibliomania.util.BibliomaniaUtil;
30  import org.paneris.melati.shopping.MelatiShoppingConfig;
31  import org.paneris.melati.shopping.ShoppingTrolley;
32  import org.paneris.melati.shopping.ShoppingTrolleyItem;
33  import org.webmacro.servlet.WebContext;
34  
35  /**
36  * <p> A Shopping Trolley stored information in the user's Shopping Trolley.<p>
37  * <p> It does this by storing itself in the session.</p>
38  * <p> For this reason, the constructors are private, and you will be expected to
39  * always get the Shopping Trolley using getInstance();</p>
40  *
41  * usage example:
42  *
43  * ShoppingTrolley trolley = ShoppingTrolley.getInstance(Melati melati);
44  * context.put("trolley", trolley);
45  *
46  **/
47  
48  public class BibliomaniaShoppingTrolley extends ShoppingTrolley {
49    
50    private static String MY_TROLLEY = "org.paneris.bibliomania.shopping.BibliomaniaShoppingTrolley";
51    private ShopOrder order = null;
52    public Country countryCode;
53    
54   /**
55    * Get the Locale for this trolley.
56    */
57    public Locale getLocale(){
58      // attempt to get from user preferences
59      if (locale == null) {
60        BibliomaniaDatabase db = (BibliomaniaDatabase)melati.getDatabase();
61        User user = (User)melati.getUser();
62        if (user.isRegisteredUser(db) && user.getCurrency() != null) {
63          locale = user.getCurrency().toLocale();
64        }
65        // attempt to find from user's http headers
66        if (locale == null) {
67          locale = BibliomaniaUtil.makeLocale(
68                                melati.getRequest().getHeader("accept-language"));
69        }
70      }
71      return locale; 
72    }
73    
74   /**
75    * Public Constructor to build a trolley from some id.
76    */
77    public void initialise(Melati melatiP, MelatiShoppingConfig configP) {
78      super.initialise(melatiP, configP);
79    }
80  
81  
82   /**
83    * Load a trolley from something persistent.
84    */
85    public void load(Integer id) throws InstantiationPropertyException {
86      BibliomaniaDatabase db = (BibliomaniaDatabase)melati.getDatabase();
87      order = db.getShopOrderTable().getShopOrderObject(id);
88      setCountryCode(order.getCountry());
89      setCountry(countryCode.getDisplayname());
90      setName(order.getName());
91      setEmail(order.getEmail());
92      setTel(order.getTel());
93      setDeliveryAddress(order.getAddress());
94      setTown(order.getTown());
95      setCounty(order.getCounty());
96      setPostcode(order.getPostcode());
97      setLocale(order.getCurrency().toLocale());
98      Enumeration e = order.getItems();
99      while (e.hasMoreElements()) {
100       ShopOrderItem line = (ShopOrderItem)e.nextElement();
101       ShoppingTrolleyItem item = newItem(line.getProduct().getTroid(), null, null);
102       if (line.getQuantity() != null) {
103         item.setQuantity(line.getQuantity().doubleValue());
104       }
105     }
106   }
107 
108  /**
109   * Save a trolley to something persistent.
110   */
111   public void save() {
112     final BibliomaniaDatabase db = (BibliomaniaDatabase)melati.getDatabase();
113     User user = (User)melati.getUser();
114     if (user == null || !user.isRegisteredUser(db)) {
115       user = (User)((UserTable)db.getUserTable()).getEmailColumn()
116                                                  .firstWhereEq(email);
117       if (user == null) {
118         // we have new user, save their details for next time
119         final User newUser;
120         newUser = (User)db.getUserTable().newPersistent();
121         newUser.setName(name);
122         newUser.setEmail(email);
123         newUser.setTel(tel);
124         newUser.setAddress(address);
125         newUser.setTown(town);
126         newUser.setCounty(county);
127         newUser.setRegion(postcode);
128         newUser.setCountry(countryCode);
129         newUser.generateDefaults();
130         PoemThread.withAccessToken(
131           AccessToken.root,
132           new PoemTask() {
133             public void run() {
134               db.getUserTable().create(newUser);
135             }
136           }
137         );
138         user = newUser;
139       }
140     } else {
141       // we have a genuinely logged in user, save their details for next time
142       user.setName(name);
143       user.setEmail(email);
144       user.setTel(tel);
145       user.setAddress(address);
146       user.setTown(town);
147       user.setCounty(county);
148       user.setRegion(postcode);
149       user.setCountry(countryCode);
150     }
151     if (order == null) {
152       order = (ShopOrder)db.getShopOrderTable().newPersistent();
153     } else {
154       order.removeItems();
155     }
156     order.setUser(user);
157     order.setName(name);
158     order.setEmail(email);
159     order.setTel(tel);
160     order.setAddress(address);
161     order.setTown(town);
162     order.setCounty(county);
163     order.setPostcode(postcode);
164     order.setCountry(countryCode);
165     order.setStatus(db.getOrderStatusTable().getNotAuthorised());
166     order.setAmount(convertFromUK(getTotalValue()));
167     order.setDelivery(convertFromUK(getTotalDeliveryValue()));
168     order.setAmountUK(getTotalValue());
169     order.setDeliveryUK(getTotalDeliveryValue());
170     order.setCurrency(getCurrency());
171     if (order.getTroid() == null) {
172       db.getShopOrderTable().create(order);
173     }
174     for (Enumeration en = getItems(); en.hasMoreElements();) {
175       BibliomaniaShoppingTrolleyItem item = (BibliomaniaShoppingTrolleyItem) en.nextElement();
176       item.save(db, order);
177     }
178   }
179 
180  /**
181   * Provide a mechanism for working out if 
182   * this order should include a delivery charge.
183   */
184   public boolean hasDelivery(){
185     if (countryCode == null) return false;
186     return true;
187   }
188 
189   
190  /**
191   * Can we deliver this - we need to check that each item has a supplier.
192   */
193   public boolean canSupply(){
194     boolean canSupply = true;
195     for (Enumeration en = getItems(); en.hasMoreElements();) {
196       BibliomaniaShoppingTrolleyItem item = (BibliomaniaShoppingTrolleyItem) en.nextElement();
197       if (item.getDeliveryCharge() == null) canSupply = false;
198     }
199     return canSupply;
200   }
201     
202  /**
203   *  Work out the cost of delivery.
204   */
205   public double getDeliveryValue() {
206     double cost = 0;
207     
208     Enumeration f = getSuppliersCharges();
209     while (f.hasMoreElements()) {
210       cost += ((SupplierCharge)f.nextElement()).charge.getOrdercharge().doubleValue();
211     }
212     return cost;
213   }
214 
215  /**
216   *  Extract the list of supplier's charges.
217   */
218   public Enumeration getSuppliersCharges() {
219     // get the cost for each supplier
220     Hashtable charges = new Hashtable();
221     Enumeration e = getItems();
222     while (e.hasMoreElements()) {
223       BibliomaniaShoppingTrolleyItem item = (BibliomaniaShoppingTrolleyItem)e.nextElement();
224       DeliveryCharge charge = item.getDeliveryCharge();
225       if (charge != null) {
226         Integer troid = charge.getTroid();
227         SupplierCharge sc = new SupplierCharge(charge,item);
228         if (charges.containsKey(troid))
229           sc.amount += ((SupplierCharge)charges.get(troid)).getAmount();
230         charges.put(troid,sc);
231       }
232     }
233     return charges.elements();
234   }
235 
236  /**
237   *  Provide a mechanism for working out if 
238   *  this order should include a discount.
239   */
240   public boolean hasDiscount() {
241     return false;
242   }
243 
244  /**
245   * If you want to apply a discount to this order, do it here.
246   */
247   public double getDiscountRate() {
248     return 0;
249   }
250 
251  /**
252   *  Provide a mechanism for working out if 
253   *  this order should include VAT (default should be true).
254   */
255   public boolean hasVAT() {
256     return true;
257   }
258   
259   public Field getCountries() {
260     BibliomaniaDatabase db = (BibliomaniaDatabase)melati.getDatabase();
261     Integer current = null;
262     if (countryCode != null) current = countryCode.getTroid();
263     return new Field(current, db.getShopOrderTable().getCountryColumn());
264   }
265   
266   public Country getCountryCode() {
267     return countryCode;
268   }
269 
270   public void setFromForm(Melati melati) {
271     super.setFromForm(melati);
272     BibliomaniaDatabase db = (BibliomaniaDatabase)melati.getDatabase();
273     Integer countryCodeId = 
274          Form.getIntegerField(melati.getServletTemplateContext(),"field_country");
275     Country newCode = (Country)db.getCountryTable()
276                   .getIdColumn().firstWhereEq(countryCodeId);
277     setCountryCode(newCode);
278   }
279   
280   public void setCountryCode(Country newCode) {
281     if (newCode != countryCode) {
282       // need to unset charges so that they can be recalculated
283       Enumeration e = getItems();
284       while (e.hasMoreElements()) {
285         BibliomaniaShoppingTrolleyItem line = (BibliomaniaShoppingTrolleyItem)e.nextElement();
286         line.setGotCharge(false);
287       }
288     }
289     countryCode = newCode;
290   }
291   
292   public void setDefaultDetails(Melati melati) {
293     BibliomaniaDatabase db = (BibliomaniaDatabase)melati.getDatabase();
294     User user = (User)melati.getUser();
295     if (user != null && user.isRegisteredUser(db)) {
296       if (address == null) address = user.getAddress();
297       if (town == null) town = user.getTown();
298       if (county == null) county = user.getCounty();
299       if (name == null) name = user.getName();
300       if (tel == null) tel = user.getTel();
301       if (countryCode == null) setCountryCode(user.getCountry());
302       if (postcode == null) postcode = user.getRegion();
303       if (email == null) email = user.getEmail();
304     }
305   }
306   
307   public void assertLogin(Melati melatiP) {
308     BibliomaniaDatabase db = (BibliomaniaDatabase)melatiP.getDatabase();
309     if (!PoemThread.accessToken().givesCapability(
310         db.getRegisteredUserCapability()))
311       throw new NotLoggedInException();
312   }
313 
314   public void confirmPayment(Melati melatiP) {
315     BibliomaniaDatabase db = (BibliomaniaDatabase)melatiP.getDatabase();
316     ServletTemplateContext context = melatiP.getServletTemplateContext();
317     String amountString = context.getFormField("amount");
318     String uniqueString = melatiP.getRequest().getQueryString();
319     Integer orderInteger = Form.getIntegerField(context,"orderref");
320     ServletTemplateContext templateContext = melatiP.getServletTemplateContext();
321     boolean error = false;
322     try {
323       double amount = new Double(amountString).doubleValue();
324       load(orderInteger);
325       amount /= 100;
326       templateContext.put("trolley",this);
327       if (!uniqueString.equals("foo")) {
328         error = true;
329         context.put("error", "2");
330       }
331       if (getTotalValue() != amount) {
332         error = true;
333         context.put("expect", new Double(convertFromUK(getTotalValue())));
334         context.put("paid", new Double(convertFromUK(amount)));
335         context.put("error", "1");
336       } 
337       if (!error) {
338         TemplateEngine templateEngine = melati.getTemplateEngine();
339         templateContext.put("melati",melati);
340         
341         // email to bibliomaia
342         MelatiWriter sw = templateEngine.getStringWriter();
343         String templateName = "email/Bibliomania.wm";
344         templateEngine.expandTemplate(sw, templateName, templateContext);
345         String emailText = sw.toString();
346         sw = templateEngine.getStringWriter();
347         Email.send(db.getSettingTable().get(Email.SMTPSERVER), getEmail(), db.getOrderEmailTo(), "", 
348                    "Bibliomania Order (number: " + orderInteger + ")", emailText);
349 
350         //email to customer
351         sw = templateEngine.getStringWriter();
352         templateName = "email/Customer.wm";
353         templateEngine.expandTemplate(sw, templateName, templateContext);
354         emailText = sw.toString();
355         Email.send(db.getSettingTable().get(Email.SMTPSERVER), db.getOrderEmailFrom(), getEmail(), "", 
356                    "Your order at Bibliomania.com (number: " + orderInteger + ")", emailText);
357 
358         //email to suppliers
359         Enumeration f = getSuppliersCharges();
360         while (f.hasMoreElements()) {
361           SupplierCharge charge = (SupplierCharge)f.nextElement();
362           templateContext.put("charge",charge);
363           sw = templateEngine.getStringWriter();
364           templateName = "email/Supplier.wm";
365           templateEngine.expandTemplate(sw, templateName, templateContext);
366           emailText = sw.toString();
367           Email.send(db.getSettingTable().get(Email.SMTPSERVER), db.getOrderEmailFrom(), charge.getCharge().getSupplier().getEmail(), "", 
368                      "Book orders via Bibliomania.com (number: " + orderInteger + ")", emailText);
369         }
370         
371         order.setStatus(db.getOrderStatusTable().getAuthorised());
372         Enumeration e = order.getItems();
373         while (e.hasMoreElements()) {
374           ShopOrderItem purchase = (ShopOrderItem)e.nextElement();
375           purchase.setStatus(db.getOrderStatusTable().getAuthorised());
376         }
377         remove(melati);
378       }
379     } catch (Exception e) {
380       error = true;
381       StringWriter sw = new StringWriter();
382       PrintWriter pw = new PrintWriter(sw);
383       e.printStackTrace(pw);
384       context.put("error", sw);
385     }
386   }
387   
388  /**
389   * Return the name of the trolley (for storing in the session).
390   */
391   public static String name() {
392     return MY_TROLLEY;
393   }
394 
395   public ShopOrder getOrder() {
396     return order;
397   }
398 
399   public static double round(double num) {
400     int a = Math.round(new Float(num * 100).floatValue());
401     double b = new Double(a).doubleValue();
402     return (b/100);
403   }
404 
405  /**
406   * Format a number in the locale currency.
407   */
408   public String convertFromUKandFormat(double value) {
409     return getCurrency().convertFromUKandFormat(value);
410   }
411 
412  /**
413   * Convert from foreign currency back to sterling.
414   */
415   public double convertFromUK(double value) {
416     return getCurrency().convertFromUK(value);
417   }
418 
419  /**
420   * Convert from foreign currency back to sterling.
421   */
422   public double convertToUK(double value) {
423     return getCurrency().convertToUK(value);
424   }
425   
426   public Currency getCurrency(Locale localeP) {
427     return ((BibliomaniaDatabase) melati.getDatabase())
428                               .getCurrencyTable().getCurrency(localeP);
429   }
430   
431   public Currency getCurrency() {
432     return getCurrency(getLocale());
433   }
434 
435   public String displayCurrency(double value) {
436     return convertFromUKandFormat(value);
437   }
438 
439   public void configureRequest(Melati melatiP) {
440     this.melati = melatiP;
441     BibliomaniaDatabase db = (BibliomaniaDatabase)melatiP.getDatabase();
442     db.setupContext(melatiP, (WebContext)melatiP.getTemplateContext().getContext(),
443                     db.getShopSectionGroup());
444   }  
445 
446     
447   public class SupplierCharge {
448     public DeliveryCharge charge;     
449     public double amount;
450     
451     public SupplierCharge (DeliveryCharge c, BibliomaniaShoppingTrolleyItem item) {
452       charge = c;
453       amount = item.getValue();
454     }
455     
456     public DeliveryCharge getCharge() {
457       return charge;
458     }
459     
460     public double getAmount() {
461       return amount;
462     }
463   }
464   
465 }
466