GnuCash 2.4.99
gncInvoice.c
00001 /********************************************************************\
00002  * gncInvoice.c -- the Core Business Invoice                        *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020  *                                                                  *
00021 \********************************************************************/
00022 
00023 /*
00024  * Copyright (C) 2001,2002,2006 Derek Atkins
00025  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
00026  * Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
00027  * Author: Derek Atkins <warlord@MIT.EDU>
00028  */
00029 
00030 #include "config.h"
00031 
00032 #include <glib.h>
00033 #include <glib/gi18n.h>
00034 
00035 #include "Transaction.h"
00036 #include "Account.h"
00037 #include "gncBillTermP.h"
00038 #include "gncEntry.h"
00039 #include "gncEntryP.h"
00040 #include "gnc-features.h"
00041 #include "gncJobP.h"
00042 #include "gncInvoice.h"
00043 #include "gncInvoiceP.h"
00044 #include "gncOwnerP.h"
00045 
00046 struct _gncInvoice
00047 {
00048     QofInstance   inst;
00049 
00050     char          *id;
00051     char          *notes;
00052     gboolean      active;
00053 
00054     char          *billing_id;
00055     char          *printname;
00056     GncBillTerm   *terms;
00057     GList         *entries;
00058     GList         *prices;
00059     GncOwner      owner;
00060     GncOwner      billto;
00061     GncJob        *job;
00062     Timespec      date_opened;
00063     Timespec      date_posted;
00064 
00065     gnc_numeric   to_charge_amount;
00066 
00067     gnc_commodity *currency;
00068 
00069     Account       *posted_acc;
00070     Transaction   *posted_txn;
00071     GNCLot        *posted_lot;
00072 };
00073 
00074 struct _gncInvoiceClass
00075 {
00076     QofInstanceClass parent_class;
00077 };
00078 
00079 static QofLogModule log_module = GNC_MOD_BUSINESS;
00080 
00081 #define _GNC_MOD_NAME     GNC_ID_INVOICE
00082 
00083 #define GNC_INVOICE_ID    "gncInvoice"
00084 #define GNC_INVOICE_GUID  "invoice-guid"
00085 #define GNC_INVOICE_IS_CN "credit-note"
00086 
00087 #define SET_STR(obj, member, str) { \
00088         char * tmp; \
00089         \
00090         if (!safe_strcmp (member, str)) return; \
00091         gncInvoiceBeginEdit (obj); \
00092         tmp = CACHE_INSERT (str); \
00093         CACHE_REMOVE (member); \
00094         member = tmp; \
00095         }
00096 
00097 static void mark_invoice (GncInvoice *invoice);
00098 static void
00099 mark_invoice (GncInvoice *invoice)
00100 {
00101     qof_instance_set_dirty(&invoice->inst);
00102     qof_event_gen (&invoice->inst, QOF_EVENT_MODIFY, NULL);
00103 }
00104 
00105 QofBook * gncInvoiceGetBook(GncInvoice *x)
00106 {
00107     return qof_instance_get_book(QOF_INSTANCE(x));
00108 }
00109 
00110 /* ================================================================== */
00111 
00112 enum
00113 {
00114     PROP_0,
00115     PROP_NOTES
00116 };
00117 
00118 /* GObject Initialization */
00119 G_DEFINE_TYPE(GncInvoice, gnc_invoice, QOF_TYPE_INSTANCE);
00120 
00121 static void
00122 gnc_invoice_init(GncInvoice* inv)
00123 {
00124 }
00125 
00126 static void
00127 gnc_invoice_dispose(GObject *invp)
00128 {
00129     G_OBJECT_CLASS(gnc_invoice_parent_class)->dispose(invp);
00130 }
00131 
00132 static void
00133 gnc_invoice_finalize(GObject* invp)
00134 {
00135     G_OBJECT_CLASS(gnc_invoice_parent_class)->finalize(invp);
00136 }
00137 
00138 static void
00139 gnc_invoice_get_property (GObject         *object,
00140                           guint            prop_id,
00141                           GValue          *value,
00142                           GParamSpec      *pspec)
00143 {
00144     GncInvoice *inv;
00145 
00146     g_return_if_fail(GNC_IS_INVOICE(object));
00147 
00148     inv = GNC_INVOICE(object);
00149     switch (prop_id)
00150     {
00151     case PROP_NOTES:
00152         g_value_set_string(value, inv->notes);
00153         break;
00154     default:
00155         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00156         break;
00157     }
00158 }
00159 
00160 static void
00161 gnc_invoice_set_property (GObject         *object,
00162                           guint            prop_id,
00163                           const GValue          *value,
00164                           GParamSpec      *pspec)
00165 {
00166     GncInvoice *inv;
00167 
00168     g_return_if_fail(GNC_IS_INVOICE(object));
00169 
00170     inv = GNC_INVOICE(object);
00171     switch (prop_id)
00172     {
00173     case PROP_NOTES:
00174         gncInvoiceSetNotes(inv, g_value_get_string(value));
00175         break;
00176     default:
00177         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00178         break;
00179     }
00180 }
00181 
00183 static gchar*
00184 impl_get_display_name(const QofInstance* inst)
00185 {
00186     GncInvoice* inv;
00187     QofInstance* owner;
00188     gchar* s;
00189 
00190     g_return_val_if_fail(inst != NULL, FALSE);
00191     g_return_val_if_fail(GNC_IS_INVOICE(inst), FALSE);
00192 
00193     inv = GNC_INVOICE(inst);
00194     owner = qofOwnerGetOwner(&inv->owner);
00195     if (owner != NULL)
00196     {
00197         gchar* display_name;
00198 
00199         display_name = qof_instance_get_display_name(owner);
00200         s = g_strdup_printf("Invoice %s (%s)", inv->id, display_name);
00201         g_free(display_name);
00202     }
00203     else
00204     {
00205         s = g_strdup_printf("Invoice %s", inv->id);
00206     }
00207 
00208     return s;
00209 }
00210 
00212 static gboolean
00213 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
00214 {
00215     GncInvoice* inv;
00216 
00217     g_return_val_if_fail(inst != NULL, FALSE);
00218     g_return_val_if_fail(GNC_IS_INVOICE(inst), FALSE);
00219 
00220     inv = GNC_INVOICE(inst);
00221 
00222     if (GNC_IS_BILLTERM(ref))
00223     {
00224         return (inv->terms == GNC_BILLTERM(ref));
00225     }
00226     else if (GNC_IS_JOB(ref))
00227     {
00228         return (inv->job == GNC_JOB(ref));
00229     }
00230     else if (GNC_IS_COMMODITY(ref))
00231     {
00232         return (inv->currency == GNC_COMMODITY(ref));
00233     }
00234     else if (GNC_IS_ACCOUNT(ref))
00235     {
00236         return (inv->posted_acc == GNC_ACCOUNT(ref));
00237     }
00238     else if (GNC_IS_TRANSACTION(ref))
00239     {
00240         return (inv->posted_txn == GNC_TRANSACTION(ref));
00241     }
00242     else if (GNC_IS_LOT(ref))
00243     {
00244         return (inv->posted_lot == GNC_LOT(ref));
00245     }
00246 
00247     return FALSE;
00248 }
00249 
00256 static GList*
00257 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
00258 {
00259     if (!GNC_IS_BILLTERM(ref) && !GNC_IS_JOB(ref) && !GNC_IS_COMMODITY(ref) && !GNC_IS_ACCOUNT(ref)
00260             && !GNC_IS_TRANSACTION(ref) && !GNC_IS_LOT(ref))
00261     {
00262         return NULL;
00263     }
00264 
00265     return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
00266 }
00267 
00268 static void
00269 gnc_invoice_class_init (GncInvoiceClass *klass)
00270 {
00271     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
00272     QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
00273 
00274     gobject_class->dispose = gnc_invoice_dispose;
00275     gobject_class->finalize = gnc_invoice_finalize;
00276     gobject_class->set_property = gnc_invoice_set_property;
00277     gobject_class->get_property = gnc_invoice_get_property;
00278 
00279     qof_class->get_display_name = impl_get_display_name;
00280     qof_class->refers_to_object = impl_refers_to_object;
00281     qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
00282 
00283     g_object_class_install_property
00284     (gobject_class,
00285      PROP_NOTES,
00286      g_param_spec_string ("notes",
00287                           "Invoice Notes",
00288                           "The invoice notes is an arbitrary string "
00289                           "assigned by the user to provide notes regarding "
00290                           "this invoice.",
00291                           NULL,
00292                           G_PARAM_READWRITE));
00293 }
00294 
00295 /* Create/Destroy Functions */
00296 GncInvoice *gncInvoiceCreate (QofBook *book)
00297 {
00298     GncInvoice *invoice;
00299 
00300     if (!book) return NULL;
00301 
00302     invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
00303     qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
00304 
00305     invoice->id = CACHE_INSERT ("");
00306     invoice->notes = CACHE_INSERT ("");
00307     invoice->billing_id = CACHE_INSERT ("");
00308 
00309     invoice->billto.type = GNC_OWNER_CUSTOMER;
00310     invoice->active = TRUE;
00311 
00312     invoice->to_charge_amount = gnc_numeric_zero();
00313 
00314     qof_event_gen (&invoice->inst, QOF_EVENT_CREATE, NULL);
00315 
00316     return invoice;
00317 }
00318 
00319 GncInvoice *gncInvoiceCopy (const GncInvoice *from)
00320 {
00321     GncInvoice *invoice;
00322     QofBook* book;
00323     GList *node;
00324     gint64 is_cn;
00325 
00326     g_assert(from);
00327     book = qof_instance_get_book(from);
00328     g_assert(book);
00329 
00330     invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
00331     qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
00332 
00333     gncInvoiceBeginEdit(invoice);
00334 
00335     invoice->id = CACHE_INSERT (from->id);
00336     invoice->notes = CACHE_INSERT (from->notes);
00337     invoice->billing_id = CACHE_INSERT (from->billing_id);
00338     invoice->active = from->active;
00339 
00340     is_cn = kvp_frame_get_gint64(from->inst.kvp_data, GNC_INVOICE_IS_CN);
00341     kvp_frame_set_gint64(invoice->inst.kvp_data, GNC_INVOICE_IS_CN, is_cn);
00342 
00343     invoice->terms = from->terms;
00344     gncBillTermIncRef (invoice->terms);
00345 
00346     gncOwnerCopy(&from->billto, &invoice->billto);
00347     gncOwnerCopy(&from->owner, &invoice->owner);
00348     invoice->job = from->job; // FIXME: Need IncRef or similar here?!?
00349 
00350     invoice->to_charge_amount = from->to_charge_amount;
00351     invoice->date_opened = from->date_opened;
00352 
00353     // Oops. Do not forget to copy the pointer to the correct currency here.
00354     invoice->currency = from->currency;
00355 
00356     // Copy all invoice->entries
00357     for (node = from->entries; node; node = node->next)
00358     {
00359         GncEntry *from_entry = node->data;
00360         GncEntry *to_entry = gncEntryCreate(book);
00361         gncEntryCopy(from_entry, to_entry);
00362 
00363         switch (gncInvoiceGetOwnerType (invoice))
00364         {
00365         case GNC_OWNER_VENDOR:
00366         case GNC_OWNER_EMPLOYEE:
00367             // this is a vendor bill, or an expense voucher
00368             gncBillAddEntry(invoice, to_entry);
00369             break;
00370         case GNC_OWNER_CUSTOMER:
00371         default:
00372             // this is an invoice
00373             gncInvoiceAddEntry(invoice, to_entry);
00374             break;
00375         }
00376     }
00377 
00378     // FIXME: The prices are not (yet) copied; is this a problem?
00379 
00380     // Posted-date and the posted Txn is intentionally not copied; the
00381     // copy isn't "posted" but needs to be posted by the user.
00382 
00383     gncInvoiceCommitEdit(invoice);
00384 
00385     return invoice;
00386 }
00387 
00388 void gncInvoiceDestroy (GncInvoice *invoice)
00389 {
00390     if (!invoice) return;
00391     qof_instance_set_destroying(invoice, TRUE);
00392     gncInvoiceCommitEdit (invoice);
00393 }
00394 
00395 static void gncInvoiceFree (GncInvoice *invoice)
00396 {
00397     if (!invoice) return;
00398 
00399     qof_event_gen (&invoice->inst, QOF_EVENT_DESTROY, NULL);
00400 
00401     CACHE_REMOVE (invoice->id);
00402     CACHE_REMOVE (invoice->notes);
00403     CACHE_REMOVE (invoice->billing_id);
00404     g_list_free (invoice->entries);
00405     g_list_free (invoice->prices);
00406 
00407     if (invoice->printname) g_free (invoice->printname);
00408 
00409     if (invoice->terms)
00410         gncBillTermDecRef (invoice->terms);
00411 
00412     /* qof_instance_release (&invoice->inst); */
00413     g_object_unref (invoice);
00414 }
00415 
00416 /* ================================================================== */
00417 /* Set Functions */
00418 
00419 void gncInvoiceSetID (GncInvoice *invoice, const char *id)
00420 {
00421     if (!invoice || !id) return;
00422     SET_STR (invoice, invoice->id, id);
00423     mark_invoice (invoice);
00424     gncInvoiceCommitEdit (invoice);
00425 }
00426 
00427 void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner)
00428 {
00429     if (!invoice || !owner) return;
00430     if (gncOwnerEqual (&invoice->owner, owner)) return;
00431     gncInvoiceBeginEdit (invoice);
00432     gncOwnerCopy (owner, &invoice->owner);
00433     mark_invoice (invoice);
00434     gncInvoiceCommitEdit (invoice);
00435 }
00436 
00437 static void
00438 qofInvoiceSetOwner (GncInvoice *invoice, QofInstance *ent)
00439 {
00440     if (!invoice || !ent)
00441     {
00442         return;
00443     }
00444     gncInvoiceBeginEdit (invoice);
00445     qofOwnerSetEntity(&invoice->owner, ent);
00446     mark_invoice (invoice);
00447     gncInvoiceCommitEdit (invoice);
00448 }
00449 
00450 static void
00451 qofInvoiceSetBillTo (GncInvoice *invoice, QofInstance *ent)
00452 {
00453     if (!invoice || !ent)
00454     {
00455         return;
00456     }
00457     gncInvoiceBeginEdit (invoice);
00458     qofOwnerSetEntity(&invoice->billto, ent);
00459     mark_invoice (invoice);
00460     gncInvoiceCommitEdit (invoice);
00461 }
00462 
00463 void gncInvoiceSetDateOpenedGDate (GncInvoice *invoice, const GDate *date)
00464 {
00465     g_assert (date);
00466     gncInvoiceSetDateOpened(invoice, timespecCanonicalDayTime(gdate_to_timespec(*date)));
00467 }
00468 
00469 void gncInvoiceSetDateOpened (GncInvoice *invoice, Timespec date)
00470 {
00471     if (!invoice) return;
00472     if (timespec_equal (&invoice->date_opened, &date)) return;
00473     gncInvoiceBeginEdit (invoice);
00474     invoice->date_opened = date;
00475     mark_invoice (invoice);
00476     gncInvoiceCommitEdit (invoice);
00477 }
00478 
00479 void gncInvoiceSetDatePosted (GncInvoice *invoice, Timespec date)
00480 {
00481     if (!invoice) return;
00482     if (timespec_equal (&invoice->date_posted, &date)) return;
00483     gncInvoiceBeginEdit (invoice);
00484     invoice->date_posted = date;
00485     mark_invoice (invoice);
00486     gncInvoiceCommitEdit (invoice);
00487 }
00488 
00489 void gncInvoiceSetTerms (GncInvoice *invoice, GncBillTerm *terms)
00490 {
00491     if (!invoice) return;
00492     if (invoice->terms == terms) return;
00493     gncInvoiceBeginEdit (invoice);
00494     if (invoice->terms)
00495         gncBillTermDecRef (invoice->terms);
00496     invoice->terms = terms;
00497     if (invoice->terms)
00498         gncBillTermIncRef (invoice->terms);
00499     mark_invoice (invoice);
00500     gncInvoiceCommitEdit (invoice);
00501 }
00502 
00503 void gncInvoiceSetBillingID (GncInvoice *invoice, const char *billing_id)
00504 {
00505     if (!invoice) return;
00506     SET_STR (invoice, invoice->billing_id, billing_id);
00507     mark_invoice (invoice);
00508     gncInvoiceCommitEdit (invoice);
00509 }
00510 
00511 void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes)
00512 {
00513     if (!invoice || !notes) return;
00514     SET_STR (invoice, invoice->notes, notes);
00515     mark_invoice (invoice);
00516     gncInvoiceCommitEdit (invoice);
00517 }
00518 
00519 void gncInvoiceSetActive (GncInvoice *invoice, gboolean active)
00520 {
00521     if (!invoice) return;
00522     if (invoice->active == active) return;
00523     gncInvoiceBeginEdit (invoice);
00524     invoice->active = active;
00525     mark_invoice (invoice);
00526     gncInvoiceCommitEdit (invoice);
00527 }
00528 
00529 void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note)
00530 {
00531     if (!invoice) return;
00532     gncInvoiceBeginEdit (invoice);
00533     kvp_frame_set_gint64(invoice->inst.kvp_data, GNC_INVOICE_IS_CN,
00534                          credit_note ? 1 : 0);
00535     mark_invoice (invoice);
00536     gncInvoiceCommitEdit (invoice);
00537 
00538     /* If this is a credit note, set a feature flag for it in the book
00539      * This will prevent older GnuCash versions that don't support
00540      * credit notes to open this file. */
00541     if (credit_note)
00542         gnc_features_set_used (gncInvoiceGetBook (invoice), GNC_FEATURE_CREDIT_NOTES);
00543 }
00544 
00545 void gncInvoiceSetCurrency (GncInvoice *invoice, gnc_commodity *currency)
00546 {
00547     if (!invoice || !currency) return;
00548     if (invoice->currency &&
00549             gnc_commodity_equal (invoice->currency, currency))
00550         return;
00551     gncInvoiceBeginEdit (invoice);
00552     invoice->currency = currency;
00553     mark_invoice (invoice);
00554     gncInvoiceCommitEdit (invoice);
00555 }
00556 
00557 void gncInvoiceSetBillTo (GncInvoice *invoice, GncOwner *billto)
00558 {
00559     if (!invoice || !billto) return;
00560     if (gncOwnerEqual (&invoice->billto, billto)) return;
00561 
00562     gncInvoiceBeginEdit (invoice);
00563     gncOwnerCopy (billto, &invoice->billto);
00564     mark_invoice (invoice);
00565     gncInvoiceCommitEdit (invoice);
00566 }
00567 
00568 void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount)
00569 {
00570     if (!invoice) return;
00571     if (gnc_numeric_equal (invoice->to_charge_amount, amount)) return;
00572     gncInvoiceBeginEdit (invoice);
00573     invoice->to_charge_amount = amount;
00574     mark_invoice (invoice);
00575     gncInvoiceCommitEdit (invoice);
00576 }
00577 
00578 void gncInvoiceSetPostedTxn (GncInvoice *invoice, Transaction *txn)
00579 {
00580     if (!invoice) return;
00581     g_return_if_fail (invoice->posted_txn == NULL);
00582 
00583     gncInvoiceBeginEdit (invoice);
00584     invoice->posted_txn = txn;
00585     mark_invoice (invoice);
00586     gncInvoiceCommitEdit (invoice);
00587 }
00588 
00589 void gncInvoiceSetPostedLot (GncInvoice *invoice, GNCLot *lot)
00590 {
00591     if (!invoice) return;
00592     g_return_if_fail (invoice->posted_lot == NULL);
00593 
00594     gncInvoiceBeginEdit (invoice);
00595     invoice->posted_lot = lot;
00596     mark_invoice (invoice);
00597     gncInvoiceCommitEdit (invoice);
00598 }
00599 
00600 void gncInvoiceSetPostedAcc (GncInvoice *invoice, Account *acc)
00601 {
00602     if (!invoice) return;
00603     g_return_if_fail (invoice->posted_acc == NULL);
00604 
00605     gncInvoiceBeginEdit (invoice);
00606     invoice->posted_acc = acc;
00607     mark_invoice (invoice);
00608     gncInvoiceCommitEdit (invoice);
00609 }
00610 
00611 void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry)
00612 {
00613     GncInvoice *old;
00614 
00615     g_assert(invoice);
00616     g_assert(entry);
00617     if (!invoice || !entry) return;
00618 
00619     old = gncEntryGetInvoice (entry);
00620     if (old == invoice) return; /* I already own this one */
00621     if (old) gncInvoiceRemoveEntry (old, entry);
00622 
00623     gncEntrySetInvoice (entry, invoice);
00624     invoice->entries = g_list_insert_sorted (invoice->entries, entry,
00625                        (GCompareFunc)gncEntryCompare);
00626     mark_invoice (invoice);
00627 }
00628 
00629 void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry)
00630 {
00631     if (!invoice || !entry) return;
00632 
00633     gncEntrySetInvoice (entry, NULL);
00634     invoice->entries = g_list_remove (invoice->entries, entry);
00635     mark_invoice (invoice);
00636 }
00637 
00638 void gncInvoiceAddPrice (GncInvoice *invoice, GNCPrice *price)
00639 {
00640     if (!invoice || !price) return;
00641 
00642     invoice->prices = g_list_prepend(invoice->prices, price);
00643     mark_invoice (invoice);
00644 }
00645 
00646 void gncBillAddEntry (GncInvoice *bill, GncEntry *entry)
00647 {
00648     GncInvoice *old;
00649 
00650     g_assert(bill);
00651     g_assert(entry);
00652     if (!bill || !entry) return;
00653 
00654     old = gncEntryGetBill (entry);
00655     if (old == bill) return;    /* I already own this one */
00656     if (old) gncBillRemoveEntry (old, entry);
00657 
00658     gncEntrySetBill (entry, bill);
00659     bill->entries = g_list_insert_sorted (bill->entries, entry,
00660                                           (GCompareFunc)gncEntryCompare);
00661     mark_invoice (bill);
00662 }
00663 
00664 void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry)
00665 {
00666     if (!bill || !entry) return;
00667 
00668     gncEntrySetBill (entry, NULL);
00669     bill->entries = g_list_remove (bill->entries, entry);
00670     mark_invoice (bill);
00671 }
00672 
00673 void gncInvoiceSortEntries (GncInvoice *invoice)
00674 {
00675     if (!invoice) return;
00676     invoice->entries = g_list_sort(invoice->entries,
00677                                    (GCompareFunc)gncEntryCompare);
00678     mark_invoice(invoice);
00679 }
00680 
00681 /* ================================================================== */
00682 /* Get Functions */
00683 
00684 const char * gncInvoiceGetID (const GncInvoice *invoice)
00685 {
00686     if (!invoice) return NULL;
00687     return invoice->id;
00688 }
00689 
00690 const GncOwner * gncInvoiceGetOwner (const GncInvoice *invoice)
00691 {
00692     if (!invoice) return NULL;
00693     return &invoice->owner;
00694 }
00695 
00696 static QofInstance*
00697 qofInvoiceGetOwner (GncInvoice *invoice)
00698 {
00699     GncOwner *owner;
00700 
00701     if (!invoice)
00702     {
00703         return NULL;
00704     }
00705     owner = &invoice->owner;
00706     return QOF_INSTANCE(owner);
00707 }
00708 
00709 static QofInstance*
00710 qofInvoiceGetBillTo (GncInvoice *invoice)
00711 {
00712     GncOwner *billto;
00713 
00714     if (!invoice)
00715     {
00716         return NULL;
00717     }
00718     billto = &invoice->billto;
00719     return QOF_INSTANCE(billto);
00720 }
00721 
00722 Timespec gncInvoiceGetDateOpened (const GncInvoice *invoice)
00723 {
00724     Timespec ts;
00725     ts.tv_sec = 0;
00726     ts.tv_nsec = 0;
00727     if (!invoice) return ts;
00728     return invoice->date_opened;
00729 }
00730 
00731 Timespec gncInvoiceGetDatePosted (const GncInvoice *invoice)
00732 {
00733     Timespec ts;
00734     ts.tv_sec = 0;
00735     ts.tv_nsec = 0;
00736     if (!invoice) return ts;
00737     return invoice->date_posted;
00738 }
00739 
00740 Timespec gncInvoiceGetDateDue (const GncInvoice *invoice)
00741 {
00742     Transaction *txn;
00743     Timespec ts;
00744     ts.tv_sec = 0;
00745     ts.tv_nsec = 0;
00746     if (!invoice) return ts;
00747     txn = gncInvoiceGetPostedTxn (invoice);
00748     if (!txn) return ts;
00749     return xaccTransRetDateDueTS (txn);
00750 }
00751 
00752 GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice)
00753 {
00754     if (!invoice) return NULL;
00755     return invoice->terms;
00756 }
00757 
00758 const char * gncInvoiceGetBillingID (const GncInvoice *invoice)
00759 {
00760     if (!invoice) return NULL;
00761     return invoice->billing_id;
00762 }
00763 
00764 const char * gncInvoiceGetNotes (const GncInvoice *invoice)
00765 {
00766     if (!invoice) return NULL;
00767     return invoice->notes;
00768 }
00769 
00770 GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice)
00771 {
00772     const GncOwner *owner;
00773     g_return_val_if_fail (invoice, GNC_OWNER_NONE);
00774 
00775     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
00776     return (gncOwnerGetType (owner));
00777 }
00778 
00779 static gnc_numeric
00780 gncInvoiceGetTotalInternal (GncInvoice *invoice, gboolean use_value,
00781                             gboolean use_tax,
00782                             gboolean use_payment_type, GncEntryPaymentType type)
00783 {
00784     GList *node;
00785     gnc_numeric total = gnc_numeric_zero();
00786     gboolean is_cust_doc, is_cn;
00787 
00788     g_return_val_if_fail (invoice, total);
00789 
00790     /* Is the current document an invoice/credit note related to a customer or a vendor/employee ?
00791      * The GncEntry code needs to know to return the proper entry amounts
00792      */
00793     is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
00794     is_cn = gncInvoiceGetIsCreditNote (invoice);
00795 
00796     for (node = gncInvoiceGetEntries(invoice); node; node = node->next)
00797     {
00798         GncEntry *entry = node->data;
00799         gnc_numeric value, tax;
00800 
00801         if (use_payment_type && gncEntryGetBillPayment (entry) != type)
00802             continue;
00803 
00804         value = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
00805         if (gnc_numeric_check (value) == GNC_ERROR_OK)
00806         {
00807             if (use_value)
00808                 total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
00809         }
00810         else
00811             g_warning ("bad value in our entry");
00812 
00813         if (use_tax)
00814         {
00815             tax = gncEntryGetDocTaxValue (entry, FALSE, is_cust_doc, is_cn);
00816             if (gnc_numeric_check (tax) == GNC_ERROR_OK)
00817                 total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
00818             else
00819                 g_warning ("bad tax-value in our entry");
00820         }
00821     }
00822     return total;
00823 }
00824 
00825 gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice)
00826 {
00827     if (!invoice) return gnc_numeric_zero();
00828     return gncInvoiceGetTotalInternal(invoice, TRUE, TRUE, FALSE, 0);
00829 }
00830 
00831 gnc_numeric gncInvoiceGetTotalSubtotal (GncInvoice *invoice)
00832 {
00833     if (!invoice) return gnc_numeric_zero();
00834     return gncInvoiceGetTotalInternal(invoice, TRUE, FALSE, FALSE, 0);
00835 }
00836 
00837 gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice)
00838 {
00839     if (!invoice) return gnc_numeric_zero();
00840     return gncInvoiceGetTotalInternal(invoice, FALSE, TRUE, FALSE, 0);
00841 }
00842 
00843 gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type)
00844 {
00845     if (!invoice) return gnc_numeric_zero();
00846     return gncInvoiceGetTotalInternal(invoice, TRUE, TRUE, TRUE, type);
00847 }
00848 
00849 GList * gncInvoiceGetTypeListForOwnerType (GncOwnerType type)
00850 {
00851     GList *type_list = NULL;
00852     switch (type)
00853     {
00854     case GNC_OWNER_CUSTOMER:
00855         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_INVOICE));
00856         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_CREDIT_NOTE));
00857         return type_list;
00858     case GNC_OWNER_VENDOR:
00859         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_INVOICE));
00860         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_CREDIT_NOTE));
00861         return type_list;
00862     case GNC_OWNER_EMPLOYEE:
00863         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_INVOICE));
00864         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_CREDIT_NOTE));
00865         return type_list;
00866     default:
00867         return NULL;
00868     }
00869 
00870 }
00871 
00872 GncInvoiceType gncInvoiceGetType (const GncInvoice *invoice)
00873 {
00874     if (!invoice) return GNC_INVOICE_UNDEFINED;
00875     switch (gncInvoiceGetOwnerType (invoice))
00876     {
00877     case GNC_OWNER_CUSTOMER:
00878         return (gncInvoiceGetIsCreditNote(invoice) ?
00879                 GNC_INVOICE_CUST_CREDIT_NOTE :
00880                 GNC_INVOICE_CUST_INVOICE);
00881     case GNC_OWNER_VENDOR:
00882         return (gncInvoiceGetIsCreditNote(invoice) ?
00883                 GNC_INVOICE_VEND_CREDIT_NOTE :
00884                 GNC_INVOICE_VEND_INVOICE);
00885     case GNC_OWNER_EMPLOYEE:
00886         return (gncInvoiceGetIsCreditNote(invoice) ?
00887                 GNC_INVOICE_EMPL_CREDIT_NOTE :
00888                 GNC_INVOICE_EMPL_INVOICE);
00889     default:
00890         PWARN ("No invoice types defined for owner %d",
00891                gncInvoiceGetOwnerType (invoice));
00892         return GNC_INVOICE_UNDEFINED;
00893     }
00894 }
00895 
00896 const char * gncInvoiceGetTypeString (const GncInvoice *invoice)
00897 {
00898     GncInvoiceType type = gncInvoiceGetType(invoice);
00899     switch (type)
00900     {
00901     case GNC_INVOICE_CUST_INVOICE:
00902         return _("Invoice");
00903     case GNC_INVOICE_VEND_INVOICE:
00904         return _("Bill");
00905     case GNC_INVOICE_EMPL_INVOICE:
00906         return _("Expense");
00907     case GNC_INVOICE_CUST_CREDIT_NOTE:
00908         return _("Customer Credit Note");
00909     case GNC_INVOICE_VEND_CREDIT_NOTE:
00910         return _("Vendor Credit Note");
00911     case GNC_INVOICE_EMPL_CREDIT_NOTE:
00912         return _("Employee Credit Note");
00913     default:
00914         PWARN("Unknown invoice type");
00915         return NULL;
00916     }
00917 }
00918 
00919 gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice)
00920 {
00921     if (!invoice) return NULL;
00922     return invoice->currency;
00923 }
00924 
00925 GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice)
00926 {
00927     if (!invoice) return NULL;
00928     return &invoice->billto;
00929 }
00930 
00931 GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice)
00932 {
00933     if (!invoice) return NULL;
00934     return invoice->posted_lot;
00935 }
00936 
00937 Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice)
00938 {
00939     if (!invoice) return NULL;
00940     return invoice->posted_txn;
00941 }
00942 
00943 Account * gncInvoiceGetPostedAcc (const GncInvoice *invoice)
00944 {
00945     if (!invoice) return NULL;
00946     return invoice->posted_acc;
00947 }
00948 
00949 gboolean gncInvoiceGetActive (const GncInvoice *invoice)
00950 {
00951     if (!invoice) return FALSE;
00952     return invoice->active;
00953 }
00954 
00955 gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice)
00956 {
00957     gint64 is_cn;
00958     if (!invoice) return FALSE;
00959     if (kvp_frame_get_gint64(invoice->inst.kvp_data, GNC_INVOICE_IS_CN))
00960         return TRUE;
00961     else
00962         return FALSE;
00963 }
00964 
00965 
00966 gnc_numeric gncInvoiceGetToChargeAmount (const GncInvoice *invoice)
00967 {
00968     if (!invoice) return gnc_numeric_zero();
00969     return invoice->to_charge_amount;
00970 }
00971 
00972 EntryList * gncInvoiceGetEntries (GncInvoice *invoice)
00973 {
00974     if (!invoice) return NULL;
00975     return invoice->entries;
00976 }
00977 
00978 GNCPrice * gncInvoiceGetPrice(GncInvoice *invoice, gnc_commodity *commodity)
00979 {
00980     GList *node = g_list_first(invoice->prices);
00981 
00982     while (node != NULL)
00983     {
00984         GNCPrice *curr = (GNCPrice*)node->data;
00985 
00986         if (gnc_commodity_equal(commodity, gnc_price_get_commodity(curr)))
00987             return curr;
00988 
00989         node = g_list_next(node);
00990     }
00991 
00992     return NULL;
00993 }
00994 
00995 static QofCollection*
00996 qofInvoiceGetEntries (GncInvoice *invoice)
00997 {
00998     QofCollection *entry_coll;
00999     GList         *list;
01000     QofInstance     *entry;
01001 
01002     entry_coll = qof_collection_new(GNC_ID_ENTRY);
01003     for (list = gncInvoiceGetEntries(invoice); list != NULL; list = list->next)
01004     {
01005         entry = QOF_INSTANCE(list->data);
01006         qof_collection_add_entity(entry_coll, entry);
01007     }
01008     return entry_coll;
01009 }
01010 
01011 static void
01012 qofInvoiceEntryCB (QofInstance *ent, gpointer user_data)
01013 {
01014     GncInvoice *invoice;
01015 
01016     invoice = (GncInvoice*)user_data;
01017     if (!invoice || !ent)
01018     {
01019         return;
01020     }
01021     switch (gncInvoiceGetOwnerType (invoice))
01022     {
01023     case GNC_OWNER_VENDOR:
01024     {
01025         gncBillAddEntry (invoice, (GncEntry*) ent);
01026         break;
01027     }
01028     default :
01029     {
01030         gncInvoiceAddEntry(invoice, (GncEntry*)ent);
01031         break;
01032     }
01033     }
01034 }
01035 
01036 static void
01037 qofInvoiceSetEntries(GncInvoice *invoice, QofCollection *entry_coll)
01038 {
01039     if (!entry_coll)
01040     {
01041         return;
01042     }
01043     if (0 == safe_strcmp(qof_collection_get_type(entry_coll), GNC_ID_ENTRY))
01044     {
01045         qof_collection_foreach(entry_coll, qofInvoiceEntryCB, invoice);
01046     }
01047 }
01048 
01049 static GncJob*
01050 qofInvoiceGetJob (const GncInvoice *invoice)
01051 {
01052     if (!invoice)
01053     {
01054         return NULL;
01055     }
01056     return invoice->job;
01057 }
01058 
01059 static void
01060 qofInvoiceSetJob (GncInvoice *invoice, GncJob *job)
01061 {
01062     if (!invoice)
01063     {
01064         return;
01065     }
01066     invoice->job = job;
01067 }
01068 
01069 static void
01070 gncInvoiceDetachFromLot (GNCLot *lot)
01071 {
01072     KvpFrame *kvp;
01073 
01074     if (!lot) return;
01075     gnc_lot_begin_edit (lot);
01076     kvp = gnc_lot_get_slots (lot);
01077     kvp_frame_set_slot_path (kvp, NULL, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
01078     qof_instance_set_dirty (QOF_INSTANCE (lot));
01079     gnc_lot_commit_edit (lot);
01080 }
01081 
01082 static void
01083 gncInvoiceAttachToLot (GncInvoice *invoice, GNCLot *lot)
01084 {
01085     KvpFrame *kvp;
01086     KvpValue *value;
01087 
01088     if (!invoice || !lot)
01089         return;
01090 
01091     if (invoice->posted_lot) return;    /* Cannot reset invoice's lot */
01092 
01093     gnc_lot_begin_edit (lot);
01094     kvp = gnc_lot_get_slots (lot);
01095     value = kvp_value_new_guid (qof_instance_get_guid (QOF_INSTANCE(invoice)));
01096     kvp_frame_set_slot_path (kvp, value, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
01097     qof_instance_set_dirty (QOF_INSTANCE (lot));
01098     gnc_lot_commit_edit (lot);
01099     kvp_value_delete (value);
01100     gncInvoiceSetPostedLot (invoice, lot);
01101 }
01102 
01103 GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot)
01104 {
01105     KvpFrame *kvp;
01106     KvpValue *value;
01107     GncGUID *guid;
01108     QofBook *book;
01109 
01110     if (!lot) return NULL;
01111 
01112     book = gnc_lot_get_book (lot);
01113     kvp = gnc_lot_get_slots (lot);
01114     value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
01115     if (!value) return NULL;
01116 
01117     guid = kvp_value_get_guid (value);
01118     return gncInvoiceLookup(book, guid);
01119 }
01120 
01121 static void
01122 gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn)
01123 {
01124     KvpFrame *kvp;
01125     KvpValue *value;
01126 
01127     if (!invoice || !txn)
01128         return;
01129 
01130     if (invoice->posted_txn) return;    /* Cannot reset invoice's txn */
01131 
01132     xaccTransBeginEdit (txn);
01133     kvp = xaccTransGetSlots (txn);
01134     value = kvp_value_new_guid (qof_instance_get_guid(QOF_INSTANCE(invoice)));
01135     kvp_frame_set_slot_path (kvp, value, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
01136     kvp_value_delete (value);
01137     xaccTransSetTxnType (txn, TXN_TYPE_INVOICE);
01138     xaccTransCommitEdit (txn);
01139     gncInvoiceSetPostedTxn (invoice, txn);
01140 }
01141 
01142 GncInvoice *
01143 gncInvoiceGetInvoiceFromTxn (const Transaction *txn)
01144 {
01145     KvpFrame *kvp;
01146     KvpValue *value;
01147     GncGUID *guid;
01148     QofBook *book;
01149 
01150     if (!txn) return NULL;
01151 
01152     book = xaccTransGetBook (txn);
01153     kvp = xaccTransGetSlots (txn);
01154     value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
01155     if (!value) return NULL;
01156 
01157     guid = kvp_value_get_guid (value);
01158     return gncInvoiceLookup(book, guid);
01159 }
01160 
01161 gboolean gncInvoiceAmountPositive (const GncInvoice *invoice)
01162 {
01163     switch (gncInvoiceGetType (invoice))
01164     {
01165     case GNC_INVOICE_CUST_INVOICE:
01166     case GNC_INVOICE_VEND_CREDIT_NOTE:
01167     case GNC_INVOICE_EMPL_CREDIT_NOTE:
01168         return TRUE;
01169     case GNC_INVOICE_CUST_CREDIT_NOTE:
01170     case GNC_INVOICE_VEND_INVOICE:
01171     case GNC_INVOICE_EMPL_INVOICE:
01172         return FALSE;
01173     case GNC_INVOICE_UNDEFINED:
01174     default:
01175         /* Should never be reached.
01176          * If it is, perhaps a new value is added to GncInvoiceType ? */
01177         g_assert_not_reached();
01178         return FALSE;
01179     }
01180 }
01181 
01182 static gboolean gncInvoicePostAddSplit (QofBook *book,
01183                                         Account *acc,
01184                                         Transaction *txn,
01185                                         gnc_numeric value,
01186                                         const gchar *memo,
01187                                         const gchar *type,
01188                                         GncInvoice *invoice)
01189 {
01190     Split *split;
01191 
01192     split = xaccMallocSplit (book);
01193     /* set action and memo? */
01194 
01195     xaccSplitSetMemo (split, memo);
01196     xaccSplitSetAction (split, type);
01197 
01198     /* Need to insert this split into the account AND txn before
01199      * we set the Base Value.  Otherwise SetBaseValue complains
01200      * that we don't have an account and fails to set the value.
01201      */
01202     xaccAccountBeginEdit (acc);
01203     xaccAccountInsertSplit (acc, split);
01204     xaccAccountCommitEdit (acc);
01205     xaccTransAppendSplit (txn, split);
01206 
01207     /* General note on the split creations below:
01208      * Invoice and bill amounts are always stored as positive values in entries
01209      * So to convert them to proper splits, the amounts may have to be reverted
01210      * to have the proper effect on the account balance.
01211      * Credit notes have the opposite effect of invoices/bills, but their amounts
01212      * are stored as negative values as well. So to convert them into splits
01213      * they can be treated exactly the same as their invoice/bill counter parts.
01214      * The net effect is that the owner type is sufficient to determine whether a
01215      * value has to be reverted when converting an invoice/bill/cn amount to a split.
01216      */
01217     if (gnc_commodity_equal(xaccAccountGetCommodity(acc), invoice->currency))
01218     {
01219         xaccSplitSetBaseValue (split, value,
01220                                invoice->currency);
01221     }
01222     else
01223     {
01224         /*need to do conversion */
01225         GNCPrice *price = gncInvoiceGetPrice(invoice, xaccAccountGetCommodity(acc));
01226 
01227         if (price == NULL)
01228         {
01229             /*This is an error, which shouldn't even be able to happen.
01230               We can't really do anything sensible about it, and this is
01231                         a user-interface free zone so we can't try asking the user
01232               again either, have to return NULL*/
01233             return FALSE;
01234         }
01235         else
01236         {
01237             gnc_numeric converted_amount;
01238             xaccSplitSetValue(split, value);
01239             converted_amount = gnc_numeric_div(value, gnc_price_get_value(price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
01240             DEBUG("converting from %f to %f\n", gnc_numeric_to_double(value), gnc_numeric_to_double(converted_amount));
01241             xaccSplitSetAmount(split, converted_amount);
01242         }
01243     }
01244 
01245     return TRUE;
01246 }
01247 
01248 Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
01249                                        Timespec *post_date, Timespec *due_date,
01250                                        const char * memo, gboolean accumulatesplits)
01251 {
01252     Transaction *txn;
01253     QofBook *book;
01254     GNCLot *lot = NULL;
01255     GList *iter;
01256     GList *splitinfo = NULL;
01257     gnc_numeric total;
01258     gboolean is_cust_doc;
01259     gboolean is_cn;
01260     const char *name, *type;
01261     char *lot_title;
01262     Account *ccard_acct = NULL;
01263     const GncOwner *owner;
01264     gboolean autopay = TRUE; /* FIXME this will have to become a user selectable option at some point */
01265 
01266     if (!invoice || !acc) return NULL;
01267 
01268     gncInvoiceBeginEdit (invoice);
01269     book = qof_instance_get_book(invoice);
01270 
01271     /* Stabilize the Billing Terms of this invoice */
01272     if (invoice->terms)
01273         gncInvoiceSetTerms (invoice,
01274                             gncBillTermReturnChild (invoice->terms, TRUE));
01275 
01276     /* GncEntry functions need to know if the invoice/credit note is for a customer or a vendor/employee. */
01277     is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
01278     is_cn = gncInvoiceGetIsCreditNote (invoice);
01279 
01280     /* Figure out if we need to separate out "credit-card" items */
01281     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
01282     if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE)
01283         ccard_acct = gncEmployeeGetCCard (gncOwnerGetEmployee (owner));
01284 
01285     /* Create a new lot for this invoice */
01286     lot = gnc_lot_new (book);
01287     gnc_lot_begin_edit (lot);
01288 
01289     type = gncInvoiceGetTypeString (invoice);
01290 
01291     /* Set the lot title */
01292     lot_title = g_strdup_printf ("%s %s", type, gncInvoiceGetID (invoice));
01293     gnc_lot_set_title (lot, lot_title);
01294     g_free (lot_title);
01295 
01296     /* Create a new transaction */
01297     txn = xaccMallocTransaction (book);
01298     xaccTransBeginEdit (txn);
01299 
01300     name = gncOwnerGetName (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)));
01301 
01302     /* Set Transaction Description (Owner Name) , Num (invoice ID), Currency */
01303     xaccTransSetDescription (txn, name ? name : "");
01304     xaccTransSetNum (txn, gncInvoiceGetID (invoice));
01305     xaccTransSetCurrency (txn, invoice->currency);
01306 
01307     /* Entered and Posted at date */
01308     xaccTransSetDateEnteredSecs (txn, time(NULL));
01309     if (post_date)
01310     {
01311         xaccTransSetDatePostedTS (txn, post_date);
01312         gncInvoiceSetDatePosted (invoice, *post_date);
01313     }
01314 
01315     if (due_date)
01316         xaccTransSetDateDueTS (txn, due_date);
01317 
01318     /* Iterate through the entries; sum up everything for each account.
01319      * then create the appropriate splits in this txn.
01320      */
01321     total = gnc_numeric_zero();
01322     for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next)
01323     {
01324         gnc_numeric value, tax;
01325         GList *taxes;
01326         GncEntry * entry = iter->data;
01327         Account *this_acc;
01328 
01329         /* Stabilize the TaxTable in this entry */
01330         gncEntryBeginEdit (entry);
01331         if (is_cust_doc)
01332             gncEntrySetInvTaxTable
01333             (entry, gncTaxTableReturnChild (gncEntryGetInvTaxTable (entry), TRUE));
01334         else
01335         {
01336             gncEntrySetBillTaxTable
01337             (entry, gncTaxTableReturnChild (gncEntryGetBillTaxTable (entry), TRUE));
01338 
01339             /* If this is a bill, and the entry came from an invoice originally, copy the price */
01340             if (gncEntryGetBillable (entry))
01341                 gncEntrySetInvPrice (entry, gncEntryGetBillPrice (entry));
01342         }
01343         gncEntryCommitEdit (entry);
01344 
01345         /* Obtain the Entry's Value and TaxValues */
01346         value = gncEntryGetBalValue (entry, FALSE, is_cust_doc);
01347         tax   = gncEntryGetBalTaxValue (entry, FALSE, is_cust_doc);
01348         taxes = gncEntryGetBalTaxValues (entry, is_cust_doc);
01349 
01350         /* add the value for the account split */
01351         this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
01352                     gncEntryGetBillAccount (entry));
01353         if (this_acc)
01354         {
01355             if (gnc_numeric_check (value) == GNC_ERROR_OK)
01356             {
01357                 if (accumulatesplits)
01358                     splitinfo = gncAccountValueAdd (splitinfo, this_acc, value);
01359                 else if (!gncInvoicePostAddSplit (book, this_acc, txn, value,
01360                                                   gncEntryGetDescription (entry),
01361                                                   type, invoice))
01362                 {
01363                     /*This is an error, which shouldn't even be able to happen.
01364                       We can't really do anything sensible about it, and this is
01365                       a user-interface free zone so we can't try asking the user
01366                       again either, have to return NULL*/
01367                     return NULL;
01368                 }
01369 
01370                 /* If there is a credit-card account, and this is a CCard
01371                  * payment type, the don't add it to the total, and instead
01372                  * create a split to the CC Acct with a memo of the entry
01373                  * description instead of the provided memo.  Note that the
01374                  * value reversal is the same as the post account.
01375                  *
01376                  * Note: we don't have to worry about the tax values --
01377                  * expense vouchers don't have them.
01378                  */
01379                 if (ccard_acct && gncEntryGetBillPayment (entry) == GNC_PAYMENT_CARD)
01380                 {
01381                     Split *split;
01382 
01383                     split = xaccMallocSplit (book);
01384                     /* set action? */
01385                     xaccSplitSetMemo (split, gncEntryGetDescription (entry));
01386                     xaccSplitSetAction (split, type);
01387                     xaccAccountBeginEdit (ccard_acct);
01388                     xaccAccountInsertSplit (ccard_acct, split);
01389                     xaccAccountCommitEdit (ccard_acct);
01390                     xaccTransAppendSplit (txn, split);
01391                     xaccSplitSetBaseValue (split, gnc_numeric_neg (value),
01392                                            invoice->currency);
01393 
01394                 }
01395                 else
01396                     total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01397 
01398             }
01399             else
01400                 g_warning ("bad value in our entry");
01401         }
01402 
01403         /* now merge in the TaxValues */
01404         splitinfo = gncAccountValueAddList (splitinfo, taxes);
01405 
01406         /* ... and add the tax total */
01407         if (gnc_numeric_check (tax) == GNC_ERROR_OK)
01408             total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01409         else
01410             g_warning ("bad tax in our entry");
01411 
01412         gncAccountValueDestroy (taxes);
01413     } /* for */
01414 
01415     /* Iterate through the splitinfo list and generate the splits */
01416     for (iter = splitinfo; iter; iter = iter->next)
01417     {
01418         GncAccountValue *acc_val = iter->data;
01419         if (!gncInvoicePostAddSplit (book, acc_val->account, txn, acc_val->value,
01420                                      memo, type, invoice))
01421         {
01422             /*This is an error, which shouldn't even be able to happen.
01423               We can't really do anything sensible about it, and this is
01424               a user-interface free zone so we can't try asking the user
01425               again either, have to return NULL*/
01426             return NULL;
01427         }
01428     }
01429 
01430     /* If there is a ccard account, we may have an additional "to_card" payment.
01431      * we should make that now.
01432      */
01433     if (ccard_acct && !gnc_numeric_zero_p (invoice->to_charge_amount))
01434     {
01435         Split *split = xaccMallocSplit (book);
01436 
01437         /* To charge amount is stored in document value. We need balance value here
01438          * so convert if necessary. */
01439         gnc_numeric to_charge_bal_amount = (is_cn ? gnc_numeric_neg (invoice->to_charge_amount)
01440                                             : invoice->to_charge_amount);
01441 
01442         /* Set memo.  action? */
01443         xaccSplitSetMemo (split, _("Extra to Charge Card"));
01444         xaccSplitSetAction (split, type);
01445 
01446         xaccAccountBeginEdit (ccard_acct);
01447         xaccAccountInsertSplit (ccard_acct, split);
01448         xaccAccountCommitEdit (ccard_acct);
01449         xaccTransAppendSplit (txn, split);
01450         xaccSplitSetBaseValue (split, gnc_numeric_neg (to_charge_bal_amount),
01451                                invoice->currency);
01452 
01453         total = gnc_numeric_sub (total, to_charge_bal_amount,
01454                                  GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01455     }
01456 
01457     /* Now create the Posted split (which is the opposite sign of the above splits) */
01458     {
01459         Split *split = xaccMallocSplit (book);
01460 
01461         /* Set action/memo */
01462         xaccSplitSetMemo (split, memo);
01463         xaccSplitSetAction (split, type);
01464 
01465         xaccAccountBeginEdit (acc);
01466         xaccAccountInsertSplit (acc, split);
01467         xaccAccountCommitEdit (acc);
01468         xaccTransAppendSplit (txn, split);
01469         xaccSplitSetBaseValue (split, gnc_numeric_neg (total),
01470                                invoice->currency);
01471 
01472         /* add this split to the lot */
01473         gnc_lot_add_split (lot, split);
01474     }
01475 
01476     /* Now attach this invoice to the txn, lot, and account */
01477     gncInvoiceAttachToLot (invoice, lot);
01478     gncInvoiceAttachToTxn (invoice, txn);
01479     gncInvoiceSetPostedAcc (invoice, acc);
01480 
01481     xaccTransSetReadOnly (txn, _("Generated from an invoice.  Try unposting the invoice."));
01482     xaccTransCommitEdit (txn);
01483 
01484     gncAccountValueDestroy (splitinfo);
01485 
01486     gnc_lot_commit_edit (lot);
01487     gncInvoiceCommitEdit (invoice);
01488 
01489     /* If requested, attempt to automatically apply open payments
01490      * and reverse documents to this lot to close it (or at least
01491      * reduce its balance) */
01492     if (autopay)
01493         gncInvoiceAutoApplyPayments (invoice);
01494 
01495     return txn;
01496 }
01497 
01498 gboolean
01499 gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables)
01500 {
01501     Transaction *txn;
01502     GNCLot *lot;
01503     GList *lot_split_list, *lot_split_iter;
01504 
01505     if (!invoice) return FALSE;
01506     if (!gncInvoiceIsPosted (invoice)) return FALSE;
01507 
01508     txn = gncInvoiceGetPostedTxn (invoice);
01509     g_return_val_if_fail (txn, FALSE);
01510 
01511     lot = gncInvoiceGetPostedLot (invoice);
01512     g_return_val_if_fail (lot, FALSE);
01513 
01514     /* Destroy the Posted Transaction */
01515     xaccTransClearReadOnly (txn);
01516     xaccTransBeginEdit (txn);
01517     xaccTransDestroy (txn);
01518     xaccTransCommitEdit (txn);
01519 
01520     /* Disconnect the lot from the invoice; re-attach to the invoice owner */
01521     gncInvoiceDetachFromLot (lot);
01522     gncOwnerAttachToLot (&invoice->owner, lot);
01523 
01524     /* Check if this invoice was linked to other lots (payments/inverse signed
01525      * invoices).
01526      * If this is the case, recreate the link transaction between all the remaining lots.
01527      *
01528      * Note that before GnuCash 2.6 payments were not stored in separate lots, but
01529      * always ended up in invoice lots when matched to an invoice. Over-payments
01530      * were copied to a new lot, to which later an invoice was added again and so on.
01531      * These over-payments were handled with automatic payment forward transactions.
01532      * You could consider these transactions to be links between lots as well, but
01533      * to avoid some unexpected behavior, these will not be altered here.
01534      */
01535 
01536     // Note: make a copy of the lot list here, when splits are deleted from the lot,
01537     //       the original list may be destroyed by the lot code.
01538     lot_split_list = g_list_copy (gnc_lot_get_split_list (lot));
01539     for (lot_split_iter = lot_split_list; lot_split_iter; lot_split_iter = lot_split_iter->next)
01540     {
01541         Split *split = lot_split_iter->data;
01542         GList *other_split_list, *list_iter;
01543         Transaction *other_txn = xaccSplitGetParent (split);
01544         GList *lot_list = NULL;
01545 
01546         /* Only work with transactions that link invoices and payments.
01547          * Note: this check also catches the possible case of NULL splits. */
01548         if (xaccTransGetTxnType (other_txn) != TXN_TYPE_LINK)
01549             continue;
01550 
01551         /* Save a list of lots this linking transaction linked to */
01552         other_split_list = xaccTransGetSplitList (other_txn);
01553         for (list_iter = other_split_list; list_iter; list_iter = list_iter->next)
01554         {
01555             Split *other_split = list_iter->data;
01556             GNCLot *other_lot = xaccSplitGetLot (other_split);
01557 
01558             /* Omit the lot we are about to delete */
01559             if (other_lot == lot)
01560                 continue;
01561 
01562             lot_list = g_list_prepend (lot_list, other_lot);
01563         }
01564         /* Maintain original split order */
01565         lot_list = g_list_reverse (lot_list);
01566 
01567         /* Now remove this link transaction. */
01568         xaccTransClearReadOnly (other_txn);
01569         xaccTransBeginEdit (other_txn);
01570         xaccTransDestroy (other_txn);
01571         xaccTransCommitEdit (other_txn);
01572 
01573         /* Re-balance the saved lots as well as is possible */
01574         gncOwnerAutoApplyPaymentsWithLots (&invoice->owner, lot_list);
01575 
01576         /* If any of the saved lots has no more splits, then destroy it.
01577          * Otherwise if any has an invoice associated with it,
01578          * send it a modified event to reset its paid status */
01579         for (list_iter = lot_list; list_iter; list_iter = list_iter->next)
01580         {
01581             GNCLot *other_lot = list_iter->data;
01582             GncInvoice *other_invoice = gncInvoiceGetInvoiceFromLot (other_lot);
01583 
01584             if (!gnc_lot_count_splits (other_lot))
01585                 gnc_lot_destroy (other_lot);
01586             else if (other_invoice)
01587                 qof_event_gen (QOF_INSTANCE(other_invoice), QOF_EVENT_MODIFY, NULL);
01588         }
01589     }
01590     g_list_free (lot_split_list);
01591 
01592     /* If the lot has no splits, then destroy it */
01593     if (!gnc_lot_count_splits (lot))
01594         gnc_lot_destroy (lot);
01595 
01596     /* Clear out the invoice posted information */
01597     gncInvoiceBeginEdit (invoice);
01598 
01599     invoice->posted_acc = NULL;
01600     invoice->posted_txn = NULL;
01601     invoice->posted_lot = NULL;
01602     invoice->date_posted.tv_sec = invoice->date_posted.tv_nsec = 0;
01603 
01604     /* if we've been asked to reset the tax tables, then do so */
01605     if (reset_tax_tables)
01606     {
01607         gboolean is_cust_doc = (gncInvoiceGetOwnerType(invoice) == GNC_OWNER_CUSTOMER);
01608         GList *iter;
01609 
01610         for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next)
01611         {
01612             GncEntry *entry = iter->data;
01613 
01614             gncEntryBeginEdit(entry);
01615             if (is_cust_doc)
01616                 gncEntrySetInvTaxTable(entry,
01617                                        gncTaxTableGetParent(gncEntryGetInvTaxTable(entry)));
01618             else
01619                 gncEntrySetBillTaxTable(entry,
01620                                         gncTaxTableGetParent(gncEntryGetBillTaxTable(entry)));
01621             gncEntryCommitEdit(entry);
01622         }
01623     }
01624 
01625     mark_invoice (invoice);
01626     gncInvoiceCommitEdit (invoice);
01627 
01628     return TRUE;
01629 }
01630 
01631 struct lotmatch
01632 {
01633     const GncOwner *owner;
01634     gboolean positive_balance;
01635 };
01636 
01637 static gboolean
01638 gnc_lot_match_owner_balancing (GNCLot *lot, gpointer user_data)
01639 {
01640     struct lotmatch *lm = user_data;
01641     GncOwner owner_def;
01642     const GncOwner *owner;
01643     gnc_numeric balance = gnc_lot_get_balance (lot);
01644 
01645     /* Could (part of) this lot serve to balance the lot
01646      * for which this query was run ?*/
01647     if (lm->positive_balance == gnc_numeric_positive_p (balance))
01648         return FALSE;
01649 
01650     /* Is it ours? Either the lot owner or the lot invoice owner should match */
01651     if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
01652     {
01653         const GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
01654         if (!invoice)
01655             return FALSE;
01656         owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
01657     }
01658     else
01659         owner = gncOwnerGetEndOwner (&owner_def);
01660 
01661     return gncOwnerEqual (owner, lm->owner);
01662 }
01663 
01664 void gncInvoiceAutoApplyPayments (GncInvoice *invoice)
01665 {
01666     GNCLot *inv_lot;
01667     Account *acct;
01668     const GncOwner *owner;
01669     GList *lot_list;
01670     struct lotmatch lm;
01671 
01672     /* General note: "paying" in this context means balancing
01673      * a lot, by linking opposite signed lots together. So below the term
01674      * "payment" can both mean a true payment or it can mean a document of
01675      * the opposite sign (invoice vs credit note). It just
01676      * depends on what type of document was given as parameter
01677      * to this function. */
01678 
01679     /* Payments can only be applied to posted invoices */
01680     g_return_if_fail (invoice);
01681     g_return_if_fail (invoice->posted_lot);
01682 
01683     inv_lot = invoice->posted_lot;
01684     acct = invoice->posted_acc;
01685     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
01686 
01687     /* Find all lots whose balance (or part of their balance) could be
01688      * used to close this lot.
01689      * To be eligible, the lots have to have an opposite signed balance
01690      * and be for the same owner.
01691      * For example, for an invoice lot, payment lots and credit note lots
01692      * could be used. */
01693     lm.positive_balance =  gnc_numeric_positive_p (gnc_lot_get_balance (inv_lot));
01694     lm.owner = owner;
01695     lot_list = xaccAccountFindOpenLots (acct, gnc_lot_match_owner_balancing,
01696                                         &lm, NULL);
01697 
01698     lot_list = g_list_prepend (lot_list, inv_lot);
01699     gncOwnerAutoApplyPaymentsWithLots (owner, lot_list);
01700     g_list_free (lot_list);
01701 }
01702 
01703 /*
01704  * Create a payment of "amount" for the invoice owner and attempt
01705  * to balance it with the given invoice.
01706  */
01707 void
01708 gncInvoiceApplyPayment (const GncInvoice *invoice, Transaction *txn,
01709                         Account *xfer_acc, gnc_numeric amount,
01710                         gnc_numeric exch, Timespec date,
01711                         const char *memo, const char *num)
01712 {
01713     GNCLot *payment_lot, *invoice_lot;
01714     GList *selected_lots = NULL;
01715     const GncOwner *owner;
01716 
01717     /* Verify our arguments */
01718     if (!invoice || !gncInvoiceIsPosted (invoice) || !xfer_acc) return;
01719 
01720     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
01721     g_return_if_fail (owner->owner.undefined);
01722 
01723     /* Create a lot for this payment */
01724     payment_lot = gncOwnerCreatePaymentLot (owner, txn, invoice->posted_acc, xfer_acc,
01725                                             amount, exch, date, memo, num);
01726 
01727     /* Select the invoice as only payment candidate */
01728     selected_lots = g_list_prepend (selected_lots, invoice->posted_lot);
01729 
01730     /* And link the invoice lot and the payment lot together as well as possible. */
01731     if (payment_lot)
01732         selected_lots = g_list_prepend (selected_lots, payment_lot);
01733     gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
01734 }
01735 
01736 static gboolean gncInvoiceDateExists (const Timespec *date)
01737 {
01738     g_return_val_if_fail (date, FALSE);
01739     if (date->tv_sec || date->tv_nsec) return TRUE;
01740     return FALSE;
01741 }
01742 
01743 gboolean gncInvoiceIsPosted (const GncInvoice *invoice)
01744 {
01745     if (!invoice) return FALSE;
01746     return gncInvoiceDateExists (&(invoice->date_posted));
01747 }
01748 
01749 gboolean gncInvoiceIsPaid (const GncInvoice *invoice)
01750 {
01751     if (!invoice) return FALSE;
01752     if (!invoice->posted_lot) return FALSE;
01753     return gnc_lot_is_closed(invoice->posted_lot);
01754 }
01755 
01756 /* ================================================================== */
01757 
01758 void gncInvoiceBeginEdit (GncInvoice *invoice)
01759 {
01760     qof_begin_edit(&invoice->inst);
01761 }
01762 
01763 static void gncInvoiceOnError (QofInstance *inst, QofBackendError errcode)
01764 {
01765     PERR("Invoice QofBackend Failure: %d", errcode);
01766     gnc_engine_signal_commit_error( errcode );
01767 }
01768 
01769 static void gncInvoiceOnDone (QofInstance *invoice) { }
01770 
01771 static void invoice_free (QofInstance *inst)
01772 {
01773     GncInvoice *invoice = (GncInvoice *) inst;
01774     gncInvoiceFree (invoice);
01775 }
01776 
01777 void gncInvoiceCommitEdit (GncInvoice *invoice)
01778 {
01779     if (!qof_commit_edit (QOF_INSTANCE(invoice))) return;
01780     qof_commit_edit_part2 (&invoice->inst, gncInvoiceOnError,
01781                            gncInvoiceOnDone, invoice_free);
01782 }
01783 
01784 int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b)
01785 {
01786     int compare;
01787 
01788     if (a == b) return 0;
01789     if (!a) return -1;
01790     if (!b) return 1;
01791 
01792     compare = safe_strcmp (a->id, b->id);
01793     if (compare) return compare;
01794 
01795     compare = timespec_cmp (&(a->date_opened), &(b->date_opened));
01796     if (compare) return compare;
01797 
01798     compare = timespec_cmp (&(a->date_posted), &(b->date_posted));
01799     if (compare) return compare;
01800 
01801     return qof_instance_guid_compare(a, b);
01802 }
01803 
01804 gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b)
01805 {
01806     if (a == NULL && b == NULL) return TRUE;
01807     if (a == NULL || b == NULL) return FALSE;
01808 
01809     g_return_val_if_fail(GNC_IS_INVOICE(a), FALSE);
01810     g_return_val_if_fail(GNC_IS_INVOICE(b), FALSE);
01811 
01812     if (safe_strcmp(a->id, b->id) != 0)
01813     {
01814         PWARN("IDs differ: %s vs %s", a->id, b->id);
01815         return FALSE;
01816     }
01817 
01818     if (safe_strcmp(a->notes, b->notes) != 0)
01819     {
01820         PWARN("Notes differ: %s vs %s", a->notes, b->notes);
01821         return FALSE;
01822     }
01823 
01824     if (safe_strcmp(a->billing_id, b->billing_id) != 0)
01825     {
01826         PWARN("Billing IDs differ: %s vs %s", a->billing_id, b->billing_id);
01827         return FALSE;
01828     }
01829 
01830     if (safe_strcmp(a->printname, b->printname) != 0)
01831     {
01832         PWARN("Printnames differ: %s vs %s", a->printname, b->printname);
01833         return FALSE;
01834     }
01835 
01836     if (a->active != b->active)
01837     {
01838         PWARN("Active flags differ");
01839         return FALSE;
01840     }
01841 
01842     if (!gncBillTermEqual(a->terms, b->terms))
01843     {
01844         PWARN("Billterms differ");
01845         return FALSE;
01846     }
01847 
01848     if (!gncJobEqual(a->job, b->job))
01849     {
01850         PWARN("Jobs differ");
01851         return FALSE;
01852     }
01853 
01854     if (!gnc_commodity_equal(a->currency, b->currency))
01855     {
01856         PWARN("Currencies differ");
01857         return FALSE;
01858     }
01859 
01860     if (!xaccAccountEqual(a->posted_acc, b->posted_acc, TRUE))
01861     {
01862         PWARN("Posted accounts differ");
01863         return FALSE;
01864     }
01865 
01866     if (!xaccTransEqual(a->posted_txn, b->posted_txn, TRUE, TRUE, TRUE, FALSE))
01867     {
01868         PWARN("Posted tx differ");
01869         return FALSE;
01870     }
01871 
01872 #if 0
01873     if (!gncLotEqual(a->posted_lot, b->posted_lot))
01874     {
01875         PWARN("Posted lots differ");
01876         return FALSE;
01877     }
01878 #endif
01879 
01880     /* FIXME: Need real checks */
01881 #if 0
01882     GList       *entries;
01883     GList       *prices;
01884     GncOwner    owner;
01885     GncOwner    billto;
01886     Timespec    date_opened;
01887     Timespec    date_posted;
01888 
01889     gnc_numeric to_charge_amount;
01890 #endif
01891 
01892     return TRUE;
01893 }
01894 
01895 /* ============================================================= */
01896 /* Package-Private functions */
01897 
01898 static const char * _gncInvoicePrintable (gpointer obj)
01899 {
01900     GncInvoice *invoice = obj;
01901 
01902     g_return_val_if_fail (invoice, NULL);
01903 
01904     if (qof_instance_get_dirty_flag(invoice) || invoice->printname == NULL)
01905     {
01906         if (invoice->printname) g_free (invoice->printname);
01907 
01908         invoice->printname =
01909             g_strdup_printf ("%s%s", invoice->id,
01910                              gncInvoiceIsPosted (invoice) ? _(" (posted)") : "");
01911     }
01912 
01913     return invoice->printname;
01914 }
01915 
01916 static void
01917 destroy_invoice_on_book_close(QofInstance *ent, gpointer data)
01918 {
01919     GncInvoice* invoice = GNC_INVOICE(ent);
01920 
01921     gncInvoiceBeginEdit(invoice);
01922     gncInvoiceDestroy(invoice);
01923 }
01924 
01925 static void
01926 gnc_invoice_book_end(QofBook* book)
01927 {
01928     QofCollection *col;
01929 
01930     col = qof_book_get_collection(book, GNC_ID_INVOICE);
01931     qof_collection_foreach(col, destroy_invoice_on_book_close, NULL);
01932 }
01933 
01934 static QofObject gncInvoiceDesc =
01935 {
01936     DI(.interface_version = ) QOF_OBJECT_VERSION,
01937     DI(.e_type            = ) _GNC_MOD_NAME,
01938     DI(.type_label        = ) "Invoice",
01939     DI(.create            = ) (gpointer)gncInvoiceCreate,
01940     DI(.book_begin        = ) NULL,
01941     DI(.book_end          = ) gnc_invoice_book_end,
01942     DI(.is_dirty          = ) qof_collection_is_dirty,
01943     DI(.mark_clean        = ) qof_collection_mark_clean,
01944     DI(.foreach           = ) qof_collection_foreach,
01945     DI(.printable         = ) _gncInvoicePrintable,
01946     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
01947 };
01948 
01949 static void
01950 reg_lot (void)
01951 {
01952     static QofParam params[] =
01953     {
01954         {
01955             INVOICE_FROM_LOT, _GNC_MOD_NAME,
01956             (QofAccessFunc)gncInvoiceGetInvoiceFromLot, NULL
01957         },
01958         { NULL },
01959     };
01960 
01961     qof_class_register (GNC_ID_LOT, NULL, params);
01962 }
01963 
01964 static void
01965 reg_txn (void)
01966 {
01967     static QofParam params[] =
01968     {
01969         {
01970             INVOICE_FROM_TXN, _GNC_MOD_NAME,
01971             (QofAccessFunc)gncInvoiceGetInvoiceFromTxn, NULL
01972         },
01973         { NULL },
01974     };
01975 
01976     qof_class_register (GNC_ID_TRANS, NULL, params);
01977 }
01978 
01979 gboolean gncInvoiceRegister (void)
01980 {
01981     static QofParam params[] =
01982     {
01983         { INVOICE_ID,        QOF_TYPE_STRING,  (QofAccessFunc)gncInvoiceGetID,     (QofSetterFunc)gncInvoiceSetID },
01984         { INVOICE_OWNER,     GNC_ID_OWNER,     (QofAccessFunc)gncInvoiceGetOwner, NULL },
01985         { INVOICE_OPENED,    QOF_TYPE_DATE,    (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened },
01986         { INVOICE_DUE,       QOF_TYPE_DATE,    (QofAccessFunc)gncInvoiceGetDateDue, NULL },
01987         { INVOICE_POSTED,    QOF_TYPE_DATE,    (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted },
01988         { INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL },
01989         { INVOICE_IS_PAID,   QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid,    NULL },
01990         { INVOICE_BILLINGID, QOF_TYPE_STRING,  (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
01991         { INVOICE_NOTES,     QOF_TYPE_STRING,  (QofAccessFunc)gncInvoiceGetNotes,   (QofSetterFunc)gncInvoiceSetNotes },
01992         { INVOICE_ACC,       GNC_ID_ACCOUNT,   (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc },
01993         { INVOICE_POST_TXN,  GNC_ID_TRANS,     (QofAccessFunc)gncInvoiceGetPostedTxn, (QofSetterFunc)gncInvoiceSetPostedTxn },
01994         { INVOICE_POST_LOT,  GNC_ID_LOT,       (QofAccessFunc)gncInvoiceGetPostedLot, NULL/*(QofSetterFunc)gncInvoiceSetPostedLot*/ },
01995         { INVOICE_TYPE,      QOF_TYPE_INT32,   (QofAccessFunc)gncInvoiceGetType,    NULL },
01996         { INVOICE_TYPE_STRING, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetTypeString,    NULL },
01997         { INVOICE_TERMS,     GNC_ID_BILLTERM,  (QofAccessFunc)gncInvoiceGetTerms,   (QofSetterFunc)gncInvoiceSetTerms },
01998         { INVOICE_BILLTO,    GNC_ID_OWNER,     (QofAccessFunc)gncInvoiceGetBillTo, NULL  },
01999         { INVOICE_ENTRIES,   QOF_TYPE_COLLECT, (QofAccessFunc)qofInvoiceGetEntries, (QofSetterFunc)qofInvoiceSetEntries },
02000         { INVOICE_JOB,       GNC_ID_JOB,       (QofAccessFunc)qofInvoiceGetJob,     (QofSetterFunc)qofInvoiceSetJob },
02001         { QOF_PARAM_ACTIVE,  QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, (QofSetterFunc)gncInvoiceSetActive },
02002         { INVOICE_IS_CN,     QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetIsCreditNote, (QofSetterFunc)gncInvoiceSetIsCreditNote },
02003         { QOF_PARAM_BOOK,    QOF_ID_BOOK,      (QofAccessFunc)qof_instance_get_book, NULL },
02004         { QOF_PARAM_GUID,    QOF_TYPE_GUID,    (QofAccessFunc)qof_instance_get_guid, NULL },
02005         { NULL },
02006     };
02007 
02008     qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncInvoiceCompare, params);
02009     reg_lot ();
02010     reg_txn ();
02011 
02012     /* Make the compiler happy... */
02013     if (0)
02014     {
02015         qofInvoiceSetEntries(NULL, NULL);
02016         qofInvoiceGetEntries(NULL);
02017         qofInvoiceSetOwner(NULL, NULL);
02018         qofInvoiceGetOwner(NULL);
02019         qofInvoiceSetBillTo(NULL, NULL);
02020         qofInvoiceGetBillTo(NULL);
02021     }
02022     if (!qof_choice_create(GNC_ID_INVOICE))
02023     {
02024         return FALSE;
02025     }
02026     return qof_object_register (&gncInvoiceDesc);
02027 }
02028 
02029 gchar *gncInvoiceNextID (QofBook *book, const GncOwner *owner)
02030 {
02031     gchar *nextID;
02032     switch (gncOwnerGetType(gncOwnerGetEndOwner(owner)))
02033     {
02034     case GNC_OWNER_CUSTOMER:
02035         nextID = qof_book_increment_and_format_counter (book, "gncInvoice");
02036         break;
02037     case GNC_OWNER_VENDOR:
02038         nextID = qof_book_increment_and_format_counter (book, "gncBill");
02039         break;
02040     case GNC_OWNER_EMPLOYEE:
02041         nextID = qof_book_increment_and_format_counter (book, "gncExpVoucher");
02042         break;
02043     default:
02044         nextID = qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
02045         break;
02046     }
02047     return nextID;
02048 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines