KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > nightlabs > ipanema > trade > Offer


1 /*
2  * Created on 28.09.2004
3  *
4  */

5 package com.nightlabs.ipanema.trade;
6
7 import java.io.Serializable JavaDoc;
8 import java.util.Collection JavaDoc;
9 import java.util.Date JavaDoc;
10 import java.util.HashMap JavaDoc;
11 import java.util.Iterator JavaDoc;
12 import java.util.Map JavaDoc;
13
14 import javax.jdo.JDOHelper;
15 import javax.jdo.PersistenceManager;
16
17 import com.nightlabs.ModuleException;
18 import com.nightlabs.ipanema.accounting.Accounting;
19 import com.nightlabs.ipanema.accounting.Currency;
20 import com.nightlabs.ipanema.accounting.ProductInfo;
21 import com.nightlabs.ipanema.security.User;
22 import com.nightlabs.ipanema.store.Product;
23
24 /**
25  * @author Niklas Schiffler <nick@nightlabs.de>
26  * @author marco schulze - marco at nightlabs dot de
27  * @author Alexander Bieber <alex[AT]nightlabs[DOT]de>
28  *
29  * @jdo.persistence-capable
30  * identity-type = "application"
31  * objectid-class = "com.nightlabs.ipanema.trade.id.OfferID"
32  * detachable = "true"
33  *
34  * @jdo.inheritance strategy = "new-table"
35  **/

36 public class Offer
37     implements Serializable JavaDoc
38 {
39     /**
40      * @jdo.field primary-key="true"
41      * @jdo.column length="100"
42      */

43     private String JavaDoc organisationID;
44
45     /**
46      * @jdo.field primary-key="true"
47      */

48     private long offerID;
49
50     /**
51      * @jdo.field persistence-modifier="persistent"
52      */

53     private Order order;
54
55     /**
56      * @jdo.field persistence-modifier="persistent"
57      */

58     private Currency currency;
59
60     /**
61      * @jdo.field persistence-modifier="persistent"
62      */

63     private String JavaDoc primaryKey;
64
65     /**
66      * key: String invoicePK<br/>
67      * value: Invoice invoice
68      * <br/><br/>
69      *
70      * @jdo.field
71      * persistence-modifier="persistent"
72      * collection-type="map"
73      * key-type="java.lang.String"
74      * value-type="com.nightlabs.ipanema.accounting.Invoice"
75      * @!mapped-by="offer"
76      *
77      * @jdo.join
78      *
79      * @!jdo.map-vendor-extension vendor-name="jpox" key="key-field" value="primaryKey"
80      */

81     private Map JavaDoc invoices = new HashMap JavaDoc();
82
83     /**
84      * An Offer is stable, if it does not change values when prices are recalculated.
85      * Only stable Offers can be confirmed. An instable Offer can come into existence
86      * if conflicting PriceConfigs of two or more OfferItems interfere.
87      *
88      * @jdo.field persistence-modifier="persistent"
89      */

90     private boolean stable;
91
92     /**
93      * An Offer is only valid if it is stable and has been validated. It needs to be
94      * valid, before it can be confirmed.
95      *
96      * @jdo.field persistence-modifier="persistent"
97      */

98     private boolean valid = false;
99
100     /**
101      * @jdo.field persistence-modifier="persistent"
102      */

103     private boolean confirmed = false;
104
105     /**
106      * This Map contains all OfferItems that should be sold.
107      * One Product can only be once in an offer for sale and once for refund.
108      * Thus, we can use the productPK as key to the OfferItem.
109      *
110      * key: String productPK (organisationID + / + productID)<br/>
111      * value: OfferItem offerItem
112      * <br/><br/>
113      *
114      * @jdo.field
115      * persistence-modifier="persistent"
116      * collection-type="map"
117      * key-type="java.lang.String"
118      * value-type="OfferItem"
119      * dependent="true"
120      * @!mapped-by="offer"
121      *
122      * @jdo.join
123      *
124      * @!jdo.map-vendor-extension vendor-name="jpox" key="key-field" value="primaryKey"
125      */

126     private Map JavaDoc itemsToSell = new HashMap JavaDoc();
127
128     /**
129      * This Map contains all OfferItems that should be refunded.
130      * One Product can only be once in an offer for sale and once for refund.
131      * Thus, we can use the productPK as key to the OfferItem.
132      *
133      * key: String productPK (organisationID + / + productID)<br/>
134      * value: OfferItem offerItem
135      * <br/><br/>
136      *
137      * @jdo.field
138      * persistence-modifier="persistent"
139      * collection-type="map"
140      * key-type="java.lang.String"
141      * value-type="OfferItem"
142      * dependent="true"
143      * @!mapped-by="offer"
144      *
145      * @jdo.join
146      *
147      * @!jdo.map-vendor-extension vendor-name="jpox" key="key-field" value="primaryKey"
148      */

149     private Map JavaDoc itemsToRefund = new HashMap JavaDoc();
150
151     /**
152      * This member represents the sum of all prices of all offer items.
153      *
154      * @jdo.field persistence-modifier="persistent"
155      */

156     private OfferItemPrice price;
157
158     /**
159      * @jdo.field persistence-modifier="persistent"
160      */

161     private boolean containsPricesDependentOnOffer = false;
162
163     protected Offer() { }
164
165     public Offer(Order order, long offerID)
166     {
167         if (order == null)
168             throw new NullPointerException JavaDoc("order must not be null!");
169         
170         PersistenceManager pm = JDOHelper.getPersistenceManager(order);
171         if (pm == null)
172             throw new IllegalStateException JavaDoc("order is not persistent! Could not get a PersistenceManager from it!");
173
174         Accounting accounting = Accounting.getAccounting(pm);
175
176         this.order = order;
177         this.organisationID = order.getOrganisationID();
178         this.offerID = offerID;
179         this.currency = order.getCurrency();
180         this.primaryKey = getPrimaryKey(organisationID, offerID);
181 // this.price = new OfferItemPrice(, .createPriceID());
182
}
183
184     /**
185      * @return Returns the organisationID.
186      */

187     public String JavaDoc getOrganisationID()
188     {
189         return organisationID;
190     }
191
192     /**
193      * @return Returns the offerID.
194      */

195     public long getOfferID()
196     {
197         return offerID;
198     }
199     public static String JavaDoc getPrimaryKey(String JavaDoc organisationID, long offerID)
200     {
201         return organisationID + '/' + Long.toHexString(offerID);
202     }
203
204     public String JavaDoc getPrimaryKey()
205     {
206         return primaryKey;
207     }
208     
209     public synchronized OfferItem createOfferItemToRefund(User user, OfferItem offerItemToRefund)
210             throws ModuleException
211     {
212         if (isConfirmed())
213             throw new IllegalStateException JavaDoc("This offer is already confirmed! Cannot create a new OfferItem!");
214         
215         PersistenceManager pm = JDOHelper.getPersistenceManager(this);
216         if (pm == null)
217             throw new IllegalStateException JavaDoc("This instance of Offer is not persistent! Cannot create an OfferItem!");
218         
219         // Whenever an Offer is changed, it needs to be marked as not valid to force a new validation!
220
this.valid = false;
221
222         Product product = offerItemToRefund.getProduct();
223         String JavaDoc productPK = product.getPrimaryKey();
224 // if (itemsToRefund.containsKey(productPK))
225
// throw new IllegalStateException("An offerItem for the same product ("+productPK+") does already exist within this offer ("+this.getPrimaryKey()+") as a an item to refund!");
226

227         Trader trader = Trader.getTrader(pm);
228
229         OfferItem offerItem = (OfferItem)itemsToSell.get(productPK);
230         if (offerItem == null) {
231             
232             if (trader.getOrganisationID().equals(this.getOrganisationID())) {
233                 offerItem = new OfferItem(this, offerItemToRefund);
234             }
235             else {
236 // // The offer is not local but coming from another organisation, thus we need to delegate the
237
// // creation of the offerItem to the other organisation.
238
// Hashtable props = Lookup.getInitialContextProps(pm, getOrder().getVendor().getOrganisationID());
239
// try {
240
// TradeManager tm = TradeManagerUtil.getHome(props).create();
241
//
242
// offerItem = tm.createOfferItemToRefund(
243
// OfferID.create(getOrganisationID(), getOfferID()),
244
// (OfferItemID) JDOHelper.getObjectId(offerItemToRefund));
245
//
246
// tm.remove();
247
// } catch (ModuleException e) {
248
// throw e;
249
// } catch (Exception e) {
250
// throw new ModuleException(e);
251
// }
252
return null; // TODO
253
}
254             
255             itemsToRefund.put(productPK, offerItem);
256         } // if (offerItem == null) {
257

258         return offerItem;
259     }
260
261     public synchronized OfferItem createOfferItemToSell(User user, Product product, Date JavaDoc autoReleaseDT) throws ModuleException
262     {
263         if (isConfirmed())
264             throw new IllegalStateException JavaDoc("This offer is already confirmed! Cannot create a new OfferItem!");
265
266         PersistenceManager pm = JDOHelper.getPersistenceManager(this);
267         if (pm == null)
268             throw new IllegalStateException JavaDoc("This instance of Offer is not persistent! Cannot create an OfferItem!");
269
270         // Whenever an Offer is changed, it needs to be marked as not valid to force a new validation!
271
this.valid = false;
272         
273         String JavaDoc productPK = product.getPrimaryKey();
274 // if (itemsToSell.containsKey(productPK))
275
// throw new IllegalStateException("An offerItem for the same product ("+productPK+") does already exist within this offer ("+this.getPrimaryKey()+") as an item to sell!");
276

277         OfferItem offerItem = (OfferItem)itemsToSell.get(productPK);
278         if (offerItem == null) {
279             Trader trader = Trader.getTrader(pm);
280             if (trader.getOrganisationID().equals(this.getOrganisationID())) {
281                 offerItem = new OfferItem(this, product, currency);
282             } // if (trader.getOrganisationID().equals(this.getOrganisationID())) {
283
else {
284 // // The offer is not local but coming from another organisation, thus we need to delegate the
285
// // creation of the offerItem to the other organisation.
286
// Hashtable props = Lookup.getInitialContextProps(pm, getOrder().getVendor().getOrganisationID());
287
// try {
288
// TradeManager tm = TradeManagerUtil.getHome(props).create();
289
//
290
// offerItem = tm.createOfferItemToSell(
291
// OfferID.create(getOrganisationID(), getOfferID()),
292
// ProductID.create(product.getOrganisationID(), product.getProductID()),
293
// autoReleaseDT);
294
//
295
// tm.remove();
296
// } catch (ModuleException e) {
297
// throw e;
298
// } catch (Exception e) {
299
// throw new ModuleException(e);
300
// }
301
return null; // TODO
302
}
303
304             itemsToSell.put(productPK, offerItem);
305         } // if (offerItem == null) {
306

307         // We need to make sure, all products on which this product relies on are allocated
308
// Therefore, we need to create requirement-orders and -offers for all organisations that
309
// are vendors. This all is done by the Trader, but the API allows to access this feature
310
// by OfferItem.
311
offerItem.allocate(user, autoReleaseDT);
312
313         return offerItem;
314     }
315
316     /**
317      * This method recalculates all prices for all OfferItems. To be sure there's no
318      * indefinite price definition, it does it twice
319      * (if there's a price config marked as isDependentOnOffer()=true).
320      */

321     public synchronized void calculatePrice()
322     {
323 // TODO put this logic into the bean?!
324
PersistenceManager pm = JDOHelper.getPersistenceManager(this);
325         if (pm == null)
326             throw new IllegalStateException JavaDoc("This instance of Offer is not persistent! Cannot calculate the price!");
327         
328         Trader trader = Trader.getTrader(pm);
329         if (trader.getOrganisationID().equals(this.getOrganisationID())) {
330             long amountBeforeRecalc = price.getAmount();
331             calculateOfferItemPrices(false);
332             long firstNewAmount = price.getAmount();
333             if (containsPricesDependentOnOffer) {
334                 // we recalculate a second time to make sure we have no indefinite price.
335
calculateOfferItemPrices(false);
336                 long secondNewAmount = price.getAmount();
337                 this.stable = secondNewAmount == firstNewAmount;
338             }
339             else
340                 this.stable = true;
341         } // if (trader.getOrganisationID().equals(this.getOrganisationID())) {
342
else {
343             // The offer is not coming from here locally, but from another organisation (maybe on different server).
344
// Thus, we need to delegate the price calculation to the other organisation.
345

346             // TODO delegate to remote org
347
throw new UnsupportedOperationException JavaDoc("NYI");
348         }
349     }
350
351     /**
352      * This method calculates all offerItem's prices and the offer's price. It's a local helper method
353      * called by calculatePrice().
354      *
355      * @param all If all=false, it does not recalculate the price of an OfferItem,
356      * if OfferItem.isPriceDependentFromOffer() returns false.
357      * If all=true, all prices of all items are recalculated.
358      */

359     private void calculateOfferItemPrices(boolean all)
360     {
361         Accounting accounting = null;
362         price.clearFragments();
363         price.setAmount(0);
364         containsPricesDependentOnOffer = false;
365
366         for (int i = 0; i<2; i++) {
367             Collection JavaDoc items = i == 0 ? itemsToSell.values() : itemsToRefund.values();
368             for (Iterator JavaDoc it = items.iterator(); it.hasNext(); ) {
369                 OfferItem offerItem = (OfferItem)it.next();
370                 if (all || offerItem.isPriceDependentOnOffer()) {
371                     if (offerItem.isPriceDependentOnOffer())
372                         containsPricesDependentOnOffer = true;
373                     
374                     if (accounting == null) {
375                         PersistenceManager pm = JDOHelper.getPersistenceManager(this);
376                         if (pm == null)
377                             throw new IllegalStateException JavaDoc("This instance of Offer is currently not persistent! Cannot obtain a PersistenceManager");
378                         
379                         accounting = Accounting.getAccounting(pm);
380                     } // if (accounting == null) {
381
ProductInfo productInfo = accounting.getProductInfo(offerItem.getProduct(), true);
382                     offerItem.setPriceDependentOnOffer(productInfo.getPriceConfig().isDependentOnOffer());
383                     offerItem.getPrice().assign(productInfo.getPriceConfig().getPrice(offerItem));
384                 } // if (all || offerItem.isPriceDependentOnOffer()) {
385
price.sumPrice(offerItem.getPrice());
386             }
387         }
388     }
389
390     /**
391      * @return Returns the order.
392      */

393     public Order getOrder()
394     {
395         return order;
396     }
397
398     /**
399      * @return Returns the confirmed.
400      */

401     public boolean isConfirmed()
402     {
403         return confirmed;
404     }
405     /**
406      * Confirms this offer. An Offer becomes immutable once it has been confirmed.
407      */

408     protected void confirm()
409     {
410         if (!isValid())
411             throw new IllegalStateException JavaDoc("This Offer cannot be confirmed, because it is not valid! Call 'validate()' first and ensure the Offer is stable!");
412
413         confirmed = true;
414     }
415
416     /**
417      * @return Returns the currency.
418      */

419     protected Currency getCurrency()
420     {
421         return currency;
422     }
423     /**
424      * @return Returns the items.
425      */

426     public Collection JavaDoc getItemsToSell()
427     {
428         return itemsToSell.values();
429     }
430
431     /**
432      * @return Returns the itemsToRefund.
433      */

434     public Collection JavaDoc getItemsToRefund()
435     {
436         return itemsToRefund.values();
437     }
438
439     /**
440      * @return Returns the items.
441      */

442     protected Map JavaDoc getItemsToSellMap()
443     {
444         return itemsToSell;
445     }
446
447     /**
448      * @return Returns the itemsToRefund.
449      */

450     protected Map JavaDoc getItemsToRefundMap()
451     {
452         return itemsToRefund;
453     }
454
455     /**
456      * This method must be called, before the Offer can be confirmed. If the Offer is changed
457      * (e.g. by <tt>createOfferItem(..)</tt>) the <tt>valid</tt> status is reset and <tt>validate()</tt>
458      * must be called again.
459      * <br/><br/>
460      * This method causes all prices to be recalculated and all products to be allocated (if they have
461      * been released by timeout).
462      *
463      * @return
464      */

465     public boolean validate()
466     {
467         valid = false;
468
469         // TODO here, we should make sure, we have requirement-offers, which can
470
// be fulfilled.
471

472         // if (all requirement-offers are valid)
473
valid = true;
474
475         calculatePrice();
476         if (!isStable())
477             valid = false;
478
479         return isValid();
480     }
481
482     /**
483      * An Offer is stable, if PriceConfig-s do not interfere and the prices do not
484      * change on calculation, if the Offer is not changed.
485      *
486      * @return Returns the stable.
487      */

488     public boolean isStable()
489     {
490         return stable;
491     }
492     
493     /**
494      * @return Returns the valid.
495      */

496     public boolean isValid()
497     {
498         if (valid && !stable) {
499 // TODO we should log an error here, because this should never happen!
500
return false;
501         }
502         return valid;
503     }
504     
505     /**
506      * Sets valid
507      */

508     protected void setValid(boolean valid)
509     {
510         this.valid = valid;
511     }
512     
513 }
514
Popular Tags