GnuCash 2.4.99
gncEntry.c
00001 /********************************************************************\
00002  * gncEntry.c -- the Core Business Entry Interface                  *
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 Derek Atkins
00025  * Author: Derek Atkins <warlord@MIT.EDU>
00026  */
00027 
00028 #include "config.h"
00029 
00030 #include <glib.h>
00031 
00032 #include "gnc-commodity.h"
00033 
00034 #include "gncEntry.h"
00035 #include "gncEntryP.h"
00036 #include "gncInvoice.h"
00037 #include "gncOrder.h"
00038 
00039 struct _gncEntry
00040 {
00041     QofInstance inst;
00042 
00043     Timespec    date;
00044     Timespec    date_entered;
00045     char *      desc;
00046     char *      action;
00047     char *      notes;
00048     gnc_numeric         quantity;
00049 
00050     /* customer invoice data */
00051     Account *   i_account;
00052     gnc_numeric         i_price;
00053     gboolean    i_taxable;
00054     gboolean    i_taxincluded;
00055     GncTaxTable *       i_tax_table;
00056     gnc_numeric         i_discount;
00057     GncAmountType       i_disc_type;
00058     GncDiscountHow i_disc_how;
00059 
00060     /* vendor bill data */
00061     Account *   b_account;
00062     gnc_numeric         b_price;
00063     gboolean    b_taxable;
00064     gboolean    b_taxincluded;
00065     GncTaxTable *       b_tax_table;
00066     gboolean    billable;
00067     GncOwner    billto;
00068 
00069     /* employee bill data */
00070     GncEntryPaymentType b_payment;
00071 
00072     /* my parent(s) */
00073     GncOrder *  order;
00074     GncInvoice *        invoice;
00075     GncInvoice *        bill;
00076 
00077     /* CACHED VALUES */
00078     gboolean    values_dirty;
00079 
00080     /* customer invoice */
00081     gnc_numeric i_value;
00082     gnc_numeric i_value_rounded;
00083     GList *     i_tax_values;
00084     gnc_numeric i_tax_value;
00085     gnc_numeric i_tax_value_rounded;
00086     gnc_numeric i_disc_value;
00087     gnc_numeric i_disc_value_rounded;
00088     Timespec    i_taxtable_modtime;
00089 
00090     /* vendor bill */
00091     gnc_numeric b_value;
00092     gnc_numeric b_value_rounded;
00093     GList *     b_tax_values;
00094     gnc_numeric b_tax_value;
00095     gnc_numeric b_tax_value_rounded;
00096     Timespec    b_taxtable_modtime;
00097 };
00098 
00099 struct _gncEntryClass
00100 {
00101     QofInstanceClass parent_class;
00102 };
00103 
00104 static QofLogModule log_module = GNC_MOD_BUSINESS;
00105 
00106 
00107 /* You must edit the functions in this block in tandem.
00108  * KEEP THIS FUNCTION IN SYNC with the one below! */
00109 const char *
00110 gncEntryDiscountHowToString (GncDiscountHow how)
00111 {
00112     switch (how)
00113     {
00114     case (GNC_DISC_PRETAX):
00115         return "PRETAX";
00116     case (GNC_DISC_SAMETIME):
00117         return "SAMETIME";
00118     case (GNC_DISC_POSTTAX):
00119         return "POSTTAX";
00120     default:
00121         g_warning ("asked to translate unknown discount-how %d.\n", how);
00122         break;
00123     }
00124     return NULL;
00125 }
00126 
00127 /* You must edit the functions in this block in tandem.
00128  * KEEP THIS FUNCTION IN SYNC with the one above! */
00129 gboolean gncEntryDiscountStringToHow (const char *str, GncDiscountHow *how)
00130 {
00131     if (g_strcmp0 ("PRETAX", str) == 0)
00132     {
00133         *how = GNC_DISC_PRETAX;
00134         return TRUE;
00135     }
00136     if (g_strcmp0 ("SAMETIME", str) == 0)
00137     {
00138         *how = GNC_DISC_SAMETIME;
00139         return TRUE;
00140     }
00141     if (g_strcmp0 ("POSTTAX", str) == 0)
00142     {
00143         *how = GNC_DISC_POSTTAX;
00144         return TRUE;
00145     }
00146     g_warning ("asked to translate unknown discount-how string %s.\n",
00147                str ? str : "(null)");
00148 
00149     return FALSE;
00150 }
00151 
00152 /* You must edit the functions in this block in tandem.
00153  * KEEP THIS FUNCTION IN SYNC with the one below! */
00154 const char * gncEntryPaymentTypeToString (GncEntryPaymentType type)
00155 {
00156     switch (type)
00157     {
00158     case (GNC_PAYMENT_CASH):
00159         return "CASH";
00160     case (GNC_PAYMENT_CARD):
00161         return "CARD";
00162     default:
00163         g_warning ("asked to translate unknown payment type %d.\n", type);
00164         break;
00165     }
00166     return NULL ;
00167 }
00168 
00169 /* You must edit the functions in this block in tandem.
00170  * KEEP THIS FUNCTION IN SYNC with the one above! */
00171 gboolean gncEntryPaymentStringToType (const char *str, GncEntryPaymentType *type)
00172 {
00173     if (g_strcmp0 ("CASH", str) == 0)
00174     {
00175         *type = GNC_PAYMENT_CASH;
00176         return TRUE;
00177     }
00178     if (g_strcmp0 ("CARD", str) == 0)
00179     {
00180         *type = GNC_PAYMENT_CARD;
00181         return TRUE;
00182     }
00183     g_warning ("asked to translate unknown discount-how string %s.\n",
00184                str ? str : "(null)");
00185 
00186     return FALSE;
00187 }
00188 
00189 #define _GNC_MOD_NAME GNC_ID_ENTRY
00190 
00191 #define SET_STR(obj, member, str) { \
00192         char * tmp; \
00193         \
00194         if (!safe_strcmp (member, str)) return; \
00195         gncEntryBeginEdit (obj); \
00196         tmp = CACHE_INSERT (str); \
00197         CACHE_REMOVE (member); \
00198         member = tmp; \
00199         }
00200 
00201 G_INLINE_FUNC void mark_entry (GncEntry *entry);
00202 void mark_entry (GncEntry *entry)
00203 {
00204     qof_instance_set_dirty(&entry->inst);
00205     qof_event_gen (&entry->inst, QOF_EVENT_MODIFY, NULL);
00206 }
00207 
00208 /* ================================================================ */
00209 
00210 enum
00211 {
00212     PROP_0,
00213     PROP_DESCRIPTION
00214 };
00215 
00216 /* GObject Initialization */
00217 G_DEFINE_TYPE(GncEntry, gnc_entry, QOF_TYPE_INSTANCE);
00218 
00219 static void
00220 gnc_entry_init(GncEntry* entry)
00221 {
00222 }
00223 
00224 static void
00225 gnc_entry_dispose(GObject *entryp)
00226 {
00227     G_OBJECT_CLASS(gnc_entry_parent_class)->dispose(entryp);
00228 }
00229 
00230 static void
00231 gnc_entry_finalize(GObject* entryp)
00232 {
00233     G_OBJECT_CLASS(gnc_entry_parent_class)->finalize(entryp);
00234 }
00235 
00236 static void
00237 gnc_entry_get_property (GObject         *object,
00238                         guint            prop_id,
00239                         GValue          *value,
00240                         GParamSpec      *pspec)
00241 {
00242     GncEntry *entry;
00243 
00244     g_return_if_fail(GNC_IS_ENTRY(object));
00245 
00246     entry = GNC_ENTRY(object);
00247     switch (prop_id)
00248     {
00249     case PROP_DESCRIPTION:
00250         g_value_set_string(value, entry->desc);
00251         break;
00252     default:
00253         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00254         break;
00255     }
00256 }
00257 
00258 static void
00259 gnc_entry_set_property (GObject         *object,
00260                         guint            prop_id,
00261                         const GValue          *value,
00262                         GParamSpec      *pspec)
00263 {
00264     GncEntry *entry;
00265 
00266     g_return_if_fail(GNC_IS_ENTRY(object));
00267 
00268     entry = GNC_ENTRY(object);
00269     switch (prop_id)
00270     {
00271     case PROP_DESCRIPTION:
00272         gncEntrySetDescription(entry, g_value_get_string(value));
00273         break;
00274     default:
00275         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00276         break;
00277     }
00278 }
00279 
00281 static gchar*
00282 impl_get_display_name(const QofInstance* inst)
00283 {
00284     GncEntry* entry;
00285     gchar* display_name;
00286     gchar* s;
00287 
00288     g_return_val_if_fail(inst != NULL, FALSE);
00289     g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
00290 
00291     entry = GNC_ENTRY(inst);
00292     if (entry->order != NULL)
00293     {
00294         display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->order));
00295         s = g_strdup_printf("Entry in %s", display_name);
00296         g_free(display_name);
00297         return s;
00298     }
00299     if (entry->invoice != NULL)
00300     {
00301         display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->invoice));
00302         s = g_strdup_printf("Entry in %s", display_name);
00303         g_free(display_name);
00304         return s;
00305     }
00306     if (entry->bill != NULL)
00307     {
00308         display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->bill));
00309         s = g_strdup_printf("Entry in %s", display_name);
00310         g_free(display_name);
00311         return s;
00312     }
00313 
00314     return g_strdup_printf("Entry %p", inst);
00315 }
00316 
00318 static gboolean
00319 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
00320 {
00321     GncEntry* entry;
00322 
00323     g_return_val_if_fail(inst != NULL, FALSE);
00324     g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
00325 
00326     entry = GNC_ENTRY(inst);
00327 
00328     if (GNC_IS_ACCOUNT(ref))
00329     {
00330         Account* acc = GNC_ACCOUNT(ref);
00331         return (entry->i_account == acc || entry->b_account == acc);
00332     }
00333     else if (GNC_IS_TAXTABLE(ref))
00334     {
00335         GncTaxTable* tt = GNC_TAXTABLE(ref);
00336         return (entry->i_tax_table == tt || entry->b_tax_table == tt);
00337     }
00338 
00339     return FALSE;
00340 }
00341 
00348 static GList*
00349 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
00350 {
00351     if (!GNC_IS_ACCOUNT(ref) && !GNC_IS_TAXTABLE(ref))
00352     {
00353         return NULL;
00354     }
00355 
00356     return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
00357 }
00358 
00359 static void
00360 gnc_entry_class_init (GncEntryClass *klass)
00361 {
00362     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
00363     QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
00364 
00365     gobject_class->dispose = gnc_entry_dispose;
00366     gobject_class->finalize = gnc_entry_finalize;
00367     gobject_class->set_property = gnc_entry_set_property;
00368     gobject_class->get_property = gnc_entry_get_property;
00369 
00370     qof_class->get_display_name = impl_get_display_name;
00371     qof_class->refers_to_object = impl_refers_to_object;
00372     qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
00373 
00374     g_object_class_install_property
00375     (gobject_class,
00376      PROP_DESCRIPTION,
00377      g_param_spec_string ("description",
00378                           "Entry Description",
00379                           "The description is an arbitrary string "
00380                           "assigned by the user.  It provides identification "
00381                           "for this entry.",
00382                           NULL,
00383                           G_PARAM_READWRITE));
00384 }
00385 
00386 /* Create/Destroy Functions */
00387 GncEntry *gncEntryCreate (QofBook *book)
00388 {
00389     GncEntry *entry;
00390     gnc_numeric zero = gnc_numeric_zero ();
00391 
00392     if (!book) return NULL;
00393 
00394     entry = g_object_new (GNC_TYPE_ENTRY, NULL);
00395     qof_instance_init_data (&entry->inst, _GNC_MOD_NAME, book);
00396 
00397     entry->desc = CACHE_INSERT ("");
00398     entry->action = CACHE_INSERT ("");
00399     entry->notes = CACHE_INSERT ("");
00400     entry->quantity = zero;
00401 
00402     entry->i_price = zero;
00403     entry->i_taxable = TRUE;
00404     entry->i_discount = zero;
00405     entry->i_disc_type = GNC_AMT_TYPE_PERCENT;
00406     entry->i_disc_how = GNC_DISC_PRETAX;
00407 
00408     entry->b_price = zero;
00409     entry->b_taxable = TRUE;
00410     entry->billto.type = GNC_OWNER_CUSTOMER;
00411     entry->b_payment = GNC_PAYMENT_CASH;
00412 
00413     entry->values_dirty = TRUE;
00414 
00415     qof_event_gen (&entry->inst, QOF_EVENT_CREATE, NULL);
00416 
00417     return entry;
00418 }
00419 
00420 void gncEntryDestroy (GncEntry *entry)
00421 {
00422     if (!entry) return;
00423     qof_instance_set_destroying(entry, TRUE);
00424     gncEntryCommitEdit(entry);
00425 }
00426 
00427 static void gncEntryFree (GncEntry *entry)
00428 {
00429     if (!entry) return;
00430 
00431     qof_event_gen (&entry->inst, QOF_EVENT_DESTROY, NULL);
00432 
00433     CACHE_REMOVE (entry->desc);
00434     CACHE_REMOVE (entry->action);
00435     CACHE_REMOVE (entry->notes);
00436     if (entry->i_tax_values)
00437         gncAccountValueDestroy (entry->i_tax_values);
00438     if (entry->b_tax_values)
00439         gncAccountValueDestroy (entry->b_tax_values);
00440     if (entry->i_tax_table)
00441         gncTaxTableDecRef (entry->i_tax_table);
00442     if (entry->b_tax_table)
00443         gncTaxTableDecRef (entry->b_tax_table);
00444 
00445     /* qof_instance_release (&entry->inst); */
00446     g_object_unref (entry);
00447 }
00448 
00449 /* ================================================================ */
00450 /* Set Functions */
00451 
00452 void gncEntrySetDate (GncEntry *entry, Timespec date)
00453 {
00454     gboolean first_date = FALSE;
00455     Timespec zero_time = { 0, 0 };
00456 
00457     if (!entry) return;
00458     if (timespec_equal (&entry->date, &date)) return;
00459     if (timespec_equal (&entry->date, &zero_time))
00460         first_date = TRUE;
00461     gncEntryBeginEdit (entry);
00462     entry->date = date;
00463     mark_entry (entry);
00464     gncEntryCommitEdit (entry);
00465 
00466     /* Don't re-sort the first time we set the date on this entry */
00467     if (!first_date)
00468     {
00469         if (entry->invoice)
00470             gncInvoiceSortEntries(entry->invoice);
00471         if (entry->bill)
00472             gncInvoiceSortEntries(entry->bill);
00473     }
00474 }
00475 
00476 void gncEntrySetDateGDate (GncEntry *entry, const GDate* date)
00477 {
00478     if (!entry || !date || !g_date_valid(date))
00479         return;
00480 
00481     /* Watch out: Here we are deviating from the initial convention that a
00482     GDate always converts to the start time of the day. Instead, the GDate is
00483     converted to "noon" on the respective date. This is not nice, but this
00484     convention was used for the Timespec of GncEntry all the time, so we better
00485     stick to it.*/
00486     gncEntrySetDate(entry, timespecCanonicalDayTime(gdate_to_timespec(*date)));
00487 }
00488 
00489 void gncEntrySetDateEntered (GncEntry *entry, Timespec date)
00490 {
00491     if (!entry) return;
00492     if (timespec_equal (&entry->date_entered, &date)) return;
00493     gncEntryBeginEdit (entry);
00494     entry->date_entered = date;
00495     mark_entry (entry);
00496     gncEntryCommitEdit (entry);
00497 }
00498 
00499 void gncEntrySetDescription (GncEntry *entry, const char *desc)
00500 {
00501     if (!entry || !desc) return;
00502     SET_STR (entry, entry->desc, desc);
00503     mark_entry (entry);
00504     gncEntryCommitEdit (entry);
00505 }
00506 
00507 void gncEntrySetAction (GncEntry *entry, const char *action)
00508 {
00509     if (!entry || !action) return;
00510     SET_STR (entry, entry->action, action);
00511     mark_entry (entry);
00512     gncEntryCommitEdit (entry);
00513 }
00514 
00515 void gncEntrySetNotes (GncEntry *entry, const char *notes)
00516 {
00517     if (!entry || !notes) return;
00518     SET_STR (entry, entry->notes, notes);
00519     mark_entry (entry);
00520     gncEntryCommitEdit (entry);
00521 }
00522 
00523 void gncEntrySetQuantity (GncEntry *entry, gnc_numeric quantity)
00524 {
00525     if (!entry) return;
00526     if (gnc_numeric_eq (entry->quantity, quantity)) return;
00527     gncEntryBeginEdit (entry);
00528     entry->quantity = quantity;
00529     entry->values_dirty = TRUE;
00530     mark_entry (entry);
00531     gncEntryCommitEdit (entry);
00532 }
00533 
00534 void gncEntrySetDocQuantity (GncEntry *entry, gnc_numeric quantity, gboolean is_cn)
00535 {
00536     if (!entry) return;
00537     if (gnc_numeric_eq (entry->quantity, quantity)) return;
00538     gncEntryBeginEdit (entry);
00539     entry->quantity = (is_cn ? gnc_numeric_neg (quantity) : quantity);
00540     entry->values_dirty = TRUE;
00541     mark_entry (entry);
00542     gncEntryCommitEdit (entry);
00543 }
00544 
00545 /* Customer Invoices */
00546 
00547 void gncEntrySetInvAccount (GncEntry *entry, Account *acc)
00548 {
00549     if (!entry) return;
00550     if (entry->i_account == acc) return;
00551     gncEntryBeginEdit (entry);
00552     entry->i_account = acc;
00553     mark_entry (entry);
00554     gncEntryCommitEdit (entry);
00555 }
00556 
00557 void gncEntrySetInvPrice (GncEntry *entry, gnc_numeric price)
00558 {
00559     if (!entry) return;
00560     if (gnc_numeric_eq (entry->i_price, price)) return;
00561     gncEntryBeginEdit (entry);
00562     entry->i_price = price;
00563     entry->values_dirty = TRUE;
00564     mark_entry (entry);
00565     gncEntryCommitEdit (entry);
00566 }
00567 
00568 void gncEntrySetInvTaxable (GncEntry *entry, gboolean taxable)
00569 {
00570     if (!entry) return;
00571     if (entry->i_taxable == taxable) return;
00572     gncEntryBeginEdit (entry);
00573     entry->i_taxable = taxable;
00574     entry->values_dirty = TRUE;
00575     mark_entry (entry);
00576     gncEntryCommitEdit (entry);
00577 }
00578 
00579 void gncEntrySetInvTaxIncluded (GncEntry *entry, gboolean taxincluded)
00580 {
00581     if (!entry) return;
00582     if (entry->i_taxincluded == taxincluded) return;
00583     gncEntryBeginEdit (entry);
00584     entry->i_taxincluded = taxincluded;
00585     entry->values_dirty = TRUE;
00586     mark_entry (entry);
00587     gncEntryCommitEdit (entry);
00588 }
00589 
00590 void gncEntrySetInvTaxTable (GncEntry *entry, GncTaxTable *table)
00591 {
00592     if (!entry) return;
00593     if (entry->i_tax_table == table) return;
00594     gncEntryBeginEdit (entry);
00595     if (entry->i_tax_table)
00596         gncTaxTableDecRef (entry->i_tax_table);
00597     if (table)
00598         gncTaxTableIncRef (table);
00599     entry->i_tax_table = table;
00600     entry->values_dirty = TRUE;
00601     mark_entry (entry);
00602     gncEntryCommitEdit (entry);
00603 }
00604 
00605 void gncEntrySetInvDiscount (GncEntry *entry, gnc_numeric discount)
00606 {
00607     if (!entry) return;
00608     if (gnc_numeric_eq (entry->i_discount, discount)) return;
00609     gncEntryBeginEdit (entry);
00610     entry->i_discount = discount;
00611     entry->values_dirty = TRUE;
00612     mark_entry (entry);
00613     gncEntryCommitEdit (entry);
00614 }
00615 
00616 void gncEntrySetInvDiscountType (GncEntry *entry, GncAmountType type)
00617 {
00618     if (!entry) return;
00619     if (entry->i_disc_type == type) return;
00620 
00621     gncEntryBeginEdit (entry);
00622     entry->i_disc_type = type;
00623     entry->values_dirty = TRUE;
00624     mark_entry (entry);
00625     gncEntryCommitEdit (entry);
00626 }
00627 
00628 void gncEntrySetInvDiscountHow (GncEntry *entry, GncDiscountHow how)
00629 {
00630     if (!entry) return;
00631     if (entry->i_disc_how == how) return;
00632 
00633     gncEntryBeginEdit (entry);
00634     entry->i_disc_how = how;
00635     entry->values_dirty = TRUE;
00636     mark_entry (entry);
00637     gncEntryCommitEdit (entry);
00638 }
00639 
00640 void qofEntrySetInvDiscType (GncEntry *entry, const char *type_string)
00641 {
00642     GncAmountType type;
00643 
00644     if (!entry) return;
00645     gncAmountStringToType(type_string, &type);
00646     if (entry->i_disc_type == type) return;
00647     gncEntryBeginEdit (entry);
00648     entry->i_disc_type = type;
00649     entry->values_dirty = TRUE;
00650     mark_entry (entry);
00651     gncEntryCommitEdit (entry);
00652 
00653 }
00654 
00655 void qofEntrySetInvDiscHow  (GncEntry *entry, const char *type)
00656 {
00657     GncDiscountHow how = GNC_DISC_PRETAX;
00658 
00659     if (!entry) return;
00660     gncEntryBeginEdit (entry);
00661     gncEntryDiscountStringToHow(type, &how);
00662     if (entry->i_disc_how == how) return;
00663     entry->i_disc_how = how;
00664     entry->values_dirty = TRUE;
00665     mark_entry (entry);
00666     gncEntryCommitEdit (entry);
00667 }
00668 
00669 /* Vendor Bills */
00670 
00671 void gncEntrySetBillAccount (GncEntry *entry, Account *acc)
00672 {
00673     if (!entry) return;
00674     if (entry->b_account == acc) return;
00675     gncEntryBeginEdit (entry);
00676     entry->b_account = acc;
00677     mark_entry (entry);
00678     gncEntryCommitEdit (entry);
00679 }
00680 
00681 void gncEntrySetBillPrice (GncEntry *entry, gnc_numeric price)
00682 {
00683     if (!entry) return;
00684     if (gnc_numeric_eq (entry->b_price, price)) return;
00685     gncEntryBeginEdit (entry);
00686     entry->b_price = price;
00687     entry->values_dirty = TRUE;
00688     mark_entry (entry);
00689     gncEntryCommitEdit (entry);
00690 }
00691 
00692 void gncEntrySetBillTaxable (GncEntry *entry, gboolean taxable)
00693 {
00694     if (!entry) return;
00695     if (entry->b_taxable == taxable) return;
00696     gncEntryBeginEdit (entry);
00697     entry->b_taxable = taxable;
00698     entry->values_dirty = TRUE;
00699     mark_entry (entry);
00700     gncEntryCommitEdit (entry);
00701 }
00702 
00703 void gncEntrySetBillTaxIncluded (GncEntry *entry, gboolean taxincluded)
00704 {
00705     if (!entry) return;
00706     if (entry->b_taxincluded == taxincluded) return;
00707     gncEntryBeginEdit (entry);
00708     entry->b_taxincluded = taxincluded;
00709     entry->values_dirty = TRUE;
00710     mark_entry (entry);
00711     gncEntryCommitEdit (entry);
00712 }
00713 
00714 void gncEntrySetBillTaxTable (GncEntry *entry, GncTaxTable *table)
00715 {
00716     if (!entry) return;
00717     if (entry->b_tax_table == table) return;
00718     gncEntryBeginEdit (entry);
00719     if (entry->b_tax_table)
00720         gncTaxTableDecRef (entry->b_tax_table);
00721     if (table)
00722         gncTaxTableIncRef (table);
00723     entry->b_tax_table = table;
00724     entry->values_dirty = TRUE;
00725     mark_entry (entry);
00726     gncEntryCommitEdit (entry);
00727 }
00728 
00729 void gncEntrySetBillable (GncEntry *entry, gboolean billable)
00730 {
00731     if (!entry) return;
00732     if (entry->billable == billable) return;
00733 
00734     gncEntryBeginEdit (entry);
00735     entry->billable = billable;
00736     mark_entry (entry);
00737     gncEntryCommitEdit (entry);
00738 }
00739 
00740 void gncEntrySetBillTo (GncEntry *entry, GncOwner *billto)
00741 {
00742     if (!entry || !billto) return;
00743     if (gncOwnerEqual (&entry->billto, billto)) return;
00744 
00745     gncEntryBeginEdit (entry);
00746     gncOwnerCopy (billto, &entry->billto);
00747     mark_entry (entry);
00748     gncEntryCommitEdit (entry);
00749 }
00750 
00751 void gncEntrySetBillPayment (GncEntry *entry, GncEntryPaymentType type)
00752 {
00753     if (!entry) return;
00754     if (entry->b_payment == type) return;
00755     gncEntryBeginEdit (entry);
00756     entry->b_payment = type;
00757     mark_entry (entry);
00758     gncEntryCommitEdit (entry);
00759 }
00760 
00761 /* Called from gncOrder when we're added to the Order */
00762 void gncEntrySetOrder (GncEntry *entry, GncOrder *order)
00763 {
00764     if (!entry) return;
00765     if (entry->order == order) return;
00766     gncEntryBeginEdit (entry);
00767     entry->order = order;
00768     mark_entry (entry);
00769     gncEntryCommitEdit (entry);
00770 
00771     /* Generate an event modifying the Order's end-owner */
00772 #if 0
00773     qof_event_gen (gncOwnerGetEndGUID (gncOrderGetOwner (order)),
00774                    QOF_EVENT_MODIFY, NULL);
00775 #endif
00776 }
00777 
00778 /* called from gncInvoice when we're added to the Invoice */
00779 void gncEntrySetInvoice (GncEntry *entry, GncInvoice *invoice)
00780 {
00781     if (!entry) return;
00782     if (entry->invoice == invoice) return;
00783     gncEntryBeginEdit (entry);
00784     entry->invoice = invoice;
00785     mark_entry (entry);
00786     gncEntryCommitEdit (entry);
00787 }
00788 
00789 /* called from gncInvoice when we're added to the Invoice/Bill */
00790 void gncEntrySetBill (GncEntry *entry, GncInvoice *bill)
00791 {
00792     if (!entry) return;
00793     if (entry->bill == bill) return;
00794     gncEntryBeginEdit (entry);
00795     entry->bill = bill;
00796     mark_entry (entry);
00797     gncEntryCommitEdit (entry);
00798 }
00799 
00800 void gncEntryCopy (const GncEntry *src, GncEntry *dest)
00801 {
00802     if (!src || !dest) return;
00803 
00804     gncEntryBeginEdit (dest);
00805     dest->date                  = src->date;
00806     dest->date_entered          = src->date_entered; /* ??? */
00807     gncEntrySetDescription (dest, src->desc);
00808     gncEntrySetAction (dest, src->action);
00809     gncEntrySetNotes (dest, src->notes);
00810     dest->quantity              = src->quantity;
00811 
00812     dest->i_account             = src->i_account;
00813     dest->i_price                       = src->i_price;
00814     dest->i_taxable             = src->i_taxable;
00815     dest->i_taxincluded         = src->i_taxincluded;
00816     dest->i_discount            = src->i_discount;
00817     dest->i_disc_type           = src->i_disc_type;
00818     dest->i_disc_how            = src->i_disc_how;
00819 
00820     /* vendor bill data */
00821     dest->b_account             = src->b_account;
00822     dest->b_price                       = src->b_price;
00823     dest->b_taxable             = src->b_taxable;
00824     dest->b_taxincluded         = src->b_taxincluded;
00825     dest->billable              = src->billable;
00826     dest->billto                        = src->billto;
00827 
00828     if (src->i_tax_table)
00829         gncEntrySetInvTaxTable (dest, src->i_tax_table);
00830 
00831     if (src->b_tax_table)
00832         gncEntrySetBillTaxTable (dest, src->b_tax_table);
00833 
00834     if (src->order)
00835         gncOrderAddEntry (src->order, dest);
00836 
00837     if (src->invoice)
00838         gncInvoiceAddEntry (src->invoice, dest);
00839 
00840     if (src->bill)
00841         gncBillAddEntry (src->bill, dest);
00842 
00843     dest->values_dirty = TRUE;
00844     gncEntryCommitEdit (dest);
00845 }
00846 
00847 /* ================================================================ */
00848 /* Get Functions */
00849 
00850 Timespec gncEntryGetDate (const GncEntry *entry)
00851 {
00852     Timespec ts;
00853     ts.tv_sec = 0;
00854     ts.tv_nsec = 0;
00855     if (!entry) return ts;
00856     return entry->date;
00857 }
00858 
00859 GDate gncEntryGetDateGDate(const GncEntry *entry)
00860 {
00861     return timespec_to_gdate(gncEntryGetDate(entry));
00862 }
00863 
00864 Timespec gncEntryGetDateEntered (const GncEntry *entry)
00865 {
00866     Timespec ts;
00867     ts.tv_sec = 0;
00868     ts.tv_nsec = 0;
00869     if (!entry) return ts;
00870     return entry->date_entered;
00871 }
00872 
00873 const char * gncEntryGetDescription (const GncEntry *entry)
00874 {
00875     if (!entry) return NULL;
00876     return entry->desc;
00877 }
00878 
00879 const char * gncEntryGetAction (const GncEntry *entry)
00880 {
00881     if (!entry) return NULL;
00882     return entry->action;
00883 }
00884 
00885 const char * gncEntryGetNotes (const GncEntry *entry)
00886 {
00887     if (!entry) return NULL;
00888     return entry->notes;
00889 }
00890 
00891 gnc_numeric gncEntryGetQuantity (const GncEntry *entry)
00892 {
00893     if (!entry) return gnc_numeric_zero();
00894     return entry->quantity;
00895 }
00896 
00897 gnc_numeric gncEntryGetDocQuantity (const GncEntry *entry, gboolean is_cn)
00898 {
00899     gnc_numeric value = gncEntryGetQuantity (entry);
00900     return (is_cn ? gnc_numeric_neg (value) : value);
00901 }
00902 
00903 /* Customer Invoice */
00904 
00905 Account * gncEntryGetInvAccount (const GncEntry *entry)
00906 {
00907     if (!entry) return NULL;
00908     return entry->i_account;
00909 }
00910 
00911 gnc_numeric gncEntryGetInvPrice (const GncEntry *entry)
00912 {
00913     if (!entry) return gnc_numeric_zero();
00914     return entry->i_price;
00915 }
00916 
00917 gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry)
00918 {
00919     if (!entry) return gnc_numeric_zero();
00920     return entry->i_discount;
00921 }
00922 
00923 GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry)
00924 {
00925     if (!entry) return 0;
00926     return entry->i_disc_type;
00927 }
00928 
00929 GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry)
00930 {
00931     if (!entry) return 0;
00932     return entry->i_disc_how;
00933 }
00934 
00935 char* qofEntryGetInvDiscType (const GncEntry *entry)
00936 {
00937     char *type_string;
00938 
00939     if (!entry) return 0;
00940     type_string = g_strdup(gncAmountTypeToString(entry->i_disc_type));
00941     return type_string;
00942 }
00943 
00944 char* qofEntryGetInvDiscHow (const GncEntry *entry)
00945 {
00946     char *type_string;
00947 
00948     if (!entry) return 0;
00949     type_string = g_strdup(gncEntryDiscountHowToString(entry->i_disc_how));
00950     return type_string;
00951 }
00952 
00953 gboolean gncEntryGetInvTaxable (const GncEntry *entry)
00954 {
00955     if (!entry) return FALSE;
00956     return entry->i_taxable;
00957 }
00958 
00959 gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry)
00960 {
00961     if (!entry) return FALSE;
00962     return entry->i_taxincluded;
00963 }
00964 
00965 GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry)
00966 {
00967     if (!entry) return NULL;
00968     return entry->i_tax_table;
00969 }
00970 
00971 /* vendor bills */
00972 
00973 Account * gncEntryGetBillAccount (const GncEntry *entry)
00974 {
00975     if (!entry) return NULL;
00976     return entry->b_account;
00977 }
00978 
00979 gnc_numeric gncEntryGetBillPrice (const GncEntry *entry)
00980 {
00981     if (!entry) return gnc_numeric_zero();
00982     return entry->b_price;
00983 }
00984 
00985 gboolean gncEntryGetBillTaxable (const GncEntry *entry)
00986 {
00987     if (!entry) return FALSE;
00988     return entry->b_taxable;
00989 }
00990 
00991 gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry)
00992 {
00993     if (!entry) return FALSE;
00994     return entry->b_taxincluded;
00995 }
00996 
00997 GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry)
00998 {
00999     if (!entry) return NULL;
01000     return entry->b_tax_table;
01001 }
01002 
01003 gboolean gncEntryGetBillable (const GncEntry *entry)
01004 {
01005     if (!entry) return FALSE;
01006     return entry->billable;
01007 }
01008 
01009 GncOwner * gncEntryGetBillTo (GncEntry *entry)
01010 {
01011     if (!entry) return NULL;
01012     return &entry->billto;
01013 }
01014 
01015 GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry)
01016 {
01017     if (!entry) return 0;
01018     return entry->b_payment;
01019 }
01020 
01021 GncInvoice * gncEntryGetInvoice (const GncEntry *entry)
01022 {
01023     if (!entry) return NULL;
01024     return entry->invoice;
01025 }
01026 
01027 GncInvoice * gncEntryGetBill (const GncEntry *entry)
01028 {
01029     if (!entry) return NULL;
01030     return entry->bill;
01031 }
01032 
01033 GncOrder * gncEntryGetOrder (const GncEntry *entry)
01034 {
01035     if (!entry) return NULL;
01036     return entry->order;
01037 }
01038 
01039 /* ================================================================ */
01040 /*
01041  * This is the logic of computing the total for an Entry, so you know
01042  * what values to put into various Splits or to display in the ledger.
01043  * In other words, we combine the quantity, unit-price, discount and
01044  * taxes together, depending on various flags.
01045  *
01046  * There are four potential ways to combine these numbers:
01047  * Discount:     Pre-Tax   Post-Tax
01048  *   Tax   :     Included  Not-Included
01049  *
01050  * The process is relatively simple:
01051  *
01052  *  1) compute the aggregate price (price*qty)
01053  *  2) if taxincluded, then back-compute the aggregate pre-tax price
01054  *  3) apply discount and taxes in the appropriate order
01055  *  4) return the requested results.
01056  *
01057  * Step 2 can be done with aggregate taxes; no need to compute them all
01058  * unless the caller asked for the tax_value.
01059  *
01060  * Note that the returned "value" is such that
01061  *   value + tax == "total to pay"
01062  * which means in the case of tax-included that the returned
01063  * "value" may be less than the aggregate price, even without a
01064  * discount.  If you want to display the tax-included value, you need
01065  * to add the value and taxes together.  In other words, the value is
01066  * the amount the merchant gets; the taxes are the amount the gov't
01067  * gets, and the customer pays the sum or value + taxes.
01068  *
01069  * The SCU is the denominator to convert the value.
01070  *
01071  * The discount return value is just for entertainment -- you may want
01072  * to let a consumer know how much they saved.
01073  */
01074 void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price,
01075                            const GncTaxTable *tax_table, gboolean tax_included,
01076                            gnc_numeric discount, GncAmountType discount_type,
01077                            GncDiscountHow discount_how, int SCU,
01078                            gnc_numeric *value, gnc_numeric *discount_value,
01079                            GList **tax_value)
01080 {
01081     gnc_numeric aggregate;
01082     gnc_numeric pretax;
01083     gnc_numeric result;
01084     gnc_numeric tax;
01085     gnc_numeric percent = gnc_numeric_create (100, 1);
01086     gnc_numeric tpercent = gnc_numeric_zero ();
01087     gnc_numeric tvalue = gnc_numeric_zero ();
01088 
01089     GList     * entries = gncTaxTableGetEntries (tax_table);
01090     GList     * node;
01091 
01092     /* Step 1: compute the aggregate price */
01093 
01094     aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01095 
01096     /* Step 2: compute the pre-tax aggregate */
01097 
01098     /* First, compute the aggregate tpercent and tvalue numbers */
01099     for (node = entries; node; node = node->next)
01100     {
01101         GncTaxTableEntry *entry = node->data;
01102         gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
01103 
01104         switch (gncTaxTableEntryGetType (entry))
01105         {
01106         case GNC_AMT_TYPE_VALUE:
01107             tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO,
01108                                       GNC_HOW_DENOM_LCD);
01109             break;
01110         case GNC_AMT_TYPE_PERCENT:
01111             tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO,
01112                                         GNC_HOW_DENOM_LCD);
01113             break;
01114         default:
01115             g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry));
01116             break;
01117         }
01118     }
01119     /* now we need to convert from 5% -> .05 */
01120     tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO,
01121                                 GNC_HOW_DENOM_LCD);
01122 
01123     /* Next, actually compute the pre-tax aggregate value based on the
01124      * taxincluded flag.
01125      */
01126     if (tax_table && tax_included)
01127     {
01128         /* Back-compute the pre-tax aggregate value.
01129          * We know that aggregate = pretax + pretax*tpercent + tvalue, so
01130          * pretax = (aggregate-tvalue)/(1+tpercent)
01131          */
01132         pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO,
01133                                   GNC_HOW_DENOM_LCD);
01134         pretax = gnc_numeric_div (pretax,
01135                                   gnc_numeric_add (tpercent,
01136                                           gnc_numeric_create (1, 1),
01137                                           GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
01138                                   GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01139     }
01140     else
01141     {
01142         pretax = aggregate;
01143     }
01144 
01145     /* Step 3:  apply discount and taxes in the appropriate order */
01146 
01147     /*
01148      * There are two ways to apply discounts and taxes.  In one way, you
01149      * always compute the discount off the pretax number, and compute
01150      * the taxes off of either the pretax value or "pretax-discount"
01151      * value.  In the other way, you always compute the tax on "pretax",
01152      * and compute the discount on either "pretax" or "pretax+taxes".
01153      *
01154      * I don't know which is the "correct" way.
01155      */
01156 
01157     /*
01158      * Type:    discount    tax
01159      * PRETAX   pretax      pretax-discount
01160      * SAMETIME pretax      pretax
01161      * POSTTAX  pretax+tax  pretax
01162      */
01163 
01164     switch (discount_how)
01165     {
01166     case GNC_DISC_PRETAX:
01167     case GNC_DISC_SAMETIME:
01168         /* compute the discount from pretax */
01169 
01170         if (discount_type == GNC_AMT_TYPE_PERCENT)
01171         {
01172             discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
01173                                         GNC_HOW_DENOM_LCD);
01174             discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO,
01175                                         GNC_HOW_DENOM_LCD);
01176         }
01177 
01178         result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01179 
01180         /* Figure out when to apply the tax, pretax or pretax-discount */
01181         if (discount_how == GNC_DISC_PRETAX)
01182             pretax = result;
01183         break;
01184 
01185     case GNC_DISC_POSTTAX:
01186         /* compute discount on pretax+taxes */
01187 
01188         if (discount_type == GNC_AMT_TYPE_PERCENT)
01189         {
01190             gnc_numeric after_tax;
01191 
01192             tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01193             after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01194             after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO,
01195                                          GNC_HOW_DENOM_LCD);
01196             discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
01197                                         GNC_HOW_DENOM_LCD);
01198             discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO,
01199                                         GNC_HOW_DENOM_LCD);
01200         }
01201 
01202         result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01203         break;
01204 
01205     default:
01206         g_warning ("unknown DiscountHow value: %d", discount_how);
01207         break;
01208     }
01209 
01210     /* Step 4:  return the requested results. */
01211 
01212     /* result == amount merchant gets
01213      * discount == amount of discount
01214      * need to compute taxes (based on 'pretax') if the caller wants it.
01215      */
01216 
01217     if (discount_value != NULL)
01218     {
01219         if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND_HALF_UP);
01220         *discount_value = discount;
01221     }
01222 
01223     if (value != NULL)
01224     {
01225         if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND_HALF_UP);
01226         *value = result;
01227     }
01228 
01229     /* Now... Compute the list of tax values (if the caller wants it) */
01230 
01231     if (tax_value != NULL)
01232     {
01233         GList * taxes = NULL;
01234 
01235         for (node = entries; node; node = node->next)
01236         {
01237             GncTaxTableEntry *entry = node->data;
01238             Account *acc = gncTaxTableEntryGetAccount (entry);
01239             gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
01240 
01241             g_return_if_fail (acc);
01242 
01243             switch (gncTaxTableEntryGetType (entry))
01244             {
01245             case GNC_AMT_TYPE_VALUE:
01246                 if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND_HALF_UP);
01247                 taxes = gncAccountValueAdd (taxes, acc, amount);
01248                 break;
01249             case GNC_AMT_TYPE_PERCENT:
01250                 amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO,
01251                                           GNC_HOW_DENOM_LCD);
01252                 tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
01253                 if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND_HALF_UP);
01254                 taxes = gncAccountValueAdd (taxes, acc, tax);
01255                 break;
01256             default:
01257                 break;
01258             }
01259         }
01260         *tax_value = taxes;
01261     }
01262 
01263     return;
01264 }
01265 
01266 static int
01267 get_entry_commodity_denom (const GncEntry *entry)
01268 {
01269     gnc_commodity *c;
01270     if (!entry)
01271         return 0;
01272     if (entry->invoice)
01273     {
01274         c = gncInvoiceGetCurrency (entry->invoice);
01275         if (c)
01276             return (gnc_commodity_get_fraction (c));
01277     }
01278     if (entry->bill)
01279     {
01280         c = gncInvoiceGetCurrency (entry->bill);
01281         if (c)
01282             return (gnc_commodity_get_fraction (c));
01283     }
01284     return 100000;
01285 }
01286 
01287 static void
01288 gncEntryRecomputeValues (GncEntry *entry)
01289 {
01290     int denom;
01291 
01292     /* See if either tax table changed since we last computed values */
01293     if (entry->i_tax_table)
01294     {
01295         Timespec modtime = gncTaxTableLastModified (entry->i_tax_table);
01296         if (timespec_cmp (&entry->i_taxtable_modtime, &modtime))
01297         {
01298             entry->values_dirty = TRUE;
01299             entry->i_taxtable_modtime = modtime;
01300         }
01301     }
01302     if (entry->b_tax_table)
01303     {
01304         Timespec modtime = gncTaxTableLastModified (entry->b_tax_table);
01305         if (timespec_cmp (&entry->b_taxtable_modtime, &modtime))
01306         {
01307             entry->values_dirty = TRUE;
01308             entry->b_taxtable_modtime = modtime;
01309         }
01310     }
01311 
01312     if (!entry->values_dirty)
01313         return;
01314 
01315     /* Clear the last-computed tax values */
01316     if (entry->i_tax_values)
01317     {
01318         gncAccountValueDestroy (entry->i_tax_values);
01319         entry->i_tax_values = NULL;
01320     }
01321     if (entry->b_tax_values)
01322     {
01323         gncAccountValueDestroy (entry->b_tax_values);
01324         entry->b_tax_values = NULL;
01325     }
01326 
01327     /* Determine the commodity denominator */
01328     denom = get_entry_commodity_denom (entry);
01329 
01330     /* Compute the invoice values */
01331     gncEntryComputeValue (entry->quantity, entry->i_price,
01332                           (entry->i_taxable ? entry->i_tax_table : NULL),
01333                           entry->i_taxincluded,
01334                           entry->i_discount, entry->i_disc_type,
01335                           entry->i_disc_how,
01336                           denom,
01337                           &(entry->i_value), &(entry->i_disc_value),
01338                           &(entry->i_tax_values));
01339 
01340     /* Compute the bill values */
01341     gncEntryComputeValue (entry->quantity, entry->b_price,
01342                           (entry->b_taxable ? entry->b_tax_table : NULL),
01343                           entry->b_taxincluded,
01344                           gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
01345                           denom,
01346                           &(entry->b_value), NULL, &(entry->b_tax_values));
01347 
01348     entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom,
01349                              GNC_HOW_RND_ROUND_HALF_UP);
01350     entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom,
01351                                   GNC_HOW_RND_ROUND_HALF_UP);
01352     entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values);
01353     entry->i_tax_value_rounded = gnc_numeric_convert (entry->i_tax_value, denom,
01354                                  GNC_HOW_RND_ROUND_HALF_UP);
01355 
01356     entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom,
01357                              GNC_HOW_RND_ROUND_HALF_UP);
01358     entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values);
01359     entry->b_tax_value_rounded = gnc_numeric_convert (entry->b_tax_value, denom,
01360                                  GNC_HOW_RND_ROUND_HALF_UP);
01361     entry->values_dirty = FALSE;
01362 }
01363 
01364 /* The "Int" functions below are for internal use only.
01365  * Outside this file, use the "Doc" or "Bal" variants found below instead. */
01366 static gnc_numeric gncEntryGetIntValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
01367 {
01368     if (!entry) return gnc_numeric_zero();
01369     gncEntryRecomputeValues (entry);
01370     if (round)
01371         return (is_cust_doc ? entry->i_value_rounded : entry->b_value_rounded);
01372     else
01373         return (is_cust_doc ? entry->i_value : entry->b_value);
01374 }
01375 
01376 static gnc_numeric gncEntryGetIntTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
01377 {
01378     if (!entry) return gnc_numeric_zero();
01379     gncEntryRecomputeValues (entry);
01380     if (round)
01381         return (is_cust_doc ? entry->i_tax_value_rounded : entry->b_tax_value_rounded);
01382     else
01383         return (is_cust_doc ? entry->i_tax_value : entry->b_tax_value);
01384 }
01385 
01386 /* Careful: the returned list is managed by the entry, and will only be valid for a short time */
01387 static AccountValueList * gncEntryGetIntTaxValues (GncEntry *entry, gboolean is_cust_doc)
01388 {
01389     if (!entry) return NULL;
01390     gncEntryRecomputeValues (entry);
01391     return (is_cust_doc ? entry->i_tax_values : entry->b_tax_values);
01392 }
01393 
01394 static gnc_numeric gncEntryGetIntDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
01395 {
01396     if (!entry) return gnc_numeric_zero();
01397     gncEntryRecomputeValues (entry);
01398     if (round)
01399         return (is_cust_doc ? entry->i_disc_value_rounded : gnc_numeric_zero());
01400     else
01401         return (is_cust_doc ? entry->i_disc_value : gnc_numeric_zero());
01402 }
01403 
01404 gnc_numeric gncEntryGetDocValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
01405 {
01406     gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
01407     return (is_cn ? gnc_numeric_neg (value) : value);
01408 }
01409 
01410 gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
01411 {
01412     gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
01413     return (is_cn ? gnc_numeric_neg (value) : value);
01414 }
01415 
01416 /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
01417 AccountValueList * gncEntryGetDocTaxValues (GncEntry *entry, gboolean is_cust_doc, gboolean is_cn)
01418 {
01419     AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
01420     AccountValueList *values = NULL, *node;
01421 
01422     /* Make a copy of the list with negated values if necessary. */
01423     for (node = int_values; node; node = node->next)
01424     {
01425         GncAccountValue *acct_val = node->data;
01426         values = gncAccountValueAdd (values, acct_val->account,
01427                                      (is_cn ? gnc_numeric_neg (acct_val->value)
01428                                       : acct_val->value));
01429     }
01430 
01431     return values;
01432 }
01433 
01434 gnc_numeric gncEntryGetDocDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
01435 {
01436     gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
01437     return (is_cn ? gnc_numeric_neg (value) : value);
01438 }
01439 
01440 gnc_numeric gncEntryGetBalValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
01441 {
01442     gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
01443     return (is_cust_doc ? gnc_numeric_neg (value) : value);
01444 }
01445 
01446 gnc_numeric gncEntryGetBalTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
01447 {
01448     gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
01449     return (is_cust_doc ? gnc_numeric_neg (value) : value);
01450 }
01451 
01452 /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
01453 AccountValueList * gncEntryGetBalTaxValues (GncEntry *entry, gboolean is_cust_doc)
01454 {
01455     AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
01456     AccountValueList *values = NULL, *node;
01457 
01458     /* Make a copy of the list with negated values if necessary. */
01459     for (node = int_values; node; node = node->next)
01460     {
01461         GncAccountValue *acct_val = node->data;
01462         values = gncAccountValueAdd (values, acct_val->account,
01463                                      (is_cust_doc ? gnc_numeric_neg (acct_val->value)
01464                                       : acct_val->value));
01465     }
01466 
01467     return values;
01468 }
01469 
01470 gnc_numeric gncEntryGetBalDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
01471 {
01472     gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
01473     return (is_cust_doc ? gnc_numeric_neg (value) : value);
01474 }
01475 
01476 /* XXX this existence of this routine is just wrong */
01477 gboolean gncEntryIsOpen (const GncEntry *entry)
01478 {
01479     if (!entry) return FALSE;
01480     return (qof_instance_get_editlevel(entry) > 0);
01481 }
01482 
01483 /* ================================================================ */
01484 
01485 void gncEntryBeginEdit (GncEntry *entry)
01486 {
01487     qof_begin_edit(&entry->inst);
01488 }
01489 
01490 static void gncEntryOnError (QofInstance *entry, QofBackendError errcode)
01491 {
01492     PERR("Entry QofBackend Failure: %d", errcode);
01493     gnc_engine_signal_commit_error( errcode );
01494 }
01495 
01496 static void gncEntryOnDone (QofInstance *inst) {}
01497 
01498 static void entry_free (QofInstance *inst)
01499 {
01500     GncEntry *entry = (GncEntry *)inst;
01501     gncEntryFree (entry);
01502 }
01503 
01504 void gncEntryCommitEdit (GncEntry *entry)
01505 {
01506     if (!qof_commit_edit (QOF_INSTANCE(entry))) return;
01507     qof_commit_edit_part2 (&entry->inst, gncEntryOnError,
01508                            gncEntryOnDone, entry_free);
01509 }
01510 
01511 int gncEntryCompare (const GncEntry *a, const GncEntry *b)
01512 {
01513     int compare;
01514 
01515     if (a == b) return 0;
01516     if (!a && b) return -1;
01517     if (a && !b) return 1;
01518 
01519     compare = timespec_cmp (&(a->date), &(b->date));
01520     if (compare) return compare;
01521 
01522     compare = timespec_cmp (&(a->date_entered), &(b->date_entered));
01523     if (compare) return compare;
01524 
01525     compare = safe_strcmp (a->desc, b->desc);
01526     if (compare) return compare;
01527 
01528     compare = safe_strcmp (a->action, b->action);
01529     if (compare) return compare;
01530 
01531     return qof_instance_guid_compare(a, b);
01532 }
01533 
01534 #define CHECK_STRING(X, Y, FIELD) \
01535     if (safe_strcmp((X)->FIELD, (Y)->FIELD) != 0) \
01536     { \
01537         PWARN("%s differ: %s vs %s", #FIELD, (X)->FIELD, (Y)->FIELD); \
01538         return FALSE; \
01539     }
01540 
01541 #define CHECK_ACCOUNT(X, Y, FIELD) \
01542     if (!xaccAccountEqual((X)->FIELD, (Y)->FIELD, TRUE)) \
01543     { \
01544         PWARN("%s differ", #FIELD); \
01545         return FALSE; \
01546     }
01547 
01548 #define CHECK_NUMERIC(X, Y, FIELD) \
01549     if (!gnc_numeric_equal((X)->FIELD, (Y)->FIELD)) \
01550     { \
01551         PWARN("%s differ", #FIELD); \
01552         return FALSE; \
01553     }
01554 
01555 #define CHECK_VALUE(X, Y, FIELD) \
01556     if ((X)->FIELD != (Y)->FIELD) \
01557     { \
01558         PWARN("%s differ", #FIELD); \
01559         return FALSE; \
01560     }
01561 
01562 
01563 /* ============================================================= */
01564 /* Object declaration */
01565 
01566 static void
01567 destroy_entry_on_book_close(QofInstance *ent, gpointer data)
01568 {
01569     GncEntry* entry = GNC_ENTRY(ent);
01570 
01571     gncEntryBeginEdit(entry);
01572     gncEntryDestroy(entry);
01573 }
01574 
01579 static void
01580 gnc_entry_book_end(QofBook* book)
01581 {
01582     QofCollection *col;
01583 
01584     col = qof_book_get_collection(book, GNC_ID_ENTRY);
01585     qof_collection_foreach(col, destroy_entry_on_book_close, NULL);
01586 }
01587 
01588 static QofObject gncEntryDesc =
01589 {
01590     DI(.interface_version = ) QOF_OBJECT_VERSION,
01591     DI(.e_type            = ) _GNC_MOD_NAME,
01592     DI(.type_label        = ) "Order/Invoice/Bill Entry",
01593     DI(.create            = ) (gpointer)gncEntryCreate,
01594     DI(.book_begin        = ) NULL,
01595     DI(.book_end          = ) gnc_entry_book_end,
01596     DI(.is_dirty          = ) qof_collection_is_dirty,
01597     DI(.mark_clean        = ) qof_collection_mark_clean,
01598     DI(.foreach           = ) qof_collection_foreach,
01599     DI(.printable         = ) NULL,
01600     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
01601 };
01602 
01603 gboolean gncEntryRegister (void)
01604 {
01605     static QofParam params[] =
01606     {
01607         { ENTRY_DATE, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDate, (QofSetterFunc)gncEntrySetDate },
01608         { ENTRY_DATE_ENTERED, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDateEntered, (QofSetterFunc)gncEntrySetDateEntered },
01609         { ENTRY_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetDescription, (QofSetterFunc)gncEntrySetDescription },
01610         { ENTRY_ACTION, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetAction, (QofSetterFunc)gncEntrySetAction },
01611         { ENTRY_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetNotes, (QofSetterFunc)gncEntrySetNotes },
01612         { ENTRY_QTY, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetQuantity, (QofSetterFunc)gncEntrySetQuantity },
01613         { ENTRY_IPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetInvPrice, (QofSetterFunc)gncEntrySetInvPrice },
01614         { ENTRY_BPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetBillPrice, (QofSetterFunc)gncEntrySetBillPrice },
01615         { ENTRY_INVOICE, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetInvoice, NULL },
01616         { ENTRY_IACCT, GNC_ID_ACCOUNT,  (QofAccessFunc)gncEntryGetInvAccount,  (QofSetterFunc)gncEntrySetInvAccount  },
01617         { ENTRY_BACCT, GNC_ID_ACCOUNT,  (QofAccessFunc)gncEntryGetBillAccount, (QofSetterFunc)gncEntrySetBillAccount },
01618         { ENTRY_BILL, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetBill, NULL },
01619         {
01620             ENTRY_INV_DISC_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscType,
01621             (QofSetterFunc)qofEntrySetInvDiscType
01622         },
01623         {
01624             ENTRY_INV_DISC_HOW, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscHow,
01625             (QofSetterFunc)qofEntrySetInvDiscHow
01626         },
01627         {
01628             ENTRY_INV_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxable,
01629             (QofSetterFunc)gncEntrySetInvTaxable
01630         },
01631         {
01632             ENTRY_INV_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxIncluded,
01633             (QofSetterFunc)gncEntrySetInvTaxIncluded
01634         },
01635         {
01636             ENTRY_BILL_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxable,
01637             (QofSetterFunc)gncEntrySetBillTaxable
01638         },
01639         {
01640             ENTRY_BILL_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxIncluded,
01641             (QofSetterFunc)gncEntrySetBillTaxIncluded
01642         },
01643         { ENTRY_BILLABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillable, (QofSetterFunc)gncEntrySetBillable },
01644         { ENTRY_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncEntryGetBillTo, (QofSetterFunc)gncEntrySetBillTo },
01645         { ENTRY_ORDER, GNC_ID_ORDER, (QofAccessFunc)gncEntryGetOrder, NULL },
01646         { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
01647         { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
01648         { NULL },
01649     };
01650 
01651     qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncEntryCompare, params);
01652 
01653     return qof_object_register (&gncEntryDesc);
01654 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines