KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > nightlabs > ipanema > accounting > Accounting


1 /*
2  * Created on 27.10.2004
3  */

4 package com.nightlabs.ipanema.accounting;
5
6 import java.util.Collection JavaDoc;
7 import java.util.HashMap JavaDoc;
8 import java.util.HashSet JavaDoc;
9 import java.util.Iterator JavaDoc;
10 import java.util.Map JavaDoc;
11
12 import javax.jdo.JDOHelper;
13 import javax.jdo.JDOObjectNotFoundException;
14 import javax.jdo.PersistenceManager;
15
16 import com.nightlabs.ModuleException;
17 import com.nightlabs.ipanema.accounting.id.InvoiceID;
18 import com.nightlabs.ipanema.organisation.LocalOrganisation;
19 import com.nightlabs.ipanema.security.User;
20 import com.nightlabs.ipanema.store.Product;
21 import com.nightlabs.ipanema.trade.LegalEntity;
22 import com.nightlabs.ipanema.trade.Offer;
23 import com.nightlabs.ipanema.trade.OfferItem;
24 import com.nightlabs.ipanema.trade.Order;
25 import com.nightlabs.ipanema.trade.OrganisationLegalEntity;
26 import com.nightlabs.ipanema.trade.id.OfferItemID;
27 import com.nightlabs.ipanema.transfer.Transfer;
28 import com.nightlabs.ipanema.transfer.TransferRegistry;
29 import com.nightlabs.ipanema.transfer.id.AnchorID;
30
31 /**
32  * @author Marco Schulze - marco at nightlabs dot de
33  * @author Alexander Bieber <alex[AT]nightlabs[DOT]de>
34  *
35  * @jdo.persistence-capable
36  * identity-type = "datastore"
37  * detachable = "true"
38  *
39  * @jdo.inheritance strategy = "new-table"
40  */

41 public class Accounting
42     implements TransferRegistry
43 {
44     /**
45      * This method returns the singleton instance of Accounting. If there is
46      * no instance of Accounting in the datastore, yet, it will be created.
47      *
48      * @param pm
49      * @return
50      */

51     public static Accounting getAccounting(PersistenceManager pm)
52     {
53         Iterator JavaDoc it = pm.getExtent(Accounting.class).iterator();
54         if (it.hasNext())
55             return (Accounting)it.next();
56
57         Accounting accounting = new Accounting();
58
59         // initialize the organisationID and all other members
60
it = pm.getExtent(LocalOrganisation.class).iterator();
61         if (!it.hasNext())
62             throw new IllegalStateException JavaDoc("LocalOrganisation undefined in datastore!");
63         LocalOrganisation localOrganisation = (LocalOrganisation) it.next();
64         String JavaDoc organisationID = localOrganisation.getOrganisation().getOrganisationID();
65         accounting.organisationID = organisationID;
66         accounting.mandator = new OrganisationLegalEntity(localOrganisation.getOrganisation());
67         accounting.accountingPriceConfig = new AccountingPriceConfig(organisationID, accounting.createPriceConfigID());
68
69         pm.makePersistent(accounting);
70         return accounting;
71     }
72
73     /**
74      * @jdo.field persistence-modifier="persistent"
75      * @jdo.column length="100"
76      */

77     private String JavaDoc organisationID;
78     
79     /**
80      * @jdo.field persistence-modifier="persistent"
81      */

82     private OrganisationLegalEntity mandator;
83     
84     /**
85      * @jdo.field persistence-modifier="persistent"
86      */

87     private AccountingPriceConfig accountingPriceConfig;
88
89     /**
90      * @return Returns the organisationID.
91      */

92     public String JavaDoc getOrganisationID()
93     {
94         return organisationID;
95     }
96     
97     /**
98      * @jdo.field persistence-modifier="none"
99      */

100     protected transient PersistenceManager accountingPM = null;
101     
102     protected PersistenceManager getPersistenceManager() {
103         if (accountingPM == null) {
104             accountingPM = JDOHelper.getPersistenceManager(this);
105             if (accountingPM == null)
106                 throw new IllegalStateException JavaDoc("This instance of Accounting is not persistent, can not get a PersistenceManager!");
107         }
108         return accountingPM;
109     }
110     
111     public OrganisationLegalEntity getMandator() {
112         return mandator;
113     }
114     
115     /**
116      * key: String productPK<br/>
117      * value: ProductInfo productInfo
118      *
119      * @jdo.field
120      * persistence-modifier="persistent"
121      * collection-type="map"
122      * key-type="java.lang.String"
123      * value-type="ProductInfo"
124      * dependent="true"
125      *
126      * @jdo.join
127      *
128      * @jdo.map-vendor-extension vendor-name="jpox" key="key-length" value="max 201"
129      */

130     protected Map JavaDoc productInfos = new HashMap JavaDoc();
131     
132     public ProductInfo getProductInfo(String JavaDoc organisationID, String JavaDoc productID, boolean throwExceptionIfNotExistent)
133     {
134         String JavaDoc pk = ProductInfo.getPrimaryKey(organisationID, productID);
135         ProductInfo res = (ProductInfo) productInfos.get(pk);
136         if (res == null && throwExceptionIfNotExistent)
137             throw new IllegalArgumentException JavaDoc("No ProductInfo existing with organisationID=\""+organisationID+"\" and productID=\""+productID+"\"!");
138         return res;
139     }
140
141     public ProductInfo getProductInfo(Product product, boolean throwExceptionIfNotExistent)
142     {
143         String JavaDoc pk = product.getPrimaryKey();
144         ProductInfo res = (ProductInfo) productInfos.get(pk);
145         if (res == null && throwExceptionIfNotExistent)
146             throw new IllegalArgumentException JavaDoc("No ProductInfo existing for product \""+pk+"\"!");
147         return res;
148     }
149
150     public void addProductInfo(ProductInfo productInfo)
151     {
152         productInfos.put(productInfo.getPrimaryKey(), productInfo);
153     }
154
155     /**
156      * This method creates a ProductInfo by looking up and delegating to
157      * the ProductInfoFactory which is associated to the extended ProductInfo.
158      * If there is either no extended ProductInfo or it does not have a
159      * ProductInfoFactory associated, this method creates an instance of
160      * <tt>ProductInfo</tt> (the root object).
161      * <p>
162      * If there exists already a ProductInfo for the given Product, it will
163      * simply be returned without any changes.
164      *
165      * @param product The product for which to create a ProductInfo
166      */

167     public ProductInfo createProductInfo(Product product)
168         throws ModuleException
169     {
170         String JavaDoc pk = product.getPrimaryKey();
171         ProductInfo productInfo = (ProductInfo) productInfos.get(pk);
172
173         if (productInfo == null) {
174             ProductInfo extendedProductInfo = null;
175             ProductInfoFactory productInfoFactory = null;
176             Product extendedProduct = product.getExtendedProduct();
177
178             if (extendedProduct != null)
179                 extendedProductInfo = getProductInfo(extendedProduct, true);
180
181             if (extendedProductInfo != null)
182                 productInfoFactory = extendedProductInfo.getProductInfoFactory();
183
184             if (productInfoFactory != null)
185                 productInfo = productInfoFactory.createProductInfo(product, extendedProductInfo);
186             else
187                 productInfo = new ProductInfo(product, extendedProductInfo);
188
189             addProductInfo(productInfo);
190             updatePackagedProductInfos(productInfo);
191         }
192
193         return productInfo;
194     }
195
196     /**
197      * @return Returns the accountingPriceConfig.
198      */

199     public AccountingPriceConfig getAccountingPriceConfig()
200     {
201         return accountingPriceConfig;
202     }
203
204     /**
205      * This method sets the <tt>packagedProductInfos</tt> to match the
206      * <tt>packagedProducts</tt> of the <tt>Product</tt> that is analog
207      * to the given <tt>ProductInfo</tt>
208      *
209      * @param productInfo The ProductInfo whose packagedProductInfos shall be updated.
210      */

211     public void updatePackagedProductInfos(ProductInfo productInfo)
212     {
213         // Add all missing packaged ProductInfo s.
214

215         // In packagedProductPKs we store the String productPK s that should exist.
216
HashSet JavaDoc packagedProductPKs = new HashSet JavaDoc();
217         for (Iterator JavaDoc it = productInfo.getProduct().getPackagedProducts().iterator(); it.hasNext(); ) {
218             Product packagedProduct = (Product)it.next();
219             packagedProductPKs.add(packagedProduct.getPrimaryKey());
220             ProductInfo packagedProductInfo = productInfo.getPackagedProductInfo(
221                     packagedProduct.getOrganisationID(), packagedProduct.getProductID(), false);
222             if (packagedProductInfo == null) {
223                 packagedProductInfo = getProductInfo(packagedProduct, true);
224                 productInfo.addPackagedProductInfo(packagedProductInfo);
225             }
226         }
227
228
229         // Remove all packaged ProductInfo s which are not registered in packagedProductPKs
230

231         // Because we cannot remove while iterating, we need to store everything that should
232
// be deleted in packagedProductInfosToDelete
233
HashSet JavaDoc packagedProductInfosToDelete = new HashSet JavaDoc();
234         for (Iterator JavaDoc it = productInfo.getPackagedProductInfos().iterator(); it.hasNext(); ) {
235             ProductInfo packagedProductInfo = (ProductInfo)it.next();
236             if (!packagedProductPKs.contains(packagedProductInfo.getPrimaryKey()))
237                 packagedProductInfosToDelete.add(packagedProductInfo);
238         }
239
240         for (Iterator JavaDoc it = packagedProductInfosToDelete.iterator(); it.hasNext(); ) {
241             ProductInfo packagedProductInfo = (ProductInfo)it.next();
242             productInfo.removePackagedProductInfo(
243                     packagedProductInfo.getOrganisationID(), packagedProductInfo.getProductID());
244         }
245     }
246
247 /////////// begin implementation of TransferRegistry /////////////
248
/**
249      * @jdo.field persistence-modifier="persistent"
250      */

251     private long nextTransferID = 0;
252
253     /**
254      * key: String transferPrimaryKey {organisationID + / + transferTypeID + / + transferID}<br/>
255      * value: Transfer transfer
256      *
257      * @jdo.field
258      * persistence-modifier="persistent"
259      * collection-type="map"
260      * key-type="java.lang.String"
261      * value-type="Transfer"
262      * dependent="true"
263      *
264      * @jdo.join
265      *
266      * @jdo.map-vendor-extension vendor-name="jpox" key="key-length" value="max 201"
267      */

268     protected Map JavaDoc transfers = new HashMap JavaDoc();
269
270     /**
271      * This method adds an instance of Transfer. This is not necessary, if the Transfer has been created
272      * by this organisation, because every Transfer does a self-registration.
273      *
274      * @param transfer
275      */

276     public void addTransfer(Transfer transfer)
277     {
278         if (transfer == null)
279             throw new NullPointerException JavaDoc("transfer is null!");
280         
281         if (transfer.getOrganisationID() == null)
282             throw new NullPointerException JavaDoc("transfer.organisationID is null!");
283
284         if (transfer.getTransferID() < 0)
285             throw new NullPointerException JavaDoc("transfer.transferID < 0!");
286
287         String JavaDoc key = transfer.getPrimaryKey();
288         if (transfers.containsKey(key))
289             throw new IllegalStateException JavaDoc("Cannot add transfer, because a transfer with the same key already exists!");
290
291         transfers.put(transfer.getPrimaryKey(), transfer);
292     }
293
294     public synchronized long createTransferID(String JavaDoc organisationID, String JavaDoc transferTypeID)
295     {
296         if (organisationID.equals(getOrganisationID()))
297             throw new IllegalArgumentException JavaDoc("This instance of Accounting can only create transferIDs for the organisation \""+getOrganisationID()+"\"!");
298
299         if (!MoneyTransfer.TRANSFERTYPEID.equals(transferTypeID))
300             throw new IllegalArgumentException JavaDoc("This implementation of TransferRegistry manages only Transfers with transferTypeID=\""+MoneyTransfer.TRANSFERTYPEID+"\"!");
301         long res = nextTransferID;
302         nextTransferID = res + 1;
303         return res;
304     }
305 /////////// end implementation of TransferRegistry /////////////
306

307     /**
308      * @jdo.field persistence-modifier="persistent"
309      */

310     private long nextPriceConfigID = 0;
311
312     public synchronized long createPriceConfigID() {
313         long res = nextPriceConfigID;
314         nextPriceConfigID = res + 1;
315         return res;
316     }
317
318     /**
319      * @jdo.field persistence-modifier="persistent"
320      */

321     private long nextInvoiceID = 0;
322     
323     protected synchronized long createInvoiceID() {
324         long res = nextInvoiceID;
325         nextInvoiceID = res + 1;
326         return res;
327     }
328     
329     
330     /**
331      * Creates a new Invoice with the given offerItems.
332      * Checks weather vendor and customer are the same for all involved offers
333      * weather not offerItems are associated to another invoice
334      * and if all offerItems prices are in the same currency. If one check
335      * fails a InvoiceEditException will be thrown.
336      */

337     public Invoice createInvoice(User user, Collection JavaDoc offerItems)
338     throws InvoiceEditException
339     {
340         if (offerItems.size() <= 0)
341             throw new InvoiceEditException(
342                 InvoiceEditException.REASON_NO_OFFERITEMS,
343                 "Can not create an Invoice with no OfferItems!"
344             );
345         
346         // Make sure all offerItems are not yet in an invoice.
347
// all offers have the same vendor and customer
348
// and all offers have the same currency
349
String JavaDoc vendorPK = null;
350         String JavaDoc customerPK = null;
351         LegalEntity customerLE = null;
352         Currency invoiceCurrency = null;
353         for (Iterator JavaDoc iter = offerItems.iterator(); iter.hasNext();) {
354             OfferItem offerItem = (OfferItem) iter.next();
355             
356             if (vendorPK == null)
357                 vendorPK = offerItem.getOffer().getOrder().getVendor().getPrimaryKey();
358             if (customerPK == null) {
359                 customerLE = offerItem.getOffer().getOrder().getCustomer();
360                 customerPK = customerLE.getPrimaryKey();
361             }
362             if (invoiceCurrency == null)
363                 invoiceCurrency = offerItem.getPrice().getCurrency();
364             
365             OfferItemID offerItemID = OfferItemID.create(offerItem.getOrganisationID(),offerItem.getOfferID(),offerItem.getProductOrganisationID(),offerItem.getProductID());
366             Offer itemOffer = offerItem.getOffer();
367             Order itemOrder = itemOffer.getOrder();
368             
369             if (!itemOffer.isConfirmed()) {
370                 throw new InvoiceEditException(
371                     InvoiceEditException.REASON_OFFER_NOT_CONFIRMED,
372                     "At least one involved offer is not confirmed!",
373                     offerItemID
374                 );
375             }
376             
377             if (!vendorPK.equals(itemOrder.getVendor().getPrimaryKey())
378                         ||
379                     !customerPK.equals(itemOrder.getCustomer().getPrimaryKey())
380                     )
381             {
382                 throw new InvoiceEditException(
383                     InvoiceEditException.REASON_ANCHORS_DONT_MATCH,
384                     "Vendor and customer are not equal for all involved orders, can not create Invoice!!"
385                 );
386             }
387             
388             if (offerItem.getInvoice() != null) {
389                 InvoiceID invoiceID = InvoiceID.create(offerItem.getInvoice().getOrganisationID(), offerItem.getInvoice().getInvoiceID());
390                 throw new InvoiceEditException(
391                     InvoiceEditException.REASON_OFFERITEM_ALREADY_IN_INVOICE,
392                     "OfferItem already in an invoice. OfferItem "+offerItemID+", Invoice "+invoiceID,
393                     offerItemID,
394                     invoiceID
395                 );
396             }
397             
398             if (!invoiceCurrency.getCurrencyID().equals(offerItem.getPrice().getCurrency().getCurrencyID()))
399                 throw new InvoiceEditException(
400                     InvoiceEditException.REASON_MULTIPLE_CURRENCIES,
401                     "Can not create an Invoice with more than one Currency!"
402                 );
403         }
404         
405         if (!vendorPK.equals(getMandator().getPrimaryKey()))
406             throw new InvoiceEditException(
407                 InvoiceEditException.REASON_FOREIGN_ORGANISATION,
408                 "Attempt to create a Invoice not with the local organisation as vendor. Vendor is "+vendorPK
409             );
410         
411         Invoice invoice = new Invoice(user, getMandator().getOrganisationID(), createInvoiceID());
412         for (Iterator JavaDoc iter = offerItems.iterator(); iter.hasNext();) {
413             OfferItem offerItem = (OfferItem) iter.next();
414             invoice.addOfferItem(offerItem);
415         }
416         getPersistenceManager().makePersistent(invoice);
417         
418         
419             
420         return invoice;
421     }
422     
423     /**
424      * Books the given Invoice.
425      *
426      * @param initiator
427      * @param invoice
428      */

429     public void bookInvoice(User initiator, Invoice invoice) {
430         
431         LegalEntity from = null;
432         LegalEntity to = null;
433         
434         if (invoice.getPrice().getAmount() >= 0) {
435             from = invoice.getCustomer();
436             to = invoice.getVendor();
437         }
438         else {
439             from = invoice.getVendor();
440             to = invoice.getCustomer();
441         }
442         
443         // create the InterLegalEntityMoneyTransfer with positive amount but in the right direction
444
InterLegalEntityMoneyTransfer interLegalEntityMoneyTransfer = new InterLegalEntityMoneyTransfer(
445             Accounting.getAccounting(getPersistenceManager()),
446             null,
447             initiator,
448             from,
449             to,
450             invoice,
451             invoice.getPrice().getCurrency(),
452             invoice.getPrice().getAmountAbsoluteValue()
453 // invoice.getPrice().getAmount()
454
);
455         from.bookTransfer(interLegalEntityMoneyTransfer);
456         to.bookTransfer(interLegalEntityMoneyTransfer);
457         
458         invoice.setBooked(initiator, true);
459     }
460     
461     /**
462      * Finalizes an invoice and sends it to the involved
463      * organisation if neccessary.
464      *
465      * @param finalizer
466      * @param invoice
467      */

468     public void finalizeInvoice(User finalizer, Invoice invoice) {
469         if (!invoice.getVendor().getPrimaryKey().equals(getMandator().getPrimaryKey()))
470             throw new IllegalArgumentException JavaDoc("Can not finalize an invoice where mandator is not vendor of this invoice!");
471         invoice.setFinalized(finalizer, true);
472         if (invoice.getCustomer() instanceof OrganisationLegalEntity) {
473             // TODO: Put the Invoice in the queue on this organisations server ...
474
}
475     }
476     
477     /**
478      * Finds (and creates if neccessary) the right Account for the given LegalEntity and Currency.
479      *
480      * @param partnerAccountType Will be used as midfix for the account searched for. See {@link Account#PARTNER_ACCOUNT_TYPE_VENDOR} or {@link Account#PARTNER_ACCOUNT_TYPE_CUSTOMER}.
481      * @param partner The legal entity the account should be searched for.
482      * @param currency The currency the account should record.
483      * @return The found or created acccount. Never null.
484      */

485     public Account getPartnerAccount(String JavaDoc partnerAccountType, LegalEntity partner, Currency currency) {
486         if (partner == null)
487             throw new IllegalArgumentException JavaDoc("Parameter partner must not be null!");
488         if (currency == null)
489             throw new IllegalArgumentException JavaDoc("Parameter currency must not be null!");
490         
491         String JavaDoc searchAccountID = partner.getAnchorID()+"-"+partnerAccountType+"-"+currency.getCurrencyID();
492         AnchorID anchorID = AnchorID.create(this.getOrganisationID(), searchAccountID);
493         
494         Account account = null;
495         Object JavaDoc o = null;
496         try {
497             o = getPersistenceManager().getObjectById(anchorID);
498             account = (Account)o;
499         }
500         catch (ClassCastException JavaDoc ce) {
501             IllegalStateException JavaDoc ill = new IllegalStateException JavaDoc("Found persistent object with oid "+anchorID+" but is not of type Account but "+o.getClass().getName());
502             ill.initCause(ce);
503             throw ill;
504         }
505         catch (JDOObjectNotFoundException je) {
506             // account not existing, create it
507
account = new Account(this.getOrganisationID(), searchAccountID, currency, false);
508             getPersistenceManager().makePersistent(account);
509             account.setOwner(partner);
510         }
511         
512         if (account == null)
513             throw new IllegalStateException JavaDoc("Account with oid "+anchorID+" could neither be found nor created!");
514         
515         if (!account.getOwner().getPrimaryKey().equals(partner.getPrimaryKey()))
516             throw new IllegalStateException JavaDoc("A account for oid "+anchorID+" could be found, but its owner is not the partner the search was performed for. Owner: "+account.getOwner().getPrimaryKey()+", Partner: "+partner.getPrimaryKey());
517         
518         return account;
519     }
520 }
521
Popular Tags