LCOV - code coverage report
Current view: top level - libgnucash/engine - gncEntry.c (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 71.7 % 796 571
Test Date: 2026-11-02 18:19:43 Functions: 86.8 % 106 92
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * gncEntry.c -- the Core Business Entry Interface                  *
       3              :  *                                                                  *
       4              :  * This program is free software; you can redistribute it and/or    *
       5              :  * modify it under the terms of the GNU General Public License as   *
       6              :  * published by the Free Software Foundation; either version 2 of   *
       7              :  * the License, or (at your option) any later version.              *
       8              :  *                                                                  *
       9              :  * This program is distributed in the hope that it will be useful,  *
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      12              :  * GNU General Public License for more details.                     *
      13              :  *                                                                  *
      14              :  * You should have received a copy of the GNU General Public License*
      15              :  * along with this program; if not, contact:                        *
      16              :  *                                                                  *
      17              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      18              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      19              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      20              :  *                                                                  *
      21              : \********************************************************************/
      22              : 
      23              : /*
      24              :  * Copyright (C) 2001,2002 Derek Atkins
      25              :  * Author: Derek Atkins <warlord@MIT.EDU>
      26              :  */
      27              : 
      28              : #include <config.h>
      29              : 
      30              : #include <glib.h>
      31              : #include <qofinstance-p.h>
      32              : #include <inttypes.h>
      33              : 
      34              : #include "gnc-commodity.h"
      35              : 
      36              : #include "gncEntry.h"
      37              : #include "gncEntryP.h"
      38              : #include "gnc-features.h"
      39              : #include "gncInvoice.h"
      40              : #include "gncOrder.h"
      41              : 
      42              : struct _gncEntry
      43              : {
      44              :     QofInstance inst;
      45              : 
      46              :     time64      date;
      47              :     time64      date_entered;
      48              :     const char *        desc;
      49              :     const char *        action;
      50              :     const char *        notes;
      51              :     gnc_numeric         quantity;
      52              : 
      53              :     /* customer invoice data */
      54              :     Account *   i_account;
      55              :     gnc_numeric         i_price;
      56              :     gboolean    i_taxable;
      57              :     gboolean    i_taxincluded;
      58              :     GncTaxTable *       i_tax_table;
      59              :     gnc_numeric         i_discount;
      60              :     GncAmountType       i_disc_type;
      61              :     GncDiscountHow i_disc_how;
      62              : 
      63              :     /* vendor bill data */
      64              :     Account *   b_account;
      65              :     gnc_numeric         b_price;
      66              :     gboolean    b_taxable;
      67              :     gboolean    b_taxincluded;
      68              :     GncTaxTable *       b_tax_table;
      69              :     gboolean    billable;
      70              :     GncOwner    billto;
      71              : 
      72              :     /* employee bill data */
      73              :     GncEntryPaymentType b_payment;
      74              : 
      75              :     /* my parent(s) */
      76              :     GncOrder *  order;
      77              :     GncInvoice *        invoice;
      78              :     GncInvoice *        bill;
      79              : 
      80              :     /* CACHED VALUES */
      81              :     gboolean    values_dirty;
      82              : 
      83              :     /* customer invoice */
      84              :     gnc_numeric i_value;
      85              :     gnc_numeric i_value_rounded;
      86              :     GList *     i_tax_values;
      87              :     gnc_numeric i_tax_value;
      88              :     gnc_numeric i_tax_value_rounded;
      89              :     gnc_numeric i_disc_value;
      90              :     gnc_numeric i_disc_value_rounded;
      91              :     time64      i_taxtable_modtime;
      92              : 
      93              :     /* vendor bill */
      94              :     gnc_numeric b_value;
      95              :     gnc_numeric b_value_rounded;
      96              :     GList *     b_tax_values;
      97              :     gnc_numeric b_tax_value;
      98              :     gnc_numeric b_tax_value_rounded;
      99              :     time64      b_taxtable_modtime;
     100              : };
     101              : 
     102              : struct _gncEntryClass
     103              : {
     104              :     QofInstanceClass parent_class;
     105              : };
     106              : 
     107              : static QofLogModule log_module = GNC_MOD_BUSINESS;
     108              : 
     109              : 
     110              : /* You must edit the functions in this block in tandem.
     111              :  * KEEP THIS FUNCTION IN SYNC with the one below! */
     112              : const char *
     113           41 : gncEntryDiscountHowToString (GncDiscountHow how)
     114              : {
     115           41 :     switch (how)
     116              :     {
     117           16 :     case (GNC_DISC_PRETAX):
     118           16 :         return "PRETAX";
     119           15 :     case (GNC_DISC_SAMETIME):
     120           15 :         return "SAMETIME";
     121           10 :     case (GNC_DISC_POSTTAX):
     122           10 :         return "POSTTAX";
     123            0 :     default:
     124            0 :         PWARN ("asked to translate unknown discount-how %d.\n", how);
     125            0 :         break;
     126              :     }
     127            0 :     return NULL;
     128              : }
     129              : 
     130              : /* You must edit the functions in this block in tandem.
     131              :  * KEEP THIS FUNCTION IN SYNC with the one above! */
     132            2 : gboolean gncEntryDiscountStringToHow (const char *str, GncDiscountHow *how)
     133              : {
     134            2 :     if (g_strcmp0 ("PRETAX", str) == 0)
     135              :     {
     136            2 :         *how = GNC_DISC_PRETAX;
     137            2 :         return TRUE;
     138              :     }
     139            0 :     if (g_strcmp0 ("SAMETIME", str) == 0)
     140              :     {
     141            0 :         *how = GNC_DISC_SAMETIME;
     142            0 :         return TRUE;
     143              :     }
     144            0 :     if (g_strcmp0 ("POSTTAX", str) == 0)
     145              :     {
     146            0 :         *how = GNC_DISC_POSTTAX;
     147            0 :         return TRUE;
     148              :     }
     149            0 :     PWARN ("asked to translate unknown discount-how string %s.\n",
     150              :                str ? str : "(null)");
     151              : 
     152            0 :     return FALSE;
     153              : }
     154              : 
     155              : /* You must edit the functions in this block in tandem.
     156              :  * KEEP THIS FUNCTION IN SYNC with the one below! */
     157            2 : const char * gncEntryPaymentTypeToString (GncEntryPaymentType type)
     158              : {
     159            2 :     switch (type)
     160              :     {
     161            2 :     case (GNC_PAYMENT_CASH):
     162            2 :         return "CASH";
     163            0 :     case (GNC_PAYMENT_CARD):
     164            0 :         return "CARD";
     165            0 :     default:
     166            0 :         PWARN ("asked to translate unknown payment type %d.\n", type);
     167            0 :         break;
     168              :     }
     169            0 :     return NULL ;
     170              : }
     171              : 
     172              : /* You must edit the functions in this block in tandem.
     173              :  * KEEP THIS FUNCTION IN SYNC with the one above! */
     174            3 : gboolean gncEntryPaymentStringToType (const char *str, GncEntryPaymentType *type)
     175              : {
     176            3 :     if (g_strcmp0 ("CASH", str) == 0)
     177              :     {
     178            3 :         *type = GNC_PAYMENT_CASH;
     179            3 :         return TRUE;
     180              :     }
     181            0 :     if (g_strcmp0 ("CARD", str) == 0)
     182              :     {
     183            0 :         *type = GNC_PAYMENT_CARD;
     184            0 :         return TRUE;
     185              :     }
     186            0 :     PWARN ("asked to translate unknown discount-how string %s.\n",
     187              :                str ? str : "(null)");
     188              : 
     189            0 :     return FALSE;
     190              : }
     191              : 
     192              : #define _GNC_MOD_NAME GNC_ID_ENTRY
     193              : 
     194              : #define SET_STR(obj, member, str) { \
     195              :         if (!g_strcmp0 (member, str)) return; \
     196              :         gncEntryBeginEdit (obj); \
     197              :         CACHE_REPLACE (member, str); \
     198              :         }
     199              : 
     200              : static inline void mark_entry (GncEntry *entry);
     201         1158 : void mark_entry (GncEntry *entry)
     202              : {
     203         1158 :     qof_instance_set_dirty(&entry->inst);
     204         1158 :     qof_event_gen (&entry->inst, QOF_EVENT_MODIFY, NULL);
     205         1158 : }
     206              : 
     207              : /* ================================================================ */
     208              : 
     209              : enum
     210              : {
     211              :     PROP_0,
     212              : //  PROP_DATE,          /* Table */
     213              : //  PROP_DATE_ENTERED,  /* Table */
     214              :     PROP_DESCRIPTION,   /* Table */
     215              : //  PROP_ACTION,        /* Table */
     216              : //  PROP_NOTES,         /* Table */
     217              : //  PROP_QUANTITY,      /* Table (numeric) */
     218              : //  PROP_I_ACCT,        /* Table */
     219              : //  PROP_I_PRICE,       /* Table (numeric) */
     220              : //  PROP_I_DISCOUNT,    /* Table (numeric) */
     221              : //  PROP_INVOICE,       /* Table */
     222              : //  PROP_I_DISC_TYPE,   /* Table */
     223              : //  PROP_I_DISC_HOW,    /* Table */
     224              : //  PROP_I_TAXABLE,     /* Table */
     225              : //  PROP_I_TAX_INCL,    /* Table */
     226              : //  PROP_I_TAXTABLE,    /* Table */
     227              : //  PROP_B_ACCT,        /* Table */
     228              : //  PROP_B_PRICE,       /* Table (numeric) */
     229              : //  PROP_BILL,          /* Table */
     230              : //  PROP_B_TAXTABLE_1,  /* Table */
     231              : //  PROP_B_TAX_INCL,    /* Table */
     232              : //  PROP_B_TAXTABLE,    /* Table */
     233              : //  PROP_B_PAYTYPE,     /* Table */
     234              : //  PROP_BILLABLE,      /* Table */
     235              : //  PROP_BILLTO_TYPE,   /* Table */
     236              : //  PROP_BILLTO,        /* Table */
     237              : //  PROP_ORDER,         /* Table */
     238              : };
     239              : 
     240              : /* GObject Initialization */
     241          170 : G_DEFINE_TYPE(GncEntry, gnc_entry, QOF_TYPE_INSTANCE)
     242              : 
     243              : static void
     244          121 : gnc_entry_init(GncEntry* entry)
     245              : {
     246          121 : }
     247              : 
     248              : static void
     249           20 : gnc_entry_dispose(GObject *entryp)
     250              : {
     251           20 :     G_OBJECT_CLASS(gnc_entry_parent_class)->dispose(entryp);
     252           20 : }
     253              : 
     254              : static void
     255           20 : gnc_entry_finalize(GObject* entryp)
     256              : {
     257           20 :     G_OBJECT_CLASS(gnc_entry_parent_class)->finalize(entryp);
     258           20 : }
     259              : 
     260              : static void
     261            1 : gnc_entry_get_property (GObject         *object,
     262              :                         guint            prop_id,
     263              :                         GValue          *value,
     264              :                         GParamSpec      *pspec)
     265              : {
     266              :     GncEntry *entry;
     267              : 
     268            1 :     g_return_if_fail(GNC_IS_ENTRY(object));
     269              : 
     270            1 :     entry = GNC_ENTRY(object);
     271            1 :     switch (prop_id)
     272              :     {
     273            1 :     case PROP_DESCRIPTION:
     274            1 :         g_value_set_string(value, entry->desc);
     275            1 :         break;
     276            0 :     default:
     277            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     278            0 :         break;
     279              :     }
     280              : }
     281              : 
     282              : static void
     283            1 : gnc_entry_set_property (GObject         *object,
     284              :                         guint            prop_id,
     285              :                         const GValue          *value,
     286              :                         GParamSpec      *pspec)
     287              : {
     288              :     GncEntry *entry;
     289              : 
     290            1 :     g_return_if_fail(GNC_IS_ENTRY(object));
     291              : 
     292            1 :     entry = GNC_ENTRY(object);
     293            1 :     g_assert (qof_instance_get_editlevel(entry));
     294              : 
     295            1 :     switch (prop_id)
     296              :     {
     297            1 :     case PROP_DESCRIPTION:
     298            1 :         gncEntrySetDescription(entry, g_value_get_string(value));
     299            1 :         break;
     300            0 :     default:
     301            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     302            0 :         break;
     303              :     }
     304              : }
     305              : 
     306              : /** Return displayable name */
     307              : static gchar*
     308            0 : impl_get_display_name(const QofInstance* inst)
     309              : {
     310              :     GncEntry* entry;
     311              :     gchar* display_name;
     312              :     gchar* s;
     313              : 
     314            0 :     g_return_val_if_fail(inst != NULL, FALSE);
     315            0 :     g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
     316              : 
     317            0 :     entry = GNC_ENTRY(inst);
     318            0 :     if (entry->order != NULL)
     319              :     {
     320            0 :         display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->order));
     321            0 :         s = g_strdup_printf("Entry in %s", display_name);
     322            0 :         g_free(display_name);
     323            0 :         return s;
     324              :     }
     325            0 :     if (entry->invoice != NULL)
     326              :     {
     327            0 :         display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->invoice));
     328            0 :         s = g_strdup_printf("Entry in %s", display_name);
     329            0 :         g_free(display_name);
     330            0 :         return s;
     331              :     }
     332            0 :     if (entry->bill != NULL)
     333              :     {
     334            0 :         display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->bill));
     335            0 :         s = g_strdup_printf("Entry in %s", display_name);
     336            0 :         g_free(display_name);
     337            0 :         return s;
     338              :     }
     339              : 
     340            0 :     return g_strdup_printf("Entry %p", inst);
     341              : }
     342              : 
     343              : /** Does this object refer to a specific object */
     344              : static gboolean
     345            0 : impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
     346              : {
     347              :     GncEntry* entry;
     348              : 
     349            0 :     g_return_val_if_fail(inst != NULL, FALSE);
     350            0 :     g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
     351              : 
     352            0 :     entry = GNC_ENTRY(inst);
     353              : 
     354            0 :     if (GNC_IS_ACCOUNT(ref))
     355              :     {
     356            0 :         Account* acc = GNC_ACCOUNT(ref);
     357            0 :         return (entry->i_account == acc || entry->b_account == acc);
     358              :     }
     359            0 :     else if (GNC_IS_TAXTABLE(ref))
     360              :     {
     361            0 :         GncTaxTable* tt = GNC_TAXTABLE(ref);
     362            0 :         return (entry->i_tax_table == tt || entry->b_tax_table == tt);
     363              :     }
     364              : 
     365            0 :     return FALSE;
     366              : }
     367              : 
     368              : /** Returns a list of my type of object which refers to an object.  For example, when called as
     369              :         qof_instance_get_typed_referring_object_list(taxtable, account);
     370              :     it will return the list of taxtables which refer to a specific account.  The result should be the
     371              :     same regardless of which taxtable object is used.  The list must be freed by the caller but the
     372              :     objects on the list must not.
     373              :  */
     374              : static GList*
     375            0 : impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
     376              : {
     377            0 :     if (!GNC_IS_ACCOUNT(ref) && !GNC_IS_TAXTABLE(ref))
     378              :     {
     379            0 :         return NULL;
     380              :     }
     381              : 
     382            0 :     return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
     383              : }
     384              : 
     385              : static void
     386           11 : gnc_entry_class_init (GncEntryClass *klass)
     387              : {
     388           11 :     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     389           11 :     QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
     390              : 
     391           11 :     gobject_class->dispose = gnc_entry_dispose;
     392           11 :     gobject_class->finalize = gnc_entry_finalize;
     393           11 :     gobject_class->set_property = gnc_entry_set_property;
     394           11 :     gobject_class->get_property = gnc_entry_get_property;
     395              : 
     396           11 :     qof_class->get_display_name = impl_get_display_name;
     397           11 :     qof_class->refers_to_object = impl_refers_to_object;
     398           11 :     qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
     399              : 
     400           11 :     g_object_class_install_property
     401              :     (gobject_class,
     402              :      PROP_DESCRIPTION,
     403              :      g_param_spec_string ("description",
     404              :                           "Entry Description",
     405              :                           "The description is an arbitrary string "
     406              :                           "assigned by the user.  It provides identification "
     407              :                           "for this entry.",
     408              :                           NULL,
     409              :                           G_PARAM_READWRITE));
     410           11 : }
     411              : 
     412              : /* Create/Destroy Functions */
     413          121 : GncEntry *gncEntryCreate (QofBook *book)
     414              : {
     415              :     GncEntry *entry;
     416          121 :     gnc_numeric zero = gnc_numeric_zero ();
     417              : 
     418          121 :     if (!book) return NULL;
     419              : 
     420          121 :     entry = g_object_new (GNC_TYPE_ENTRY, NULL);
     421          121 :     qof_instance_init_data (&entry->inst, _GNC_MOD_NAME, book);
     422              : 
     423          121 :     entry->desc = CACHE_INSERT ("");
     424          121 :     entry->action = CACHE_INSERT ("");
     425          121 :     entry->notes = CACHE_INSERT ("");
     426          121 :     entry->quantity = zero;
     427              : 
     428          121 :     entry->i_price = zero;
     429          121 :     entry->i_taxable = TRUE;
     430          121 :     entry->i_discount = zero;
     431          121 :     entry->i_disc_type = GNC_AMT_TYPE_PERCENT;
     432          121 :     entry->i_disc_how = GNC_DISC_PRETAX;
     433              : 
     434          121 :     entry->b_price = zero;
     435          121 :     entry->b_taxable = TRUE;
     436          121 :     entry->billto.type = GNC_OWNER_CUSTOMER;
     437          121 :     entry->b_payment = GNC_PAYMENT_CASH;
     438              : 
     439          121 :     entry->values_dirty = TRUE;
     440              : 
     441          121 :     qof_event_gen (&entry->inst, QOF_EVENT_CREATE, NULL);
     442              : 
     443          121 :     return entry;
     444              : }
     445              : 
     446           21 : void gncEntryDestroy (GncEntry *entry)
     447              : {
     448           21 :     if (!entry) return;
     449           21 :     qof_instance_set_destroying(entry, TRUE);
     450           21 :     gncEntryCommitEdit(entry);
     451              : }
     452              : 
     453           20 : static void gncEntryFree (GncEntry *entry)
     454              : {
     455           20 :     if (!entry) return;
     456              : 
     457           20 :     qof_event_gen (&entry->inst, QOF_EVENT_DESTROY, NULL);
     458              : 
     459           20 :     CACHE_REMOVE (entry->desc);
     460           20 :     CACHE_REMOVE (entry->action);
     461           20 :     CACHE_REMOVE (entry->notes);
     462           20 :     if (entry->i_tax_values)
     463            0 :         gncAccountValueDestroy (entry->i_tax_values);
     464           20 :     if (entry->b_tax_values)
     465            0 :         gncAccountValueDestroy (entry->b_tax_values);
     466              : 
     467           20 :     if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(entry))))
     468              :     {
     469            6 :         if (entry->i_tax_table)
     470            0 :             gncTaxTableDecRef (entry->i_tax_table);
     471            6 :         if (entry->b_tax_table)
     472            0 :             gncTaxTableDecRef (entry->b_tax_table);
     473              :     }
     474              : 
     475              :     /* qof_instance_release (&entry->inst); */
     476           20 :     g_object_unref (entry);
     477              : }
     478              : 
     479              : /* ================================================================ */
     480              : /* Set Functions */
     481              : 
     482          127 : void gncEntrySetDate (GncEntry *entry, time64 date)
     483              : {
     484          127 :     gboolean first_date = FALSE;
     485          127 :     if (!entry) return;
     486          127 :     if (entry->date == date) return;
     487          127 :     if (!entry->date)
     488          120 :         first_date = TRUE;
     489          127 :     gncEntryBeginEdit (entry);
     490          127 :     entry->date = date;
     491          127 :     mark_entry (entry);
     492          127 :     gncEntryCommitEdit (entry);
     493              : 
     494              :     /* Don't re-sort the first time we set the date on this entry */
     495          127 :     if (!first_date)
     496              :     {
     497            7 :         if (entry->invoice)
     498            0 :             gncInvoiceSortEntries(entry->invoice);
     499            7 :         if (entry->bill)
     500            0 :             gncInvoiceSortEntries(entry->bill);
     501              :     }
     502              : }
     503              : 
     504          100 : void gncEntrySetDateGDate (GncEntry *entry, const GDate* date)
     505              : {
     506          100 :     if (!entry || !date || !g_date_valid(date))
     507            0 :         return;
     508              : 
     509              :     /* Watch out: Here we are deviating from the initial convention that a
     510              :     GDate always converts to the start time of the day. Instead, the GDate is
     511              :     converted to "noon" on the respective date. This is not nice, but this
     512              :     convention was used for the time64 of GncEntry all the time, so we better
     513              :     stick to it.*/
     514          100 :     gncEntrySetDate(entry, time64CanonicalDayTime(gdate_to_time64(*date)));
     515              : }
     516              : 
     517           13 : void gncEntrySetDateEntered (GncEntry *entry, time64 date)
     518              : {
     519           13 :     if (!entry) return;
     520           13 :     if (entry->date_entered == date) return;
     521           13 :     gncEntryBeginEdit (entry);
     522           13 :     entry->date_entered = date;
     523           13 :     mark_entry (entry);
     524           13 :     gncEntryCommitEdit (entry);
     525              : }
     526              : 
     527          119 : void gncEntrySetDescription (GncEntry *entry, const char *desc)
     528              : {
     529          119 :     if (!entry || !desc) return;
     530          119 :     SET_STR (entry, entry->desc, desc);
     531          118 :     mark_entry (entry);
     532          118 :     gncEntryCommitEdit (entry);
     533              : }
     534              : 
     535          104 : void gncEntrySetAction (GncEntry *entry, const char *action)
     536              : {
     537          104 :     if (!entry || !action) return;
     538          104 :     SET_STR (entry, entry->action, action);
     539          103 :     mark_entry (entry);
     540          103 :     gncEntryCommitEdit (entry);
     541              : }
     542              : 
     543           62 : void gncEntrySetNotes (GncEntry *entry, const char *notes)
     544              : {
     545           62 :     if (!entry || !notes) return;
     546           62 :     SET_STR (entry, entry->notes, notes);
     547           61 :     mark_entry (entry);
     548           61 :     gncEntryCommitEdit (entry);
     549              : }
     550              : 
     551           16 : void gncEntrySetQuantity (GncEntry *entry, gnc_numeric quantity)
     552              : {
     553           16 :     if (!entry) return;
     554           16 :     if (gnc_numeric_eq (entry->quantity, quantity)) return;
     555           15 :     gncEntryBeginEdit (entry);
     556           15 :     entry->quantity = quantity;
     557           15 :     entry->values_dirty = TRUE;
     558           15 :     mark_entry (entry);
     559           15 :     gncEntryCommitEdit (entry);
     560              : }
     561              : 
     562          109 : void gncEntrySetDocQuantity (GncEntry *entry, gnc_numeric quantity, gboolean is_cn)
     563              : {
     564          109 :     if (!entry) return;
     565          109 :     if (gnc_numeric_eq (entry->quantity, (is_cn ? gnc_numeric_neg (quantity) : quantity))) return;
     566          108 :     gncEntryBeginEdit (entry);
     567          108 :     entry->quantity = (is_cn ? gnc_numeric_neg (quantity) : quantity);
     568          108 :     entry->values_dirty = TRUE;
     569          108 :     mark_entry (entry);
     570          108 :     gncEntryCommitEdit (entry);
     571              : }
     572              : 
     573              : /* Customer Invoices */
     574              : 
     575           95 : void gncEntrySetInvAccount (GncEntry *entry, Account *acc)
     576              : {
     577           95 :     if (!entry) return;
     578           95 :     if (entry->i_account == acc) return;
     579           95 :     gncEntryBeginEdit (entry);
     580           95 :     entry->i_account = acc;
     581           95 :     mark_entry (entry);
     582           95 :     gncEntryCommitEdit (entry);
     583              : }
     584              : 
     585           93 : void gncEntrySetInvPrice (GncEntry *entry, gnc_numeric price)
     586              : {
     587           93 :     if (!entry) return;
     588           93 :     if (gnc_numeric_eq (entry->i_price, price)) return;
     589           92 :     gncEntryBeginEdit (entry);
     590           92 :     entry->i_price = price;
     591           92 :     entry->values_dirty = TRUE;
     592           92 :     mark_entry (entry);
     593           92 :     gncEntryCommitEdit (entry);
     594              : }
     595              : 
     596           43 : void gncEntrySetInvTaxable (GncEntry *entry, gboolean taxable)
     597              : {
     598           43 :     if (!entry) return;
     599              : 
     600           43 :     ENTER ("%d", taxable);
     601           43 :     if (entry->i_taxable == taxable) {
     602           27 :          LEAVE ("Value already set");
     603           27 :          return;
     604              :     }
     605           16 :     gncEntryBeginEdit (entry);
     606           16 :     entry->i_taxable = taxable;
     607           16 :     entry->values_dirty = TRUE;
     608           16 :     mark_entry (entry);
     609           16 :     gncEntryCommitEdit (entry);
     610           16 :     LEAVE ("");
     611              : }
     612              : 
     613           50 : void gncEntrySetInvTaxIncluded (GncEntry *entry, gboolean taxincluded)
     614              : {
     615           50 :     if (!entry) return;
     616              : 
     617           50 :     ENTER ("%d", taxincluded);
     618           50 :     if (entry->i_taxincluded == taxincluded) {
     619           26 :          LEAVE ("Value already set");
     620           26 :          return;
     621              :     }
     622           24 :     gncEntryBeginEdit (entry);
     623           24 :     entry->i_taxincluded = taxincluded;
     624           24 :     entry->values_dirty = TRUE;
     625           24 :     mark_entry (entry);
     626           24 :     gncEntryCommitEdit (entry);
     627           24 :     LEAVE ("");
     628              : }
     629              : 
     630          148 : void gncEntrySetInvTaxTable (GncEntry *entry, GncTaxTable *table)
     631              : {
     632          148 :     if (!entry) return;
     633              : 
     634          148 :     ENTER ("%s", gncTaxTableGetName (table));
     635          148 :     if (entry->i_tax_table == table) {
     636           55 :          LEAVE ("Value already set");
     637           55 :          return;
     638              :     }
     639           93 :     gncEntryBeginEdit (entry);
     640           93 :     if (entry->i_tax_table)
     641           46 :         gncTaxTableDecRef (entry->i_tax_table);
     642           93 :     if (table)
     643           87 :         gncTaxTableIncRef (table);
     644           93 :     entry->i_tax_table = table;
     645           93 :     entry->values_dirty = TRUE;
     646           93 :     mark_entry (entry);
     647           93 :     gncEntryCommitEdit (entry);
     648           93 :     LEAVE ("");
     649              : }
     650              : 
     651           42 : void gncEntrySetInvDiscount (GncEntry *entry, gnc_numeric discount)
     652              : {
     653           42 :     if (!entry) return;
     654           42 :     if (gnc_numeric_eq (entry->i_discount, discount)) return;
     655           41 :     gncEntryBeginEdit (entry);
     656           41 :     entry->i_discount = discount;
     657           41 :     entry->values_dirty = TRUE;
     658           41 :     mark_entry (entry);
     659           41 :     gncEntryCommitEdit (entry);
     660              : }
     661              : 
     662           41 : void gncEntrySetInvDiscountType (GncEntry *entry, GncAmountType type)
     663              : {
     664           41 :     if (!entry) return;
     665           41 :     if (entry->i_disc_type == type) return;
     666              : 
     667           20 :     gncEntryBeginEdit (entry);
     668           20 :     entry->i_disc_type = type;
     669           20 :     entry->values_dirty = TRUE;
     670           20 :     mark_entry (entry);
     671           20 :     gncEntryCommitEdit (entry);
     672              : }
     673              : 
     674           41 : void gncEntrySetInvDiscountHow (GncEntry *entry, GncDiscountHow how)
     675              : {
     676           41 :     if (!entry) return;
     677           41 :     if (entry->i_disc_how == how) return;
     678              : 
     679           25 :     gncEntryBeginEdit (entry);
     680           25 :     entry->i_disc_how = how;
     681           25 :     entry->values_dirty = TRUE;
     682           25 :     mark_entry (entry);
     683           25 :     gncEntryCommitEdit (entry);
     684              : }
     685              : 
     686            1 : void qofEntrySetInvDiscType (GncEntry *entry, const char *type_string)
     687              : {
     688              :     GncAmountType type;
     689              : 
     690            2 :     if (!entry) return;
     691            1 :     gncAmountStringToType(type_string, &type);
     692            1 :     if (entry->i_disc_type == type) return;
     693            0 :     gncEntryBeginEdit (entry);
     694            0 :     entry->i_disc_type = type;
     695            0 :     entry->values_dirty = TRUE;
     696            0 :     mark_entry (entry);
     697            0 :     gncEntryCommitEdit (entry);
     698              : 
     699              : }
     700              : 
     701            1 : void qofEntrySetInvDiscHow  (GncEntry *entry, const char *type)
     702              : {
     703            1 :     GncDiscountHow how = GNC_DISC_PRETAX;
     704              : 
     705            2 :     if (!entry) return;
     706            1 :     gncEntryBeginEdit (entry);
     707            1 :     gncEntryDiscountStringToHow(type, &how);
     708            1 :     if (entry->i_disc_how == how) return;
     709            0 :     entry->i_disc_how = how;
     710            0 :     entry->values_dirty = TRUE;
     711            0 :     mark_entry (entry);
     712            0 :     gncEntryCommitEdit (entry);
     713              : }
     714              : 
     715              : /* Vendor Bills */
     716              : 
     717           23 : void gncEntrySetBillAccount (GncEntry *entry, Account *acc)
     718              : {
     719           23 :     if (!entry) return;
     720           23 :     if (entry->b_account == acc) return;
     721           23 :     gncEntryBeginEdit (entry);
     722           23 :     entry->b_account = acc;
     723           23 :     mark_entry (entry);
     724           23 :     gncEntryCommitEdit (entry);
     725              : }
     726              : 
     727           23 : void gncEntrySetBillPrice (GncEntry *entry, gnc_numeric price)
     728              : {
     729           23 :     if (!entry) return;
     730           23 :     if (gnc_numeric_eq (entry->b_price, price)) return;
     731           22 :     gncEntryBeginEdit (entry);
     732           22 :     entry->b_price = price;
     733           22 :     entry->values_dirty = TRUE;
     734           22 :     mark_entry (entry);
     735           22 :     gncEntryCommitEdit (entry);
     736              : }
     737              : 
     738            4 : void gncEntrySetBillTaxable (GncEntry *entry, gboolean taxable)
     739              : {
     740            4 :     if (!entry) return;
     741              : 
     742            4 :     ENTER ("%d", taxable);
     743            4 :     if (entry->b_taxable == taxable) {
     744            2 :          LEAVE ("Value already set");
     745            2 :          return;
     746              :     }
     747            2 :     gncEntryBeginEdit (entry);
     748            2 :     entry->b_taxable = taxable;
     749            2 :     entry->values_dirty = TRUE;
     750            2 :     mark_entry (entry);
     751            2 :     gncEntryCommitEdit (entry);
     752            2 :     LEAVE ("");
     753              : }
     754              : 
     755            4 : void gncEntrySetBillTaxIncluded (GncEntry *entry, gboolean taxincluded)
     756              : {
     757            4 :     if (!entry) return;
     758              : 
     759            4 :     ENTER ("%d", taxincluded);
     760            4 :     if (entry->b_taxincluded == taxincluded) {
     761            4 :          LEAVE ("Value already set");
     762            4 :          return;
     763              :     }
     764            0 :     gncEntryBeginEdit (entry);
     765            0 :     entry->b_taxincluded = taxincluded;
     766            0 :     entry->values_dirty = TRUE;
     767            0 :     mark_entry (entry);
     768            0 :     gncEntryCommitEdit (entry);
     769            0 :     LEAVE ("");
     770              : }
     771              : 
     772           20 : void gncEntrySetBillTaxTable (GncEntry *entry, GncTaxTable *table)
     773              : {
     774           20 :     if (!entry) return;
     775              : 
     776           20 :     ENTER ("%s", gncTaxTableGetName (table));
     777           20 :     if (entry->b_tax_table == table) {
     778           20 :          LEAVE ("Value already set");
     779           20 :          return;
     780              :     }
     781            0 :     gncEntryBeginEdit (entry);
     782            0 :     if (entry->b_tax_table)
     783            0 :         gncTaxTableDecRef (entry->b_tax_table);
     784            0 :     if (table)
     785            0 :         gncTaxTableIncRef (table);
     786            0 :     entry->b_tax_table = table;
     787            0 :     entry->values_dirty = TRUE;
     788            0 :     mark_entry (entry);
     789            0 :     gncEntryCommitEdit (entry);
     790            0 :     LEAVE ("");
     791              : }
     792              : 
     793            4 : void gncEntrySetBillable (GncEntry *entry, gboolean billable)
     794              : {
     795            4 :     if (!entry) return;
     796            4 :     if (entry->billable == billable) return;
     797              : 
     798            0 :     gncEntryBeginEdit (entry);
     799            0 :     entry->billable = billable;
     800            0 :     mark_entry (entry);
     801            0 :     gncEntryCommitEdit (entry);
     802              : }
     803              : 
     804            0 : void gncEntrySetBillTo (GncEntry *entry, GncOwner *billto)
     805              : {
     806            0 :     if (!entry || !billto) return;
     807            0 :     if (gncOwnerEqual (&entry->billto, billto)) return;
     808              : 
     809            0 :     gncEntryBeginEdit (entry);
     810            0 :     gncOwnerCopy (billto, &entry->billto);
     811            0 :     mark_entry (entry);
     812            0 :     gncEntryCommitEdit (entry);
     813              : }
     814              : 
     815            4 : void gncEntrySetBillPayment (GncEntry *entry, GncEntryPaymentType type)
     816              : {
     817            4 :     if (!entry) return;
     818            4 :     if (entry->b_payment == type) return;
     819            0 :     gncEntryBeginEdit (entry);
     820            0 :     entry->b_payment = type;
     821            0 :     mark_entry (entry);
     822            0 :     gncEntryCommitEdit (entry);
     823              : }
     824              : 
     825              : /* Called from gncOrder when we're added to the Order */
     826           40 : void gncEntrySetOrder (GncEntry *entry, GncOrder *order)
     827              : {
     828           40 :     if (!entry) return;
     829           40 :     if (entry->order == order) return;
     830           40 :     gncEntryBeginEdit (entry);
     831           40 :     entry->order = order;
     832           40 :     mark_entry (entry);
     833           40 :     gncEntryCommitEdit (entry);
     834              : 
     835              : }
     836              : 
     837              : /* called from gncInvoice when we're added to the Invoice */
     838          116 : void gncEntrySetInvoice (GncEntry *entry, GncInvoice *invoice)
     839              : {
     840          116 :     if (!entry) return;
     841          116 :     if (entry->invoice == invoice) return;
     842          115 :     gncEntryBeginEdit (entry);
     843          115 :     entry->invoice = invoice;
     844          115 :     mark_entry (entry);
     845          115 :     gncEntryCommitEdit (entry);
     846              : }
     847              : 
     848              : /* called from gncInvoice when we're added to the Invoice/Bill */
     849            5 : void gncEntrySetBill (GncEntry *entry, GncInvoice *bill)
     850              : {
     851            5 :     if (!entry) return;
     852            5 :     if (entry->bill == bill) return;
     853            5 :     gncEntryBeginEdit (entry);
     854            5 :     entry->bill = bill;
     855            5 :     mark_entry (entry);
     856            5 :     gncEntryCommitEdit (entry);
     857              : }
     858              : 
     859            0 : void gncEntryCopy (const GncEntry *src, GncEntry *dest, gboolean add_entry)
     860              : {
     861            0 :     if (!src || !dest) return;
     862              : 
     863            0 :     gncEntryBeginEdit (dest);
     864            0 :     dest->date                       = src->date;
     865            0 :     dest->date_entered               = src->date_entered; /* ??? */
     866            0 :     gncEntrySetDescription (dest, src->desc);
     867            0 :     gncEntrySetAction (dest, src->action);
     868            0 :     gncEntrySetNotes (dest, src->notes);
     869            0 :     dest->quantity           = src->quantity;
     870              : 
     871            0 :     dest->i_account          = src->i_account;
     872            0 :     dest->i_price                    = src->i_price;
     873            0 :     dest->i_taxable          = src->i_taxable;
     874            0 :     dest->i_taxincluded              = src->i_taxincluded;
     875            0 :     dest->i_discount         = src->i_discount;
     876            0 :     dest->i_disc_type                = src->i_disc_type;
     877            0 :     dest->i_disc_how         = src->i_disc_how;
     878              : 
     879              :     /* vendor bill data */
     880            0 :     dest->b_account          = src->b_account;
     881            0 :     dest->b_price                    = src->b_price;
     882            0 :     dest->b_taxable          = src->b_taxable;
     883            0 :     dest->b_taxincluded              = src->b_taxincluded;
     884            0 :     dest->billable           = src->billable;
     885            0 :     dest->billto                     = src->billto;
     886              : 
     887            0 :     if (src->i_tax_table)
     888            0 :         gncEntrySetInvTaxTable (dest, src->i_tax_table);
     889              : 
     890            0 :     if (src->b_tax_table)
     891            0 :         gncEntrySetBillTaxTable (dest, src->b_tax_table);
     892              : 
     893            0 :     if (add_entry)
     894              :     {
     895            0 :         if (src->order)
     896            0 :             gncOrderAddEntry (src->order, dest);
     897              : 
     898            0 :         if (src->invoice)
     899            0 :             gncInvoiceAddEntry (src->invoice, dest);
     900              : 
     901            0 :         if (src->bill)
     902            0 :             gncBillAddEntry (src->bill, dest);
     903              :     }
     904              : 
     905            0 :     dest->values_dirty = TRUE;
     906            0 :     mark_entry (dest);
     907            0 :     gncEntryCommitEdit (dest);
     908              : }
     909              : 
     910              : /* ================================================================ */
     911              : /* Get Functions */
     912              : 
     913           67 : time64 gncEntryGetDate (const GncEntry *entry)
     914              : {
     915           67 :     return entry ? entry->date : 0;
     916              : }
     917              : 
     918            0 : GDate gncEntryGetDateGDate(const GncEntry *entry)
     919              : {
     920            0 :     return time64_to_gdate(gncEntryGetDate(entry));
     921              : }
     922              : 
     923            4 : time64 gncEntryGetDateEntered (const GncEntry *entry)
     924              : {
     925            4 :     return entry ? entry->date_entered : 0;
     926              : }
     927              : 
     928           66 : const char * gncEntryGetDescription (const GncEntry *entry)
     929              : {
     930           66 :     if (!entry) return NULL;
     931           66 :     return entry->desc;
     932              : }
     933              : 
     934           67 : const char * gncEntryGetAction (const GncEntry *entry)
     935              : {
     936           67 :     if (!entry) return NULL;
     937           67 :     return entry->action;
     938              : }
     939              : 
     940           19 : const char * gncEntryGetNotes (const GncEntry *entry)
     941              : {
     942           19 :     if (!entry) return NULL;
     943           19 :     return entry->notes;
     944              : }
     945              : 
     946           71 : gnc_numeric gncEntryGetQuantity (const GncEntry *entry)
     947              : {
     948           71 :     if (!entry) return gnc_numeric_zero();
     949           71 :     return entry->quantity;
     950              : }
     951              : 
     952           50 : gnc_numeric gncEntryGetDocQuantity (const GncEntry *entry, gboolean is_cn)
     953              : {
     954           50 :     gnc_numeric value = gncEntryGetQuantity (entry);
     955           50 :     return (is_cn ? gnc_numeric_neg (value) : value);
     956              : }
     957              : 
     958              : /* Customer Invoice */
     959              : 
     960           92 : Account * gncEntryGetInvAccount (const GncEntry *entry)
     961              : {
     962           92 :     if (!entry) return NULL;
     963           92 :     return entry->i_account;
     964              : }
     965              : 
     966           39 : gnc_numeric gncEntryGetInvPrice (const GncEntry *entry)
     967              : {
     968           39 :     if (!entry) return gnc_numeric_zero();
     969           39 :     return entry->i_price;
     970              : }
     971              : 
     972           39 : gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry)
     973              : {
     974           39 :     if (!entry) return gnc_numeric_zero();
     975           39 :     return entry->i_discount;
     976              : }
     977              : 
     978           36 : GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry)
     979              : {
     980           36 :     if (!entry) return 0;
     981           36 :     return entry->i_disc_type;
     982              : }
     983              : 
     984            0 : GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry)
     985              : {
     986            0 :     if (!entry) return 0;
     987            0 :     return entry->i_disc_how;
     988              : }
     989              : 
     990            1 : char* qofEntryGetInvDiscType (const GncEntry *entry)
     991              : {
     992              :     char *type_string;
     993              : 
     994            1 :     if (!entry) return 0;
     995            1 :     type_string = g_strdup(gncAmountTypeToString(entry->i_disc_type));
     996            1 :     return type_string;
     997              : }
     998              : 
     999            1 : char* qofEntryGetInvDiscHow (const GncEntry *entry)
    1000              : {
    1001              :     char *type_string;
    1002              : 
    1003            1 :     if (!entry) return 0;
    1004            1 :     type_string = g_strdup(gncEntryDiscountHowToString(entry->i_disc_how));
    1005            1 :     return type_string;
    1006              : }
    1007              : 
    1008           37 : gboolean gncEntryGetInvTaxable (const GncEntry *entry)
    1009              : {
    1010           37 :     if (!entry) return FALSE;
    1011           37 :     return entry->i_taxable;
    1012              : }
    1013              : 
    1014            1 : gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry)
    1015              : {
    1016            1 :     if (!entry) return FALSE;
    1017            1 :     return entry->i_taxincluded;
    1018              : }
    1019              : 
    1020          129 : GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry)
    1021              : {
    1022          129 :     if (!entry) return NULL;
    1023          129 :     return entry->i_tax_table;
    1024              : }
    1025              : 
    1026              : /* vendor bills */
    1027              : 
    1028           23 : Account * gncEntryGetBillAccount (const GncEntry *entry)
    1029              : {
    1030           23 :     if (!entry) return NULL;
    1031           23 :     return entry->b_account;
    1032              : }
    1033              : 
    1034           15 : gnc_numeric gncEntryGetBillPrice (const GncEntry *entry)
    1035              : {
    1036           15 :     if (!entry) return gnc_numeric_zero();
    1037           15 :     return entry->b_price;
    1038              : }
    1039              : 
    1040           15 : gboolean gncEntryGetBillTaxable (const GncEntry *entry)
    1041              : {
    1042           15 :     if (!entry) return FALSE;
    1043           15 :     return entry->b_taxable;
    1044              : }
    1045              : 
    1046            3 : gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry)
    1047              : {
    1048            3 :     if (!entry) return FALSE;
    1049            3 :     return entry->b_taxincluded;
    1050              : }
    1051              : 
    1052           39 : GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry)
    1053              : {
    1054           39 :     if (!entry) return NULL;
    1055           39 :     return entry->b_tax_table;
    1056              : }
    1057              : 
    1058           23 : gboolean gncEntryGetBillable (const GncEntry *entry)
    1059              : {
    1060           23 :     if (!entry) return FALSE;
    1061           23 :     return entry->billable;
    1062              : }
    1063              : 
    1064            3 : GncOwner * gncEntryGetBillTo (GncEntry *entry)
    1065              : {
    1066            3 :     if (!entry) return NULL;
    1067            3 :     return &entry->billto;
    1068              : }
    1069              : 
    1070            3 : GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry)
    1071              : {
    1072            3 :     if (!entry) return 0;
    1073            3 :     return entry->b_payment;
    1074              : }
    1075              : 
    1076          122 : GncInvoice * gncEntryGetInvoice (const GncEntry *entry)
    1077              : {
    1078          122 :     if (!entry) return NULL;
    1079          122 :     return entry->invoice;
    1080              : }
    1081              : 
    1082           18 : GncInvoice * gncEntryGetBill (const GncEntry *entry)
    1083              : {
    1084           18 :     if (!entry) return NULL;
    1085           18 :     return entry->bill;
    1086              : }
    1087              : 
    1088           97 : GncOrder * gncEntryGetOrder (const GncEntry *entry)
    1089              : {
    1090           97 :     if (!entry) return NULL;
    1091           97 :     return entry->order;
    1092              : }
    1093              : 
    1094              : /* ================================================================ */
    1095              : /*
    1096              :  * This is the logic of computing the total for an Entry, so you know
    1097              :  * what values to put into various Splits or to display in the ledger.
    1098              :  * In other words, we combine the quantity, unit-price, discount and
    1099              :  * taxes together, depending on various flags.
    1100              :  *
    1101              :  * There are four potential ways to combine these numbers:
    1102              :  * Discount:     Pre-Tax   Post-Tax
    1103              :  *   Tax   :     Included  Not-Included
    1104              :  *
    1105              :  * The process is relatively simple:
    1106              :  *
    1107              :  *  1) compute the aggregate price (price*qty)
    1108              :  *  2) if taxincluded, then back-compute the aggregate pre-tax price
    1109              :  *  3) apply discount and taxes in the appropriate order
    1110              :  *  4) return the requested results.
    1111              :  *
    1112              :  * Step 2 can be done with aggregate taxes; no need to compute them all
    1113              :  * unless the caller asked for the tax_value.
    1114              :  *
    1115              :  * Note that the returned "value" is such that
    1116              :  *   value + tax == "total to pay"
    1117              :  * which means in the case of tax-included that the returned
    1118              :  * "value" may be less than the aggregate price, even without a
    1119              :  * discount.  If you want to display the tax-included value, you need
    1120              :  * to add the value and taxes together.  In other words, the value is
    1121              :  * the amount the merchant gets; the taxes are the amount the gov't
    1122              :  * gets, and the customer pays the sum or value + taxes.
    1123              :  *
    1124              :  * The discount return value is just for entertainment -- you may want
    1125              :  * to let a consumer know how much they saved.
    1126              :  *
    1127              :  * Note this function will not do any rounding unless forced to prevent overflow.
    1128              :  * It's the caller's responsibility to round to the proper commodity
    1129              :  * denominator if needed.
    1130              :  */
    1131          316 : static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
    1132              :                                     const GncTaxTable *tax_table, gboolean tax_included,
    1133              :                                     gnc_numeric discount, GncAmountType discount_type,
    1134              :                                     GncDiscountHow discount_how,
    1135              :                                     gnc_numeric *value, gnc_numeric *discount_value,
    1136              :                                     GList **tax_value, gnc_numeric *net_price)
    1137              : {
    1138              :     gnc_numeric aggregate;
    1139              :     gnc_numeric pretax;
    1140              :     gnc_numeric result;
    1141              :     gnc_numeric tax;
    1142          316 :     gnc_numeric percent = gnc_numeric_create (100, 1);
    1143          316 :     gnc_numeric tpercent = gnc_numeric_zero ();
    1144          316 :     gnc_numeric tvalue = gnc_numeric_zero ();
    1145          316 :     gnc_numeric i_net_price = price;
    1146              : 
    1147          316 :     GList     * entries = gncTaxTableGetEntries (tax_table);
    1148              :     GList     * node;
    1149              : 
    1150          316 :     ENTER ("");
    1151              :     /* Step 1: compute the aggregate price */
    1152              : 
    1153          316 :     aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1154              : 
    1155          316 :     PINFO ("Aggregate value %" PRId64 "/%" PRId64, aggregate.num, aggregate.denom);
    1156              :     /* Step 2: compute the pre-tax aggregate */
    1157              : 
    1158              :     /* First, compute the aggregate tpercent and tvalue numbers */
    1159          376 :     for (node = entries; node; node = node->next)
    1160              :     {
    1161           60 :         GncTaxTableEntry *entry = node->data;
    1162           60 :         gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
    1163              : 
    1164           60 :         switch (gncTaxTableEntryGetType (entry))
    1165              :         {
    1166            0 :         case GNC_AMT_TYPE_VALUE:
    1167            0 :             tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO,
    1168              :                                       GNC_HOW_DENOM_LCD);
    1169            0 :             break;
    1170           60 :         case GNC_AMT_TYPE_PERCENT:
    1171           60 :             tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO,
    1172              :                                         GNC_HOW_DENOM_LCD);
    1173           60 :             break;
    1174            0 :         default:
    1175            0 :             PWARN ("Unknown tax type: %d", gncTaxTableEntryGetType (entry));
    1176            0 :             break;
    1177              :         }
    1178              :     }
    1179              :     /* now we need to convert from 5% -> .05 */
    1180          316 :     tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO,
    1181              :                                 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
    1182          316 :     PINFO("Tax rate %" PRId64 "/%" PRId64, tpercent.num, tpercent.denom);
    1183              :     /* Next, actually compute the pre-tax aggregate value based on the
    1184              :      * taxincluded flag.
    1185              :      */
    1186          316 :     if (tax_table && tax_included)
    1187              :     {
    1188              :         /* Back-compute the pre-tax aggregate value.
    1189              :          * We know that aggregate = pretax + pretax*tpercent + tvalue, so
    1190              :          * pretax = (aggregate-tvalue)/(1+tpercent)
    1191              :          */
    1192           24 :         pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO,
    1193              :                                   GNC_HOW_DENOM_LCD);
    1194           24 :         pretax = gnc_numeric_div (pretax,
    1195              :                                   gnc_numeric_add (tpercent,
    1196              :                                           gnc_numeric_create (1, 1),
    1197              :                                           GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
    1198              :                                   GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1199           24 :         PINFO ("pretax %" PRId64 "/%" PRId64, pretax.num, pretax.denom);
    1200           24 :         if (!gnc_numeric_zero_p(qty))
    1201              :         {
    1202           24 :           i_net_price = gnc_numeric_div (pretax, qty, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1203              :         }
    1204           24 :         PINFO("i_net_price %" PRId64 "/%" PRId64, i_net_price.num, i_net_price.denom);
    1205              :     }
    1206              :     else
    1207              :     {
    1208          292 :         PINFO ("Tax not included or no tax table, pretax is aggregate");
    1209          292 :         pretax = aggregate;
    1210              :     }
    1211              : 
    1212              :     /* Step 3:  apply discount and taxes in the appropriate order */
    1213              : 
    1214              :     /*
    1215              :      * There are two ways to apply discounts and taxes.  In one way, you
    1216              :      * always compute the discount off the pretax number, and compute
    1217              :      * the taxes off of either the pretax value or "pretax-discount"
    1218              :      * value.  In the other way, you always compute the tax on "pretax",
    1219              :      * and compute the discount on either "pretax" or "pretax+taxes".
    1220              :      *
    1221              :      * I don't know which is the "correct" way.
    1222              :      */
    1223              : 
    1224              :     /*
    1225              :      * Type:    discount    tax
    1226              :      * PRETAX   pretax      pretax-discount
    1227              :      * SAMETIME pretax      pretax
    1228              :      * POSTTAX  pretax+tax  pretax
    1229              :      */
    1230              : 
    1231          316 :     switch (discount_how)
    1232              :     {
    1233          296 :     case GNC_DISC_PRETAX:
    1234              :     case GNC_DISC_SAMETIME:
    1235              :         /* compute the discount from pretax */
    1236              : 
    1237          296 :         if (discount_type == GNC_AMT_TYPE_PERCENT)
    1238              :         {
    1239          108 :             discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
    1240              :                                         GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
    1241          108 :             discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO,
    1242              :                                         GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1243              :         }
    1244              : 
    1245          296 :         result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
    1246              : 
    1247              :         /* Figure out when to apply the tax, pretax or pretax-discount */
    1248          296 :         if (discount_how == GNC_DISC_PRETAX)
    1249          266 :             pretax = result;
    1250          296 :         break;
    1251              : 
    1252           20 :     case GNC_DISC_POSTTAX:
    1253              :         /* compute discount on pretax+taxes */
    1254              : 
    1255           20 :         if (discount_type == GNC_AMT_TYPE_PERCENT)
    1256              :         {
    1257              :             gnc_numeric after_tax;
    1258              : 
    1259           10 :             tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1260           10 :             after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
    1261           10 :             after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO,
    1262              :                                          GNC_HOW_DENOM_LCD);
    1263           10 :             discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
    1264              :                                         GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
    1265           10 :             discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO,
    1266              :                                         GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1267              :         }
    1268              : 
    1269           20 :         result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
    1270           20 :         break;
    1271              : 
    1272            0 :     default:
    1273            0 :         PWARN ("unknown DiscountHow value: %d", discount_how);
    1274            0 :         break;
    1275              :     }
    1276              : 
    1277              :     /* Step 4:  return the requested results. */
    1278              : 
    1279              :     /* result == amount merchant gets
    1280              :      * discount == amount of discount
    1281              :      * need to compute taxes (based on 'pretax') if the caller wants it.
    1282              :      */
    1283              : 
    1284          316 :     if (discount_value != NULL)
    1285          158 :         *discount_value = discount;
    1286              : 
    1287          316 :     if (value != NULL)
    1288          316 :         *value = result;
    1289              : 
    1290              :     /* Now... Compute the list of tax values (if the caller wants it) */
    1291              : 
    1292          316 :     if (tax_value != NULL)
    1293              :     {
    1294          316 :         GList * taxes = NULL;
    1295              : 
    1296          316 :         PINFO("Computing tax value list");
    1297          376 :         for (node = entries; node; node = node->next)
    1298              :         {
    1299           60 :             GncTaxTableEntry *entry = node->data;
    1300           60 :             Account *acc = gncTaxTableEntryGetAccount (entry);
    1301           60 :             gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
    1302              : 
    1303           60 :             g_return_if_fail (acc);
    1304              : 
    1305           60 :             switch (gncTaxTableEntryGetType (entry))
    1306              :             {
    1307            0 :             case GNC_AMT_TYPE_VALUE:
    1308            0 :                 taxes = gncAccountValueAdd (taxes, acc, amount);
    1309            0 :                 break;
    1310           60 :             case GNC_AMT_TYPE_PERCENT:
    1311           60 :                 amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO,
    1312              :                                           GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
    1313           60 :                 tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
    1314           60 :                 taxes = gncAccountValueAdd (taxes, acc, tax);
    1315           60 :                 break;
    1316            0 :             default:
    1317            0 :                 break;
    1318              :             }
    1319              :         }
    1320          316 :         *tax_value = taxes;
    1321              :     }
    1322              : 
    1323          316 :     if (net_price != NULL)
    1324            0 :       *net_price = i_net_price;
    1325              : 
    1326          316 :     LEAVE ("");
    1327          316 :     return;
    1328              : }
    1329              : 
    1330          316 : void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price,
    1331              :                            const GncTaxTable *tax_table, gboolean tax_included,
    1332              :                            gnc_numeric discount, GncAmountType discount_type,
    1333              :                            GncDiscountHow discount_how, G_GNUC_UNUSED int SCU,
    1334              :                            gnc_numeric *value, gnc_numeric *discount_value,
    1335              :                            GList **tax_value)
    1336              : {
    1337          316 :   gncEntryComputeValueInt (qty, price, tax_table, tax_included, discount, discount_type,
    1338              :                            discount_how, value, discount_value, tax_value, NULL);
    1339          316 : }
    1340              : 
    1341              : static int
    1342          158 : get_entry_commodity_denom (const GncEntry *entry)
    1343              : {
    1344              :     gnc_commodity *c;
    1345          158 :     if (!entry)
    1346            0 :         return 0;
    1347          158 :     if (entry->invoice)
    1348              :     {
    1349          152 :         c = gncInvoiceGetCurrency (entry->invoice);
    1350          152 :         if (c)
    1351          152 :             return (gnc_commodity_get_fraction (c));
    1352              :     }
    1353            6 :     if (entry->bill)
    1354              :     {
    1355            1 :         c = gncInvoiceGetCurrency (entry->bill);
    1356            1 :         if (c)
    1357            1 :             return (gnc_commodity_get_fraction (c));
    1358              :     }
    1359            5 :     return 100000;
    1360              : }
    1361              : 
    1362              : static void
    1363         1221 : gncEntryRecomputeValues (GncEntry *entry)
    1364              : {
    1365              :     int denom;
    1366              :     GList *tv_iter;
    1367              : 
    1368         1221 :     ENTER ("");
    1369              :     /* See if either tax table changed since we last computed values */
    1370         1221 :     if (entry->i_tax_table)
    1371              :     {
    1372          528 :         time64 modtime = gncTaxTableLastModifiedSecs (entry->i_tax_table);
    1373          528 :         if (entry->i_taxtable_modtime != modtime)
    1374              :         {
    1375           41 :             PINFO ("Invoice tax table changed since last recompute.");
    1376           41 :             entry->values_dirty = TRUE;
    1377           41 :             entry->i_taxtable_modtime = modtime;
    1378              :         }
    1379              :     }
    1380         1221 :     if (entry->b_tax_table)
    1381              :     {
    1382            0 :         time64 modtime = gncTaxTableLastModifiedSecs (entry->b_tax_table);
    1383            0 :         if (entry->b_taxtable_modtime != modtime)
    1384              :         {
    1385            0 :             PINFO ("Bill tax table changed since last recompute.");
    1386            0 :             entry->values_dirty = TRUE;
    1387            0 :             entry->b_taxtable_modtime = modtime;
    1388              :         }
    1389              :     }
    1390              : 
    1391         1221 :     if (!entry->values_dirty) {
    1392         1063 :         LEAVE ("No changes");
    1393         1063 :         return;
    1394              :     }
    1395              : 
    1396              :     /* Clear the last-computed tax values */
    1397          158 :     if (entry->i_tax_values)
    1398              :     {
    1399           34 :         gncAccountValueDestroy (entry->i_tax_values);
    1400           34 :         entry->i_tax_values = NULL;
    1401              :     }
    1402          158 :     if (entry->b_tax_values)
    1403              :     {
    1404            0 :         gncAccountValueDestroy (entry->b_tax_values);
    1405            0 :         entry->b_tax_values = NULL;
    1406              :     }
    1407              : 
    1408              :     /* Determine the commodity denominator */
    1409          158 :     denom = get_entry_commodity_denom (entry);
    1410              : 
    1411              :     /* Compute the invoice values */
    1412          158 :     DEBUG("Compute Invoice Values.");
    1413          158 :     gncEntryComputeValue (entry->quantity, entry->i_price,
    1414          158 :                           (entry->i_taxable ? entry->i_tax_table : NULL),
    1415              :                           entry->i_taxincluded,
    1416              :                           entry->i_discount, entry->i_disc_type,
    1417              :                           entry->i_disc_how,
    1418              :                           denom,
    1419              :                           &(entry->i_value), &(entry->i_disc_value),
    1420              :                           &(entry->i_tax_values));
    1421              : 
    1422              :     /* Compute the bill values */
    1423          158 :     DEBUG("Compute BILL Values.");
    1424            0 :     gncEntryComputeValue (entry->quantity, entry->b_price,
    1425          158 :                           (entry->b_taxable ? entry->b_tax_table : NULL),
    1426              :                           entry->b_taxincluded,
    1427              :                           gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
    1428              :                           denom,
    1429              :                           &(entry->b_value), NULL, &(entry->b_tax_values));
    1430              : 
    1431          158 :     entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom,
    1432              :                              GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1433          158 :     entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom,
    1434              :                                   GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1435          158 :     entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values);
    1436          158 :     entry->i_tax_value_rounded = gnc_numeric_zero();
    1437          218 :     for (tv_iter = entry->i_tax_values; tv_iter; tv_iter=tv_iter->next)
    1438              :     {
    1439           60 :         GncAccountValue *acc_val = tv_iter->data;
    1440           60 :         entry->i_tax_value_rounded = gnc_numeric_add (entry->i_tax_value_rounded, acc_val->value,
    1441              :                                      denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1442              :     }
    1443              : 
    1444          158 :     entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom,
    1445              :                              GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1446          158 :     entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values);
    1447          158 :     entry->b_tax_value_rounded = gnc_numeric_zero();
    1448          158 :     for (tv_iter = entry->b_tax_values; tv_iter; tv_iter=tv_iter->next)
    1449              :     {
    1450            0 :         GncAccountValue *acc_val = tv_iter->data;
    1451            0 :         entry->b_tax_value_rounded = gnc_numeric_add (entry->b_tax_value_rounded, acc_val->value,
    1452              :                                                       denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1453              :     }
    1454          158 :     entry->values_dirty = FALSE;
    1455          158 :     LEAVE ("");
    1456              : }
    1457              : 
    1458              : /* The "Int" functions below are for internal use only.
    1459              :  * Outside this file, use the "Doc" or "Bal" variants found below instead. */
    1460          555 : static gnc_numeric gncEntryGetIntValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
    1461              : {
    1462          555 :     if (!entry) return gnc_numeric_zero();
    1463          555 :     gncEntryRecomputeValues (entry);
    1464          555 :     if (round)
    1465          545 :         return (is_cust_doc ? entry->i_value_rounded : entry->b_value_rounded);
    1466              :     else
    1467           10 :         return (is_cust_doc ? entry->i_value : entry->b_value);
    1468              : }
    1469              : 
    1470          176 : static gnc_numeric gncEntryGetIntTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
    1471              : {
    1472          176 :     if (!entry) return gnc_numeric_zero();
    1473          176 :     gncEntryRecomputeValues (entry);
    1474          176 :     if (round)
    1475          166 :         return (is_cust_doc ? entry->i_tax_value_rounded : entry->b_tax_value_rounded);
    1476              :     else
    1477           10 :         return (is_cust_doc ? entry->i_tax_value : entry->b_tax_value);
    1478              : }
    1479              : 
    1480              : /* Careful: the returned list is managed by the entry, and will only be valid for a short time */
    1481          490 : static AccountValueList * gncEntryGetIntTaxValues (GncEntry *entry, gboolean is_cust_doc)
    1482              : {
    1483          490 :     if (!entry) return NULL;
    1484          490 :     gncEntryRecomputeValues (entry);
    1485          490 :     return (is_cust_doc ? entry->i_tax_values : entry->b_tax_values);
    1486              : }
    1487              : 
    1488            0 : static gnc_numeric gncEntryGetIntDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
    1489              : {
    1490            0 :     if (!entry) return gnc_numeric_zero();
    1491            0 :     gncEntryRecomputeValues (entry);
    1492            0 :     if (round)
    1493            0 :         return (is_cust_doc ? entry->i_disc_value_rounded : gnc_numeric_zero());
    1494              :     else
    1495            0 :         return (is_cust_doc ? entry->i_disc_value : gnc_numeric_zero());
    1496              : }
    1497              : 
    1498              : /* Note contrary to the GetDoc*Value and GetBal*Value functions below
    1499              :  * this function will always round the net price to the entry's
    1500              :  * currency denominator (being the invoice/bill denom or 100000 if not
    1501              :  * included in a bill or invoice) */
    1502            0 : gnc_numeric gncEntryGetPrice (const GncEntry *entry, gboolean cust_doc, gboolean net)
    1503              : {
    1504              :     gnc_numeric result;
    1505              :     int denom;
    1506              : 
    1507            0 :     if (!entry) return gnc_numeric_zero();
    1508            0 :     if (!net) return (cust_doc ? entry->i_price : entry->b_price);
    1509              : 
    1510              : 
    1511              :     /* Compute the net price */
    1512            0 :     if (cust_doc)
    1513            0 :         gncEntryComputeValueInt (entry->quantity, entry->i_price,
    1514            0 :                                  (entry->i_taxable ? entry->i_tax_table : NULL),
    1515            0 :                                  entry->i_taxincluded,
    1516            0 :                                  entry->i_discount, entry->i_disc_type,
    1517            0 :                                  entry->i_disc_how,
    1518              :                                  NULL, NULL, NULL, &result);
    1519              :     else
    1520            0 :         gncEntryComputeValueInt (entry->quantity, entry->b_price,
    1521            0 :                                  (entry->b_taxable ? entry->b_tax_table : NULL),
    1522            0 :                                  entry->b_taxincluded,
    1523              :                                  gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
    1524              :                                  NULL, NULL, NULL, &result);
    1525              : 
    1526              :     /* Determine the commodity denominator */
    1527            0 :     denom = get_entry_commodity_denom (entry);
    1528              : 
    1529            0 :     result = gnc_numeric_convert (result, denom,
    1530              :                                   GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1531              : 
    1532            0 :     return result;
    1533              : }
    1534              : 
    1535          447 : gnc_numeric gncEntryGetDocValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
    1536              : {
    1537          447 :     gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
    1538          447 :     return (is_cn ? gnc_numeric_neg (value) : value);
    1539              : }
    1540              : 
    1541           68 : gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
    1542              : {
    1543           68 :     gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
    1544           68 :     return (is_cn ? gnc_numeric_neg (value) : value);
    1545              : }
    1546              : 
    1547              : /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
    1548          490 : AccountValueList * gncEntryGetDocTaxValues (GncEntry *entry, gboolean is_cust_doc, gboolean is_cn)
    1549              : {
    1550          490 :     AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
    1551          490 :     AccountValueList *values = NULL, *node;
    1552              : 
    1553              :     /* Make a copy of the list with negated values if necessary. */
    1554          615 :     for (node = int_values; node; node = node->next)
    1555              :     {
    1556          125 :         GncAccountValue *acct_val = node->data;
    1557          125 :         values = gncAccountValueAdd (values, acct_val->account,
    1558            0 :                                      (is_cn ? gnc_numeric_neg (acct_val->value)
    1559              :                                       : acct_val->value));
    1560              :     }
    1561              : 
    1562          490 :     return values;
    1563              : }
    1564              : 
    1565            0 : gnc_numeric gncEntryGetDocDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
    1566              : {
    1567            0 :     gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
    1568            0 :     return (is_cn ? gnc_numeric_neg (value) : value);
    1569              : }
    1570              : 
    1571          108 : gnc_numeric gncEntryGetBalValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
    1572              : {
    1573          108 :     gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
    1574          108 :     return (is_cust_doc ? gnc_numeric_neg (value) : value);
    1575              : }
    1576              : 
    1577          108 : gnc_numeric gncEntryGetBalTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
    1578              : {
    1579          108 :     gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
    1580          108 :     return (is_cust_doc ? gnc_numeric_neg (value) : value);
    1581              : }
    1582              : 
    1583              : /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
    1584            0 : AccountValueList * gncEntryGetBalTaxValues (GncEntry *entry, gboolean is_cust_doc)
    1585              : {
    1586            0 :     AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
    1587            0 :     AccountValueList *values = NULL, *node;
    1588              : 
    1589              :     /* Make a copy of the list with negated values if necessary. */
    1590            0 :     for (node = int_values; node; node = node->next)
    1591              :     {
    1592            0 :         GncAccountValue *acct_val = node->data;
    1593            0 :         values = gncAccountValueAdd (values, acct_val->account,
    1594            0 :                                      (is_cust_doc ? gnc_numeric_neg (acct_val->value)
    1595              :                                       : acct_val->value));
    1596              :     }
    1597              : 
    1598            0 :     return values;
    1599              : }
    1600              : 
    1601            0 : gnc_numeric gncEntryGetBalDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
    1602              : {
    1603            0 :     gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
    1604            0 :     return (is_cust_doc ? gnc_numeric_neg (value) : value);
    1605              : }
    1606              : 
    1607              : /* XXX this existence of this routine is just wrong */
    1608            0 : gboolean gncEntryIsOpen (const GncEntry *entry)
    1609              : {
    1610            0 :     if (!entry) return FALSE;
    1611            0 :     return (qof_instance_get_editlevel(entry) > 0);
    1612              : }
    1613              : 
    1614              : /* ================================================================ */
    1615              : 
    1616         1299 : void gncEntryBeginEdit (GncEntry *entry)
    1617              : {
    1618         1299 :     qof_begin_edit(&entry->inst);
    1619         1299 : }
    1620              : 
    1621            0 : static void gncEntryOnError (QofInstance *entry, QofBackendError errcode)
    1622              : {
    1623            0 :     PERR("Entry QofBackend Failure: %d", errcode);
    1624            0 :     gnc_engine_signal_commit_error( errcode );
    1625            0 : }
    1626              : 
    1627         1206 : static void gncEntryOnDone (QofInstance *inst) {}
    1628              : 
    1629           20 : static void entry_free (QofInstance *inst)
    1630              : {
    1631           20 :     GncEntry *entry = (GncEntry *)inst;
    1632           20 :     gncEntryFree (entry);
    1633           20 : }
    1634              : 
    1635         1298 : void gncEntryCommitEdit (GncEntry *entry)
    1636              : {
    1637              :     /* GnuCash 2.6.3 and earlier didn't handle entry kvp's... */
    1638         1298 :     if (qof_instance_has_kvp(QOF_INSTANCE(entry)))
    1639            0 :         gnc_features_set_used (qof_instance_get_book (QOF_INSTANCE (entry)),
    1640              :                                GNC_FEATURE_KVP_EXTRA_DATA);
    1641              : 
    1642         1298 :     if (!qof_commit_edit (QOF_INSTANCE(entry))) return;
    1643         1226 :     qof_commit_edit_part2 (&entry->inst, gncEntryOnError,
    1644              :                            gncEntryOnDone, entry_free);
    1645              : }
    1646              : 
    1647          240 : int gncEntryCompare (const GncEntry *a, const GncEntry *b)
    1648              : {
    1649              :     int compare;
    1650              : 
    1651          240 :     if (a == b) return 0;
    1652          240 :     if (!a && b) return -1;
    1653          240 :     if (a && !b) return 1;
    1654          240 :     g_assert (a && b);  /* Silence a static analysis warning. */
    1655          240 :     if (a->date != b->date) return a->date - b->date;
    1656          240 :     if (a->date_entered != b->date_entered) return a->date_entered - b->date_entered;
    1657              : 
    1658          240 :     compare = g_strcmp0 (a->desc, b->desc);
    1659          240 :     if (compare) return compare;
    1660              : 
    1661            0 :     compare = g_strcmp0 (a->action, b->action);
    1662            0 :     if (compare) return compare;
    1663              : 
    1664            0 :     return qof_instance_guid_compare(a, b);
    1665              : }
    1666              : 
    1667              : #define CHECK_STRING(X, Y, FIELD) \
    1668              :     if (g_strcmp0((X)->FIELD, (Y)->FIELD) != 0) \
    1669              :     { \
    1670              :         PWARN("%s differ: %s vs %s", #FIELD, (X)->FIELD, (Y)->FIELD); \
    1671              :         return FALSE; \
    1672              :     }
    1673              : 
    1674              : #define CHECK_ACCOUNT(X, Y, FIELD) \
    1675              :     if (!xaccAccountEqual((X)->FIELD, (Y)->FIELD, TRUE)) \
    1676              :     { \
    1677              :         PWARN("%s differ", #FIELD); \
    1678              :         return FALSE; \
    1679              :     }
    1680              : 
    1681              : #define CHECK_NUMERIC(X, Y, FIELD) \
    1682              :     if (!gnc_numeric_equal((X)->FIELD, (Y)->FIELD)) \
    1683              :     { \
    1684              :         PWARN("%s differ", #FIELD); \
    1685              :         return FALSE; \
    1686              :     }
    1687              : 
    1688              : #define CHECK_VALUE(X, Y, FIELD) \
    1689              :     if ((X)->FIELD != (Y)->FIELD) \
    1690              :     { \
    1691              :         PWARN("%s differ", #FIELD); \
    1692              :         return FALSE; \
    1693              :     }
    1694              : 
    1695              : 
    1696              : /* ============================================================= */
    1697              : /* Object declaration */
    1698              : 
    1699              : static void
    1700           15 : destroy_entry_on_book_close(QofInstance *ent, gpointer data)
    1701              : {
    1702           15 :     GncEntry* entry = GNC_ENTRY(ent);
    1703              : 
    1704           15 :     gncEntryBeginEdit(entry);
    1705           15 :     gncEntryDestroy(entry);
    1706           15 : }
    1707              : 
    1708              : /** Handles book end - frees all entries from the book
    1709              :  *
    1710              :  * @param book Book being closed
    1711              :  */
    1712              : static void
    1713          179 : gnc_entry_book_end(QofBook* book)
    1714              : {
    1715              :     QofCollection *col;
    1716              : 
    1717          179 :     col = qof_book_get_collection(book, GNC_ID_ENTRY);
    1718          179 :     qof_collection_foreach(col, destroy_entry_on_book_close, NULL);
    1719          179 : }
    1720              : 
    1721              : static QofObject gncEntryDesc =
    1722              : {
    1723              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    1724              :     DI(.e_type            = ) _GNC_MOD_NAME,
    1725              :     DI(.type_label        = ) "Order/Invoice/Bill Entry",
    1726              :     DI(.create            = ) (gpointer)gncEntryCreate,
    1727              :     DI(.book_begin        = ) NULL,
    1728              :     DI(.book_end          = ) gnc_entry_book_end,
    1729              :     DI(.is_dirty          = ) qof_collection_is_dirty,
    1730              :     DI(.mark_clean        = ) qof_collection_mark_clean,
    1731              :     DI(.foreach           = ) qof_collection_foreach,
    1732              :     DI(.printable         = ) NULL,
    1733              :     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
    1734              : };
    1735              : 
    1736           83 : gboolean gncEntryRegister (void)
    1737              : {
    1738              :     static QofParam params[] =
    1739              :     {
    1740              :         { ENTRY_DATE, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDate, (QofSetterFunc)gncEntrySetDate },
    1741              :         { ENTRY_DATE_ENTERED, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDateEntered, (QofSetterFunc)gncEntrySetDateEntered },
    1742              :         { ENTRY_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetDescription, (QofSetterFunc)gncEntrySetDescription },
    1743              :         { ENTRY_ACTION, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetAction, (QofSetterFunc)gncEntrySetAction },
    1744              :         { ENTRY_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetNotes, (QofSetterFunc)gncEntrySetNotes },
    1745              :         { ENTRY_QTY, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetQuantity, (QofSetterFunc)gncEntrySetQuantity },
    1746              :         { ENTRY_IPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetInvPrice, (QofSetterFunc)gncEntrySetInvPrice },
    1747              :         { ENTRY_BPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetBillPrice, (QofSetterFunc)gncEntrySetBillPrice },
    1748              :         { ENTRY_INVOICE, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetInvoice, NULL },
    1749              :         { ENTRY_IACCT, GNC_ID_ACCOUNT,  (QofAccessFunc)gncEntryGetInvAccount,  (QofSetterFunc)gncEntrySetInvAccount  },
    1750              :         { ENTRY_BACCT, GNC_ID_ACCOUNT,  (QofAccessFunc)gncEntryGetBillAccount, (QofSetterFunc)gncEntrySetBillAccount },
    1751              :         { ENTRY_BILL, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetBill, NULL },
    1752              :         {
    1753              :             ENTRY_INV_DISC_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscType,
    1754              :             (QofSetterFunc)qofEntrySetInvDiscType
    1755              :         },
    1756              :         {
    1757              :             ENTRY_INV_DISC_HOW, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscHow,
    1758              :             (QofSetterFunc)qofEntrySetInvDiscHow
    1759              :         },
    1760              :         {
    1761              :             ENTRY_INV_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxable,
    1762              :             (QofSetterFunc)gncEntrySetInvTaxable
    1763              :         },
    1764              :         {
    1765              :             ENTRY_INV_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxIncluded,
    1766              :             (QofSetterFunc)gncEntrySetInvTaxIncluded
    1767              :         },
    1768              :         {
    1769              :             ENTRY_BILL_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxable,
    1770              :             (QofSetterFunc)gncEntrySetBillTaxable
    1771              :         },
    1772              :         {
    1773              :             ENTRY_BILL_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxIncluded,
    1774              :             (QofSetterFunc)gncEntrySetBillTaxIncluded
    1775              :         },
    1776              :         { ENTRY_BILLABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillable, (QofSetterFunc)gncEntrySetBillable },
    1777              :         { ENTRY_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncEntryGetBillTo, (QofSetterFunc)gncEntrySetBillTo },
    1778              :         { ENTRY_ORDER, GNC_ID_ORDER, (QofAccessFunc)gncEntryGetOrder, NULL },
    1779              :         { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
    1780              :         { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
    1781              :         { NULL },
    1782              :     };
    1783              : 
    1784           83 :     qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncEntryCompare, params);
    1785              : 
    1786           83 :     return qof_object_register (&gncEntryDesc);
    1787              : }
        

Generated by: LCOV version 2.0-1