LCOV - code coverage report
Current view: top level - libgnucash/engine - gncInvoice.c (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 63.8 % 1023 653
Test Date: 2026-11-02 18:19:43 Functions: 77.8 % 108 84
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * gncInvoice.c -- the Core Business Invoice                        *
       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,2006 Derek Atkins
      25              :  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
      26              :  * Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
      27              :  * Author: Derek Atkins <warlord@MIT.EDU>
      28              :  */
      29              : 
      30              : #include <config.h>
      31              : 
      32              : #include <inttypes.h>
      33              : #include <glib.h>
      34              : #include <glib/gi18n.h>
      35              : #include <qofinstance-p.h>
      36              : 
      37              : #include "Transaction.h"
      38              : #include "Account.h"
      39              : #include "gncBillTermP.h"
      40              : #include "gncEntry.h"
      41              : #include "gncEntryP.h"
      42              : #include "gnc-features.h"
      43              : #include "gncJobP.h"
      44              : #include "gncInvoice.h"
      45              : #include "gncInvoiceP.h"
      46              : #include "gncOwnerP.h"
      47              : #include "engine-helpers.h"
      48              : 
      49              : struct _gncInvoice
      50              : {
      51              :     QofInstance   inst;
      52              : 
      53              :     const char    *id;
      54              :     const char    *notes;
      55              :     gboolean      active;
      56              : 
      57              :     const char    *billing_id;
      58              :     char          *printname;
      59              :     GncBillTerm   *terms;
      60              :     GList         *entries;
      61              :     GList         *prices;
      62              :     GncOwner      owner;
      63              :     GncOwner      billto;
      64              :     GncJob        *job;
      65              :     time64        date_opened;
      66              :     time64        date_posted;
      67              : 
      68              :     gnc_numeric   to_charge_amount;
      69              : 
      70              :     gnc_commodity *currency;
      71              : 
      72              :     Account       *posted_acc;
      73              :     Transaction   *posted_txn;
      74              :     GNCLot        *posted_lot;
      75              : };
      76              : 
      77              : struct _gncInvoiceClass
      78              : {
      79              :     QofInstanceClass parent_class;
      80              : };
      81              : 
      82              : static QofLogModule log_module = GNC_MOD_BUSINESS;
      83              : 
      84              : #define _GNC_MOD_NAME     GNC_ID_INVOICE
      85              : 
      86              : #define GNC_INVOICE_IS_CN "credit-note"
      87              : #define GNC_INVOICE_DOCLINK "assoc_uri" // this is the old name for the document link, kept for compatibility
      88              : 
      89              : #define SET_STR(obj, member, str) { \
      90              :     if (!g_strcmp0 (member, str)) return; \
      91              :     gncInvoiceBeginEdit (obj); \
      92              :     CACHE_REPLACE (member, str); \
      93              :     }
      94              : 
      95              : static void mark_invoice (GncInvoice *invoice);
      96              : static void
      97          788 : mark_invoice (GncInvoice *invoice)
      98              : {
      99          788 :     qof_instance_set_dirty (&invoice->inst);
     100          788 :     qof_event_gen (&invoice->inst, QOF_EVENT_MODIFY, NULL);
     101          788 : }
     102              : 
     103          100 : QofBook * gncInvoiceGetBook (GncInvoice *x)
     104              : {
     105          100 :     return qof_instance_get_book (QOF_INSTANCE(x));
     106              : }
     107              : 
     108              : /* ================================================================== */
     109              : 
     110              : enum
     111              : {
     112              :     PROP_0,
     113              : //  PROP_ID,            /* Table */
     114              : //  PROP_DATE_OPENED,   /* Table */
     115              : //  PROP_DATE_POSTED,   /* Table */
     116              :     PROP_NOTES,         /* Table */
     117              : //  PROP_ACTIVE,        /* Table */
     118              : //  PROP_CURRENCY,      /* Table */
     119              : //  PROP_OWNER_TYPE,    /* Table */
     120              : //  PROP_OWNER,         /* Table */
     121              : //  PROP_TERMS,         /* Table */
     122              : //  PROP_BILLING_ID,    /* Table */
     123              : //  PROP_POST_TXN,      /* Table */
     124              : //  PROP_POST_LOT,      /* Table */
     125              : //  PROP_POST_ACCOUNT,  /* Table */
     126              : //  PROP_BILLTO_TYPE,   /* Table */
     127              : //  PROP_BILLTO,        /* Table */
     128              : //  PROP_CHARGE_AMOUNT, /* Table, (numeric) */
     129              : };
     130              : 
     131              : /* GObject Initialization */
     132          259 : G_DEFINE_TYPE(GncInvoice, gnc_invoice, QOF_TYPE_INSTANCE)
     133              : 
     134              : static void
     135           98 : gnc_invoice_init (GncInvoice* inv)
     136              : {
     137           98 :     inv->date_posted = INT64_MAX;
     138           98 :     inv->date_opened = INT64_MAX;
     139           98 : }
     140              : 
     141              : static void
     142           28 : gnc_invoice_dispose (GObject *invp)
     143              : {
     144           28 :     G_OBJECT_CLASS(gnc_invoice_parent_class)->dispose(invp);
     145           28 : }
     146              : 
     147              : static void
     148           28 : gnc_invoice_finalize (GObject* invp)
     149              : {
     150           28 :     G_OBJECT_CLASS(gnc_invoice_parent_class)->finalize(invp);
     151           28 : }
     152              : 
     153              : static void
     154            1 : gnc_invoice_get_property (GObject         *object,
     155              :                           guint            prop_id,
     156              :                           GValue          *value,
     157              :                           GParamSpec      *pspec)
     158              : {
     159              :     GncInvoice *inv;
     160              : 
     161            1 :     g_return_if_fail (GNC_IS_INVOICE(object));
     162              : 
     163            1 :     inv = GNC_INVOICE(object);
     164            1 :     switch (prop_id)
     165              :     {
     166            1 :     case PROP_NOTES:
     167            1 :         g_value_set_string (value, inv->notes);
     168            1 :         break;
     169            0 :     default:
     170            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     171            0 :         break;
     172              :     }
     173              : }
     174              : 
     175              : static void
     176            1 : gnc_invoice_set_property (GObject         *object,
     177              :                           guint            prop_id,
     178              :                           const GValue    *value,
     179              :                           GParamSpec      *pspec)
     180              : {
     181              :     GncInvoice *inv;
     182              : 
     183            1 :     g_return_if_fail (GNC_IS_INVOICE(object));
     184              : 
     185            1 :     inv = GNC_INVOICE(object);
     186            1 :     g_assert (qof_instance_get_editlevel (inv));
     187              : 
     188            1 :     switch (prop_id)
     189              :     {
     190            1 :     case PROP_NOTES:
     191            1 :         gncInvoiceSetNotes (inv, g_value_get_string (value));
     192            1 :         break;
     193            0 :     default:
     194            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     195            0 :         break;
     196              :     }
     197              : }
     198              : 
     199              : /** Returns a string representing this object */
     200              : static gchar*
     201            0 : impl_get_display_name (const QofInstance* inst)
     202              : {
     203              :     GncInvoice* inv;
     204              :     QofInstance* owner;
     205              :     gchar* s;
     206              : 
     207            0 :     g_return_val_if_fail (inst != NULL, FALSE);
     208            0 :     g_return_val_if_fail (GNC_IS_INVOICE(inst), FALSE);
     209              : 
     210            0 :     inv = GNC_INVOICE(inst);
     211            0 :     owner = qofOwnerGetOwner (&inv->owner);
     212            0 :     if (owner != NULL)
     213              :     {
     214              :         gchar* display_name;
     215              : 
     216            0 :         display_name = qof_instance_get_display_name (owner);
     217            0 :         s = g_strdup_printf ("Invoice %s (%s)", inv->id, display_name);
     218            0 :         g_free (display_name);
     219              :     }
     220              :     else
     221              :     {
     222            0 :         s = g_strdup_printf ("Invoice %s", inv->id);
     223              :     }
     224              : 
     225            0 :     return s;
     226              : }
     227              : 
     228              : /** Does this object refer to a specific object */
     229              : static gboolean
     230            0 : impl_refers_to_object (const QofInstance* inst, const QofInstance* ref)
     231              : {
     232              :     GncInvoice* inv;
     233              : 
     234            0 :     g_return_val_if_fail (inst != NULL, FALSE);
     235            0 :     g_return_val_if_fail (GNC_IS_INVOICE(inst), FALSE);
     236              : 
     237            0 :     inv = GNC_INVOICE(inst);
     238              : 
     239            0 :     if (GNC_IS_BILLTERM(ref))
     240              :     {
     241            0 :         return (inv->terms == GNC_BILLTERM(ref));
     242              :     }
     243            0 :     else if (GNC_IS_JOB(ref))
     244              :     {
     245            0 :         return (inv->job == GNC_JOB(ref));
     246              :     }
     247            0 :     else if (GNC_IS_COMMODITY(ref))
     248              :     {
     249            0 :         return (inv->currency == GNC_COMMODITY(ref));
     250              :     }
     251            0 :     else if (GNC_IS_ACCOUNT(ref))
     252              :     {
     253            0 :         return (inv->posted_acc == GNC_ACCOUNT(ref));
     254              :     }
     255            0 :     else if (GNC_IS_TRANSACTION(ref))
     256              :     {
     257            0 :         return (inv->posted_txn == GNC_TRANSACTION(ref));
     258              :     }
     259            0 :     else if (GNC_IS_LOT(ref))
     260              :     {
     261            0 :         return (inv->posted_lot == GNC_LOT(ref));
     262              :     }
     263              : 
     264            0 :     return FALSE;
     265              : }
     266              : 
     267              : /** Returns a list of my type of object which refers to an object.  For example, when called as
     268              :         qof_instance_get_typed_referring_object_list(taxtable, account);
     269              :     it will return the list of taxtables which refer to a specific account.  The result should be the
     270              :     same regardless of which taxtable object is used.  The list must be freed by the caller but the
     271              :     objects on the list must not.
     272              :  */
     273              : static GList*
     274            0 : impl_get_typed_referring_object_list (const QofInstance* inst, const QofInstance* ref)
     275              : {
     276            0 :     if (!GNC_IS_BILLTERM(ref) && !GNC_IS_JOB(ref) && !GNC_IS_COMMODITY(ref) && !GNC_IS_ACCOUNT(ref)
     277            0 :             && !GNC_IS_TRANSACTION(ref) && !GNC_IS_LOT(ref))
     278              :     {
     279            0 :         return NULL;
     280              :     }
     281              : 
     282            0 :     return qof_instance_get_referring_object_list_from_collection (qof_instance_get_collection (inst), ref);
     283              : }
     284              : 
     285              : static void
     286           12 : gnc_invoice_class_init (GncInvoiceClass *klass)
     287              : {
     288           12 :     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     289           12 :     QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
     290              : 
     291           12 :     gobject_class->dispose = gnc_invoice_dispose;
     292           12 :     gobject_class->finalize = gnc_invoice_finalize;
     293           12 :     gobject_class->set_property = gnc_invoice_set_property;
     294           12 :     gobject_class->get_property = gnc_invoice_get_property;
     295              : 
     296           12 :     qof_class->get_display_name = impl_get_display_name;
     297           12 :     qof_class->refers_to_object = impl_refers_to_object;
     298           12 :     qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
     299              : 
     300           12 :     g_object_class_install_property
     301              :     (gobject_class,
     302              :      PROP_NOTES,
     303              :      g_param_spec_string ("notes",
     304              :                           "Invoice Notes",
     305              :                           "The invoice notes is an arbitrary string "
     306              :                           "assigned by the user to provide notes regarding "
     307              :                           "this invoice.",
     308              :                           NULL,
     309              :                           G_PARAM_READWRITE));
     310           12 : }
     311              : 
     312              : /* Create/Destroy Functions */
     313           64 : GncInvoice *gncInvoiceCreate (QofBook *book)
     314              : {
     315              :     GncInvoice *invoice;
     316              : 
     317           64 :     if (!book) return NULL;
     318              : 
     319           64 :     invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
     320           64 :     qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
     321              : 
     322           64 :     invoice->id = CACHE_INSERT ("");
     323           64 :     invoice->notes = CACHE_INSERT ("");
     324           64 :     invoice->billing_id = CACHE_INSERT ("");
     325              : 
     326           64 :     invoice->billto.type = GNC_OWNER_CUSTOMER;
     327           64 :     invoice->active = TRUE;
     328              : 
     329           64 :     invoice->to_charge_amount = gnc_numeric_zero ();
     330              : 
     331           64 :     qof_event_gen (&invoice->inst, QOF_EVENT_CREATE, NULL);
     332              : 
     333           64 :     return invoice;
     334              : }
     335              : 
     336           34 : GncInvoice *gncInvoiceCopy (const GncInvoice *from)
     337              : {
     338              :     GncInvoice *invoice;
     339              :     QofBook* book;
     340              :     GList *node;
     341           34 :     GValue v = G_VALUE_INIT;
     342              : 
     343           34 :     g_assert (from);
     344           34 :     book = qof_instance_get_book (from);
     345           34 :     g_assert (book);
     346              : 
     347           34 :     invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
     348           34 :     qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
     349              : 
     350           34 :     gncInvoiceBeginEdit (invoice);
     351              : 
     352           34 :     invoice->id = CACHE_INSERT (from->id);
     353           34 :     invoice->notes = CACHE_INSERT (from->notes);
     354           34 :     invoice->billing_id = CACHE_INSERT (from->billing_id);
     355           34 :     invoice->active = from->active;
     356              : 
     357           34 :     qof_instance_get_kvp (QOF_INSTANCE (from), &v, 1, GNC_INVOICE_IS_CN);
     358           34 :     if (G_VALUE_HOLDS_INT64 (&v))
     359            0 :          qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
     360           34 :     g_value_unset (&v);
     361              : 
     362           34 :     invoice->terms = from->terms;
     363           34 :     gncBillTermIncRef (invoice->terms);
     364              : 
     365           34 :     gncOwnerCopy (&from->billto, &invoice->billto);
     366           34 :     gncOwnerCopy (&from->owner, &invoice->owner);
     367           34 :     invoice->job = from->job; // FIXME: Need IncRef or similar here?!?
     368              : 
     369           34 :     invoice->to_charge_amount = from->to_charge_amount;
     370           34 :     invoice->date_opened = from->date_opened;
     371              : 
     372              :     // Oops. Do not forget to copy the pointer to the correct currency here.
     373           34 :     invoice->currency = from->currency;
     374              : 
     375           34 :     gncInvoiceSetDocLink (invoice, gncInvoiceGetDocLink (from));
     376              : 
     377              :     // Copy all invoice->entries
     378           34 :     for (node = from->entries; node; node = node->next)
     379              :     {
     380            0 :         GncEntry *from_entry = node->data;
     381            0 :         GncEntry *to_entry = gncEntryCreate (book);
     382            0 :         gncEntryCopy (from_entry, to_entry, FALSE);
     383              : 
     384            0 :         switch (gncInvoiceGetOwnerType (invoice))
     385              :         {
     386            0 :         case GNC_OWNER_VENDOR:
     387              :         case GNC_OWNER_EMPLOYEE:
     388              :             // this is a vendor bill, or an expense voucher
     389            0 :             gncBillAddEntry (invoice, to_entry);
     390            0 :             break;
     391            0 :         case GNC_OWNER_CUSTOMER:
     392              :         default:
     393              :             // this is an invoice
     394            0 :             gncInvoiceAddEntry (invoice, to_entry);
     395            0 :             break;
     396              :         }
     397              :     }
     398              : 
     399              :     // FIXME: The prices are not (yet) copied; is this a problem?
     400              : 
     401              :     // Posted-date and the posted Txn is intentionally not copied; the
     402              :     // copy isn't "posted" but needs to be posted by the user.
     403           34 :     mark_invoice (invoice);
     404           34 :     gncInvoiceCommitEdit (invoice);
     405              : 
     406           34 :     return invoice;
     407              : }
     408              : 
     409           28 : void gncInvoiceDestroy (GncInvoice *invoice)
     410              : {
     411           28 :     if (!invoice) return;
     412           28 :     qof_instance_set_destroying (invoice, TRUE);
     413           28 :     gncInvoiceCommitEdit (invoice);
     414              : }
     415              : 
     416           28 : static void gncInvoiceFree (GncInvoice *invoice)
     417              : {
     418           28 :     if (!invoice) return;
     419              : 
     420           28 :     qof_event_gen (&invoice->inst, QOF_EVENT_DESTROY, NULL);
     421              : 
     422           28 :     CACHE_REMOVE (invoice->id);
     423           28 :     CACHE_REMOVE (invoice->notes);
     424           28 :     CACHE_REMOVE (invoice->billing_id);
     425           28 :     g_list_free (invoice->entries);
     426           28 :     g_list_free (invoice->prices);
     427              : 
     428           28 :     if (invoice->printname)
     429            0 :         g_free (invoice->printname);
     430              : 
     431           28 :     if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(invoice))))
     432              :     {
     433           14 :         if (invoice->terms)
     434            0 :             gncBillTermDecRef (invoice->terms);
     435              :     }
     436              : 
     437              :     /* qof_instance_release (&invoice->inst); */
     438           28 :     g_object_unref (invoice);
     439              : }
     440              : 
     441              : /* ================================================================== */
     442              : /* Set Functions */
     443              : 
     444           52 : void gncInvoiceSetID (GncInvoice *invoice, const char *id)
     445              : {
     446           52 :     if (!invoice || !id) return;
     447           52 :     SET_STR (invoice, invoice->id, id);
     448           52 :     mark_invoice (invoice);
     449           52 :     gncInvoiceCommitEdit (invoice);
     450              : }
     451              : 
     452           50 : void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner)
     453              : {
     454           50 :     if (!invoice || !owner) return;
     455           50 :     if (gncOwnerEqual (&invoice->owner, owner)) return;
     456           50 :     gncInvoiceBeginEdit (invoice);
     457           50 :     gncOwnerCopy (owner, &invoice->owner);
     458           50 :     mark_invoice (invoice);
     459           50 :     gncInvoiceCommitEdit (invoice);
     460              : }
     461              : 
     462              : static void
     463            0 : qofInvoiceSetOwner (GncInvoice *invoice, QofInstance *ent)
     464              : {
     465            0 :     if (!invoice || !ent)
     466              :     {
     467            0 :         return;
     468              :     }
     469            0 :     gncInvoiceBeginEdit (invoice);
     470            0 :     qofOwnerSetEntity (&invoice->owner, ent);
     471            0 :     mark_invoice (invoice);
     472            0 :     gncInvoiceCommitEdit (invoice);
     473              : }
     474              : 
     475              : static void
     476            0 : qofInvoiceSetBillTo (GncInvoice *invoice, QofInstance *ent)
     477              : {
     478            0 :     if (!invoice || !ent)
     479              :     {
     480            0 :         return;
     481              :     }
     482            0 :     gncInvoiceBeginEdit (invoice);
     483            0 :     qofOwnerSetEntity (&invoice->billto, ent);
     484            0 :     mark_invoice (invoice);
     485            0 :     gncInvoiceCommitEdit (invoice);
     486              : }
     487              : 
     488            0 : void gncInvoiceSetDateOpenedGDate (GncInvoice *invoice, const GDate *date)
     489              : {
     490            0 :     g_assert (date);
     491            0 :     gncInvoiceSetDateOpened(invoice, time64CanonicalDayTime (gdate_to_time64 (*date)));
     492            0 : }
     493              : 
     494           19 : void gncInvoiceSetDateOpened (GncInvoice *invoice, time64 date)
     495              : {
     496           19 :     if (!invoice) return;
     497           19 :     if (date == invoice->date_opened) return;
     498           19 :     gncInvoiceBeginEdit (invoice);
     499           19 :     invoice->date_opened = date;
     500           19 :     mark_invoice (invoice);
     501           19 :     gncInvoiceCommitEdit (invoice);
     502              : }
     503              : 
     504           78 : void gncInvoiceSetDatePosted (GncInvoice *invoice, time64 date)
     505              : {
     506           78 :     if (!invoice) return;
     507           78 :     if (date == invoice->date_posted) return;
     508           78 :     gncInvoiceBeginEdit (invoice);
     509           78 :     invoice->date_posted = date;
     510           78 :     mark_invoice (invoice);
     511           78 :     gncInvoiceCommitEdit (invoice);
     512              : }
     513              : 
     514           10 : void gncInvoiceSetTerms (GncInvoice *invoice, GncBillTerm *terms)
     515              : {
     516           10 :     if (!invoice) return;
     517           10 :     if (invoice->terms == terms) return;
     518           10 :     gncInvoiceBeginEdit (invoice);
     519           10 :     if (invoice->terms)
     520            5 :         gncBillTermDecRef (invoice->terms);
     521           10 :     invoice->terms = terms;
     522           10 :     if (invoice->terms)
     523           10 :         gncBillTermIncRef (invoice->terms);
     524           10 :     mark_invoice (invoice);
     525           10 :     gncInvoiceCommitEdit (invoice);
     526              : }
     527              : 
     528           11 : void gncInvoiceSetBillingID (GncInvoice *invoice, const char *billing_id)
     529              : {
     530           11 :     if (!invoice) return;
     531           11 :     SET_STR (invoice, invoice->billing_id, billing_id);
     532           10 :     mark_invoice (invoice);
     533           10 :     gncInvoiceCommitEdit (invoice);
     534              : }
     535              : 
     536           31 : void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes)
     537              : {
     538           31 :     if (!invoice || !notes) return;
     539           31 :     SET_STR (invoice, invoice->notes, notes);
     540           30 :     mark_invoice (invoice);
     541           30 :     gncInvoiceCommitEdit (invoice);
     542              : }
     543              : 
     544           38 : void gncInvoiceSetDocLink (GncInvoice *invoice, const char *doclink)
     545              : {
     546           38 :     if (!invoice || !doclink) return;
     547              : 
     548            3 :     gncInvoiceBeginEdit (invoice);
     549              : 
     550            3 :     if (doclink[0] == '\0')
     551              :     {
     552            1 :         qof_instance_set_kvp (QOF_INSTANCE (invoice), NULL, 1, GNC_INVOICE_DOCLINK);
     553              :     }
     554              :     else
     555              :     {
     556            2 :         GValue v = G_VALUE_INIT;
     557            2 :         g_value_init (&v, G_TYPE_STRING);
     558            2 :         g_value_set_static_string (&v, doclink);
     559            2 :         qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_DOCLINK);
     560            2 :         g_value_unset (&v);
     561              :     }
     562            3 :     qof_instance_set_dirty (QOF_INSTANCE(invoice));
     563            3 :     gncInvoiceCommitEdit (invoice);
     564              : }
     565              : 
     566            5 : void gncInvoiceSetActive (GncInvoice *invoice, gboolean active)
     567              : {
     568            5 :     if (!invoice) return;
     569            5 :     if (invoice->active == active) return;
     570            0 :     gncInvoiceBeginEdit (invoice);
     571            0 :     invoice->active = active;
     572            0 :     mark_invoice (invoice);
     573            0 :     gncInvoiceCommitEdit (invoice);
     574              : }
     575              : 
     576           19 : void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note)
     577              : {
     578           19 :      GValue v = G_VALUE_INIT;
     579           19 :     if (!invoice) return;
     580           19 :     gncInvoiceBeginEdit (invoice);
     581           19 :     g_value_init (&v, G_TYPE_INT64);
     582           19 :     g_value_set_int64 (&v, credit_note ? 1 : 0);
     583           19 :     qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
     584           19 :     g_value_unset (&v);
     585           19 :     mark_invoice (invoice);
     586           19 :     gncInvoiceCommitEdit (invoice);
     587              : 
     588              :     /* If this is a credit note, set a feature flag for it in the book
     589              :      * This will prevent older GnuCash versions that don't support
     590              :      * credit notes to open this file. */
     591           19 :     if (credit_note)
     592           19 :         gnc_features_set_used (gncInvoiceGetBook (invoice), GNC_FEATURE_CREDIT_NOTES);
     593              : }
     594              : 
     595           67 : void gncInvoiceSetCurrency (GncInvoice *invoice, gnc_commodity *currency)
     596              : {
     597           67 :     if (!invoice || !currency) return;
     598           82 :     if (invoice->currency &&
     599           15 :             gnc_commodity_equal (invoice->currency, currency))
     600           15 :         return;
     601           52 :     gncInvoiceBeginEdit (invoice);
     602           52 :     invoice->currency = currency;
     603           52 :     mark_invoice (invoice);
     604           52 :     gncInvoiceCommitEdit (invoice);
     605              : }
     606              : 
     607            0 : void gncInvoiceSetBillTo (GncInvoice *invoice, GncOwner *billto)
     608              : {
     609            0 :     if (!invoice || !billto) return;
     610            0 :     if (gncOwnerEqual (&invoice->billto, billto)) return;
     611              : 
     612            0 :     gncInvoiceBeginEdit (invoice);
     613            0 :     gncOwnerCopy (billto, &invoice->billto);
     614            0 :     mark_invoice (invoice);
     615            0 :     gncInvoiceCommitEdit (invoice);
     616              : }
     617              : 
     618            1 : void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount)
     619              : {
     620            1 :     if (!invoice) return;
     621            1 :     if (gnc_numeric_equal (invoice->to_charge_amount, amount)) return;
     622            0 :     gncInvoiceBeginEdit (invoice);
     623            0 :     invoice->to_charge_amount = amount;
     624            0 :     mark_invoice (invoice);
     625            0 :     gncInvoiceCommitEdit (invoice);
     626              : }
     627              : 
     628           77 : void gncInvoiceSetPostedTxn (GncInvoice *invoice, Transaction *txn)
     629              : {
     630           77 :     if (!invoice) return;
     631           77 :     g_return_if_fail (invoice->posted_txn == NULL);
     632              : 
     633           77 :     gncInvoiceBeginEdit (invoice);
     634           77 :     invoice->posted_txn = txn;
     635           77 :     mark_invoice (invoice);
     636           77 :     gncInvoiceCommitEdit (invoice);
     637              : }
     638              : 
     639           77 : void gncInvoiceSetPostedLot (GncInvoice *invoice, GNCLot *lot)
     640              : {
     641           77 :     if (!invoice) return;
     642           77 :     g_return_if_fail (invoice->posted_lot == NULL);
     643              : 
     644           77 :     gncInvoiceBeginEdit (invoice);
     645           77 :     invoice->posted_lot = lot;
     646           77 :     mark_invoice (invoice);
     647           77 :     gncInvoiceCommitEdit (invoice);
     648              : }
     649              : 
     650           77 : void gncInvoiceSetPostedAcc (GncInvoice *invoice, Account *acc)
     651              : {
     652           77 :     if (!invoice) return;
     653           77 :     g_return_if_fail (invoice->posted_acc == NULL);
     654              : 
     655           77 :     gncInvoiceBeginEdit (invoice);
     656           77 :     invoice->posted_acc = acc;
     657           77 :     mark_invoice (invoice);
     658           77 :     gncInvoiceCommitEdit (invoice);
     659              : }
     660              : 
     661          109 : void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry)
     662              : {
     663              :     GncInvoice *old;
     664              : 
     665          109 :     g_assert (invoice);
     666          109 :     g_assert (entry);
     667          109 :     if (!invoice || !entry) return;
     668              : 
     669          109 :     old = gncEntryGetInvoice (entry);
     670          109 :     if (old == invoice) return; /* I already own this one */
     671          109 :     if (old) gncInvoiceRemoveEntry (old, entry);
     672              : 
     673          109 :     gncInvoiceBeginEdit (invoice);
     674          109 :     gncEntrySetInvoice (entry, invoice);
     675          109 :     invoice->entries = g_list_insert_sorted (invoice->entries, entry,
     676              :                        (GCompareFunc)gncEntryCompare);
     677          109 :     mark_invoice (invoice);
     678          109 :     gncInvoiceCommitEdit (invoice);
     679              : }
     680              : 
     681            7 : void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry)
     682              : {
     683            7 :     if (!invoice || !entry) return;
     684              : 
     685            7 :     gncInvoiceBeginEdit (invoice);
     686            7 :     gncEntrySetInvoice (entry, NULL);
     687            7 :     invoice->entries = g_list_remove (invoice->entries, entry);
     688            7 :     mark_invoice (invoice);
     689            7 :     gncInvoiceCommitEdit (invoice);
     690              : }
     691              : 
     692            0 : void gncInvoiceAddPrice (GncInvoice *invoice, GNCPrice *price)
     693              : {
     694              :     GList *node;
     695              :     gnc_commodity *commodity;
     696              : 
     697            0 :     if (!invoice || !price) return;
     698              : 
     699              :     /* Keep only one price per commodity per invoice
     700              :      * So if a price was set previously remove it first */
     701            0 :     node = g_list_first (invoice->prices);
     702            0 :     commodity = gnc_price_get_commodity (price);
     703            0 :     while (node != NULL)
     704              :     {
     705            0 :         GNCPrice *curr = (GNCPrice*)node->data;
     706            0 :         if (gnc_commodity_equal (commodity, gnc_price_get_commodity (curr)))
     707            0 :             break;
     708            0 :         node = g_list_next (node);
     709              :     }
     710              : 
     711            0 :     gncInvoiceBeginEdit (invoice);
     712            0 :     if (node)
     713            0 :         invoice->prices = g_list_delete_link (invoice->prices, node);
     714            0 :     invoice->prices = g_list_prepend (invoice->prices, price);
     715            0 :     mark_invoice (invoice);
     716            0 :     gncInvoiceCommitEdit (invoice);
     717              : }
     718              : 
     719            5 : void gncBillAddEntry (GncInvoice *bill, GncEntry *entry)
     720              : {
     721              :     GncInvoice *old;
     722              : 
     723            5 :     g_assert (bill);
     724            5 :     g_assert (entry);
     725            5 :     if (!bill || !entry) return;
     726              : 
     727            5 :     old = gncEntryGetBill (entry);
     728            5 :     if (old == bill) return;    /* I already own this one */
     729            5 :     if (old) gncBillRemoveEntry (old, entry);
     730              : 
     731            5 :     gncInvoiceBeginEdit (bill);
     732            5 :     gncEntrySetBill (entry, bill);
     733            5 :     bill->entries = g_list_insert_sorted (bill->entries, entry,
     734              :                                           (GCompareFunc)gncEntryCompare);
     735            5 :     mark_invoice (bill);
     736            5 :     gncInvoiceCommitEdit (bill);
     737              : }
     738              : 
     739            0 : void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry)
     740              : {
     741            0 :     if (!bill || !entry) return;
     742              : 
     743            0 :     gncInvoiceBeginEdit (bill);
     744            0 :     gncEntrySetBill (entry, NULL);
     745            0 :     bill->entries = g_list_remove (bill->entries, entry);
     746            0 :     mark_invoice (bill);
     747            0 :     gncInvoiceCommitEdit (bill);
     748              : }
     749              : 
     750            0 : void gncInvoiceSortEntries (GncInvoice *invoice)
     751              : {
     752            0 :     if (!invoice) return;
     753            0 :     invoice->entries = g_list_sort (invoice->entries,
     754              :                                    (GCompareFunc)gncEntryCompare);
     755            0 :     gncInvoiceBeginEdit (invoice);
     756            0 :     mark_invoice (invoice);
     757            0 :     gncInvoiceCommitEdit (invoice);
     758              : }
     759              : 
     760            7 : void gncInvoiceRemoveEntries (GncInvoice *invoice)
     761              : {
     762            7 :     if (!invoice) return;
     763              : 
     764              :     // gnc{Bill,Invoice}RemoveEntry free the "entry" node.
     765              :     // Make sure to save "next" first.
     766           14 :     for (GList *next, *node = invoice->entries; node; node = next)
     767              :     {
     768            7 :         next = node->next;
     769            7 :         GncEntry *entry = node->data;
     770              : 
     771            7 :         switch (gncInvoiceGetOwnerType (invoice))
     772              :         {
     773            0 :         case GNC_OWNER_VENDOR:
     774              :         case GNC_OWNER_EMPLOYEE:
     775              :             // this is a vendor bill, or an expense voucher
     776            0 :             gncBillRemoveEntry (invoice, entry);
     777            0 :             break;
     778            7 :         case GNC_OWNER_CUSTOMER:
     779              :         default:
     780              :             // this is an invoice
     781            7 :             gncInvoiceRemoveEntry (invoice, entry);
     782            7 :             break;
     783              :         }
     784              : 
     785              :         /* If the entry is no longer referenced by any document,
     786              :          * remove it.
     787              :          */
     788           20 :         if (!(gncEntryGetInvoice (entry) ||
     789            7 :               gncEntryGetBill (entry) ||
     790            6 :               gncEntryGetOrder (entry)))
     791              :         {
     792            6 :             gncEntryBeginEdit (entry);
     793            6 :             gncEntryDestroy (entry);
     794              :         }
     795              :     }
     796              : }
     797              : 
     798              : /* ================================================================== */
     799              : /* Get Functions */
     800              : 
     801          371 : const char * gncInvoiceGetID (const GncInvoice *invoice)
     802              : {
     803          371 :     if (!invoice) return NULL;
     804          371 :     return invoice->id;
     805              : }
     806              : 
     807         1974 : const GncOwner * gncInvoiceGetOwner (const GncInvoice *invoice)
     808              : {
     809         1974 :     if (!invoice) return NULL;
     810         1932 :     return &invoice->owner;
     811              : }
     812              : 
     813            0 : static QofInstance * qofInvoiceGetOwner (GncInvoice *invoice)
     814              : {
     815              :     GncOwner *owner;
     816              : 
     817            0 :     if (!invoice)
     818              :     {
     819            0 :         return NULL;
     820              :     }
     821            0 :     owner = &invoice->owner;
     822            0 :     return QOF_INSTANCE(owner);
     823              : }
     824              : 
     825            0 : static QofInstance * qofInvoiceGetBillTo (GncInvoice *invoice)
     826              : {
     827              :     GncOwner *billto;
     828              : 
     829            0 :     if (!invoice)
     830              :     {
     831            0 :         return NULL;
     832              :     }
     833            0 :     billto = &invoice->billto;
     834            0 :     return QOF_INSTANCE(billto);
     835              : }
     836              : 
     837           11 : time64 gncInvoiceGetDateOpened (const GncInvoice *invoice)
     838              : {
     839           11 :     if (!invoice) return INT64_MAX;
     840           11 :     return invoice->date_opened;
     841              : }
     842              : 
     843           98 : time64 gncInvoiceGetDatePosted (const GncInvoice *invoice)
     844              : {
     845           98 :     if (!invoice) return INT64_MAX;
     846           98 :     return invoice->date_posted;
     847              : }
     848              : 
     849           80 : time64 gncInvoiceGetDateDue (const GncInvoice *invoice)
     850              : {
     851              :     Transaction *txn;
     852           80 :     if (!invoice) return INT64_MAX;
     853           80 :     txn = gncInvoiceGetPostedTxn (invoice);
     854           80 :     if (!txn) return INT64_MAX;
     855           80 :     return xaccTransRetDateDue (txn);
     856              : }
     857              : 
     858           31 : GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice)
     859              : {
     860           31 :     if (!invoice) return NULL;
     861           31 :     return invoice->terms;
     862              : }
     863              : 
     864           27 : const char * gncInvoiceGetBillingID (const GncInvoice *invoice)
     865              : {
     866           27 :     if (!invoice) return NULL;
     867           27 :     return invoice->billing_id;
     868              : }
     869              : 
     870           70 : const char * gncInvoiceGetNotes (const GncInvoice *invoice)
     871              : {
     872           70 :     if (!invoice) return NULL;
     873           70 :     return invoice->notes;
     874              : }
     875              : 
     876           51 : const char * gncInvoiceGetDocLink (const GncInvoice *invoice)
     877              : {
     878           51 :     if (!invoice) return NULL;
     879              : 
     880           51 :     GValue v = G_VALUE_INIT;
     881           51 :     qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_DOCLINK);
     882           51 :     const char *rv = G_VALUE_HOLDS_STRING(&v) ? g_value_get_string (&v) : NULL;
     883           51 :     g_value_unset (&v);
     884              : 
     885           51 :     return rv;
     886              : }
     887              : 
     888          728 : GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice)
     889              : {
     890              :     const GncOwner *owner;
     891          728 :     g_return_val_if_fail (invoice, GNC_OWNER_NONE);
     892              : 
     893          728 :     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
     894          728 :     return (gncOwnerGetType (owner));
     895              : }
     896              : 
     897          219 : static gnc_numeric gncInvoiceSumTaxesInternal (AccountValueList *taxes)
     898              : {
     899          219 :     gnc_numeric tt = gnc_numeric_zero ();
     900              : 
     901          219 :     if (taxes)
     902              :     {
     903              :         GList *node;
     904              :         // Note we can use GNC_DENOM_AUTO below for rounding because
     905              :         // the values passed to this function should already have been rounded
     906              :         // to the desired denom and addition will just preserve it in that case.
     907           34 :         for (node = taxes; node; node=node->next)
     908              :         {
     909           17 :             GncAccountValue *acc_val = node->data;
     910           17 :             tt = gnc_numeric_add (tt, acc_val->value, GNC_DENOM_AUTO,
     911              :                                   GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     912              :         }
     913              :     }
     914          219 :     return tt;
     915              : }
     916              : 
     917          373 : static gnc_numeric gncInvoiceGetNetAndTaxesInternal (GncInvoice *invoice, gboolean use_value,
     918              :                                                      AccountValueList **taxes,
     919              :                                                      gboolean use_payment_type,
     920              :                                                      GncEntryPaymentType type)
     921              : {
     922              :     GList *node;
     923          373 :     gnc_numeric net_total = gnc_numeric_zero ();
     924              :     gboolean is_cust_doc, is_cn;
     925          373 :     AccountValueList *tv_list = NULL;
     926          373 :     int denom = gnc_commodity_get_fraction (gncInvoiceGetCurrency (invoice));
     927              : 
     928          373 :     g_return_val_if_fail (invoice, net_total);
     929              : 
     930          373 :     ENTER ("");
     931              :     /* Is the current document an invoice/credit note related to a customer or a vendor/employee ?
     932              :      * The GncEntry code needs to know to return the proper entry amounts
     933              :      */
     934          373 :     is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
     935          373 :     is_cn = gncInvoiceGetIsCreditNote (invoice);
     936              : 
     937              : 
     938          947 :     for (node = gncInvoiceGetEntries (invoice); node; node = node->next)
     939              :     {
     940          574 :         GncEntry *entry = node->data;
     941              :         gnc_numeric value;
     942              : 
     943          574 :         if (use_payment_type && gncEntryGetBillPayment (entry) != type)
     944            0 :             continue;
     945              : 
     946          574 :         if (use_value)
     947              :         {
     948              :             // Always use rounded net values to prevent creating imbalanced transactions on posting
     949              :             // https://bugs.gnucash.org/show_bug.cgi?id=628903
     950          379 :             value = gncEntryGetDocValue (entry, TRUE, is_cust_doc, is_cn);
     951          379 :             if (gnc_numeric_check (value) == GNC_ERROR_OK)
     952          379 :                 net_total = gnc_numeric_add (net_total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
     953              :             else
     954            0 :                 PWARN ("bad value in our entry");
     955              :         }
     956              : 
     957          574 :         if (taxes)
     958              :         {
     959          490 :             AccountValueList *entrytaxes = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
     960          490 :             tv_list = gncAccountValueAddList (tv_list, entrytaxes);
     961          490 :             gncAccountValueDestroy (entrytaxes);
     962              :         }
     963              :     }
     964              : 
     965          373 :     if (taxes)
     966              :     {
     967              :         GList *node;
     968              :         // Round tax totals (accumulated per tax account) to prevent creating imbalanced transactions on posting
     969              :         // which could otherwise happen when using a tax table with multiple tax rates
     970          342 :         for (node = tv_list; node; node=node->next)
     971              :         {
     972           25 :             GncAccountValue *acc_val = node->data;
     973           25 :             acc_val->value = gnc_numeric_convert (acc_val->value,
     974              :                                   denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     975              :         }
     976          317 :         *taxes = tv_list;
     977              :     }
     978              : 
     979          373 :     LEAVE ("%" PRId64 "/%" PRId64, net_total.num, net_total.denom);
     980          373 :     return net_total;
     981              : }
     982              : 
     983          275 : static gnc_numeric gncInvoiceGetTotalInternal (GncInvoice *invoice, gboolean use_value,
     984              :                                                gboolean use_tax,
     985              :                                                gboolean use_payment_type, GncEntryPaymentType type)
     986              : {
     987              :     AccountValueList *taxes;
     988              :     gnc_numeric total;
     989              : 
     990          275 :     if (!invoice) return gnc_numeric_zero ();
     991              : 
     992          275 :     ENTER ("");
     993          275 :     total = gncInvoiceGetNetAndTaxesInternal (invoice, use_value, use_tax? &taxes : NULL, use_payment_type, type);
     994              : 
     995          275 :     if (use_tax)
     996              :     {
     997              :         // Note we can use GNC_DENOM_AUTO below for rounding because
     998              :         // the values passed to this function should already have been rounded
     999              :         // to the desired denom and addition will just preserve it in that case.
    1000          219 :         total = gnc_numeric_add (total, gncInvoiceSumTaxesInternal (taxes),
    1001              :                                  GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1002          219 :         gncAccountValueDestroy (taxes);
    1003              :     }
    1004          275 :     LEAVE ("%" PRId64 "/%" PRId64, total.num, total.denom);
    1005          275 :     return total;
    1006              : }
    1007              : 
    1008          184 : gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice)
    1009              : {
    1010          184 :     if (!invoice) return gnc_numeric_zero ();
    1011          184 :     return gncInvoiceGetTotalInternal (invoice, TRUE, TRUE, FALSE, 0);
    1012              : }
    1013              : 
    1014           56 : gnc_numeric gncInvoiceGetTotalSubtotal (GncInvoice *invoice)
    1015              : {
    1016           56 :     if (!invoice) return gnc_numeric_zero ();
    1017           56 :     return gncInvoiceGetTotalInternal (invoice, TRUE, FALSE, FALSE, 0);
    1018              : }
    1019              : 
    1020           35 : gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice)
    1021              : {
    1022           35 :     if (!invoice) return gnc_numeric_zero ();
    1023           35 :     return gncInvoiceGetTotalInternal (invoice, FALSE, TRUE, FALSE, 0);
    1024              : }
    1025              : 
    1026            0 : gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type)
    1027              : {
    1028            0 :     if (!invoice) return gnc_numeric_zero ();
    1029            0 :     return gncInvoiceGetTotalInternal (invoice, TRUE, TRUE, TRUE, type);
    1030              : }
    1031              : 
    1032           98 : AccountValueList *gncInvoiceGetTotalTaxList (GncInvoice *invoice)
    1033              : {
    1034              :     AccountValueList *taxes;
    1035           98 :     if (!invoice) return NULL;
    1036              : 
    1037           98 :     gncInvoiceGetNetAndTaxesInternal (invoice, FALSE, &taxes, FALSE, 0);
    1038           98 :     return taxes;
    1039              : }
    1040              : 
    1041            0 : GList * gncInvoiceGetTypeListForOwnerType (GncOwnerType type)
    1042              : {
    1043            0 :     GList *type_list = NULL;
    1044            0 :     switch (type)
    1045              :     {
    1046            0 :     case GNC_OWNER_CUSTOMER:
    1047            0 :         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_INVOICE));
    1048            0 :         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_CREDIT_NOTE));
    1049            0 :         return type_list;
    1050            0 :     case GNC_OWNER_VENDOR:
    1051            0 :         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_INVOICE));
    1052            0 :         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_CREDIT_NOTE));
    1053            0 :         return type_list;
    1054            0 :     case GNC_OWNER_EMPLOYEE:
    1055            0 :         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_INVOICE));
    1056            0 :         type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_CREDIT_NOTE));
    1057            0 :         return type_list;
    1058            0 :     default:
    1059            0 :         PWARN("Bad owner type, no invoices.");
    1060            0 :         return NULL;
    1061              :     }
    1062              : 
    1063              : }
    1064              : 
    1065          192 : GncInvoiceType gncInvoiceGetType (const GncInvoice *invoice)
    1066              : {
    1067          192 :     if (!invoice) return GNC_INVOICE_UNDEFINED;
    1068          192 :     switch (gncInvoiceGetOwnerType (invoice))
    1069              :     {
    1070          132 :     case GNC_OWNER_CUSTOMER:
    1071          132 :         return (gncInvoiceGetIsCreditNote (invoice) ?
    1072          132 :                 GNC_INVOICE_CUST_CREDIT_NOTE :
    1073              :                 GNC_INVOICE_CUST_INVOICE);
    1074           30 :     case GNC_OWNER_VENDOR:
    1075           30 :         return (gncInvoiceGetIsCreditNote (invoice) ?
    1076           30 :                 GNC_INVOICE_VEND_CREDIT_NOTE :
    1077              :                 GNC_INVOICE_VEND_INVOICE);
    1078           30 :     case GNC_OWNER_EMPLOYEE:
    1079           30 :         return (gncInvoiceGetIsCreditNote (invoice) ?
    1080           30 :                 GNC_INVOICE_EMPL_CREDIT_NOTE :
    1081              :                 GNC_INVOICE_EMPL_INVOICE);
    1082            0 :     default:
    1083            0 :         PWARN ("No invoice types defined for owner %d",
    1084              :                gncInvoiceGetOwnerType (invoice));
    1085            0 :         return GNC_INVOICE_UNDEFINED;
    1086              :     }
    1087              : }
    1088              : 
    1089          138 : const char * gncInvoiceGetTypeString (const GncInvoice *invoice)
    1090              : {
    1091          138 :     GncInvoiceType type = gncInvoiceGetType (invoice);
    1092          138 :     switch (type)
    1093              :     {
    1094           84 :     case GNC_INVOICE_CUST_INVOICE:
    1095           84 :         return _("Invoice");
    1096            9 :     case GNC_INVOICE_VEND_INVOICE:
    1097            9 :         return _("Bill");
    1098            9 :     case GNC_INVOICE_EMPL_INVOICE:
    1099            9 :         return _("Expense");
    1100           36 :     case GNC_INVOICE_CUST_CREDIT_NOTE:
    1101              :     case GNC_INVOICE_VEND_CREDIT_NOTE:
    1102              :     case GNC_INVOICE_EMPL_CREDIT_NOTE:
    1103           36 :         return _("Credit Note");
    1104            0 :     default:
    1105            0 :         PWARN("Unknown invoice type");
    1106            0 :         return NULL;
    1107              :     }
    1108              : }
    1109              : 
    1110          625 : gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice)
    1111              : {
    1112          625 :     if (!invoice) return NULL;
    1113          625 :     return invoice->currency;
    1114              : }
    1115              : 
    1116            3 : GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice)
    1117              : {
    1118            3 :     if (!invoice) return NULL;
    1119            3 :     return &invoice->billto;
    1120              : }
    1121              : 
    1122          114 : GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice)
    1123              : {
    1124          114 :     if (!invoice) return NULL;
    1125          114 :     return invoice->posted_lot;
    1126              : }
    1127              : 
    1128          314 : Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice)
    1129              : {
    1130          314 :     if (!invoice) return NULL;
    1131          308 :     return invoice->posted_txn;
    1132              : }
    1133              : 
    1134           56 : Account * gncInvoiceGetPostedAcc (const GncInvoice *invoice)
    1135              : {
    1136           56 :     if (!invoice) return NULL;
    1137           56 :     return invoice->posted_acc;
    1138              : }
    1139              : 
    1140            4 : gboolean gncInvoiceGetActive (const GncInvoice *invoice)
    1141              : {
    1142            4 :     if (!invoice) return FALSE;
    1143            4 :     return invoice->active;
    1144              : }
    1145              : 
    1146          688 : gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice)
    1147              : {
    1148          688 :     GValue v = G_VALUE_INIT;
    1149              :     gboolean retval;
    1150          688 :     if (!invoice) return FALSE;
    1151          688 :     qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_IS_CN);
    1152          688 :     retval = G_VALUE_HOLDS_INT64(&v) && g_value_get_int64 (&v);
    1153          688 :     g_value_unset (&v);
    1154          688 :     return retval;
    1155              : }
    1156              : 
    1157              : 
    1158            3 : gnc_numeric gncInvoiceGetToChargeAmount (const GncInvoice *invoice)
    1159              : {
    1160            3 :     if (!invoice) return gnc_numeric_zero ();
    1161            3 :     return invoice->to_charge_amount;
    1162              : }
    1163              : 
    1164          514 : EntryList * gncInvoiceGetEntries (GncInvoice *invoice)
    1165              : {
    1166          514 :     if (!invoice) return NULL;
    1167          514 :     return invoice->entries;
    1168              : }
    1169              : 
    1170            0 : GNCPrice * gncInvoiceGetPrice (GncInvoice *invoice, gnc_commodity *commodity)
    1171              : {
    1172            0 :     GList *node = g_list_first (invoice->prices);
    1173              : 
    1174            0 :     while (node != NULL)
    1175              :     {
    1176            0 :         GNCPrice *curr = (GNCPrice*)node->data;
    1177              : 
    1178            0 :         if (gnc_commodity_equal (commodity, gnc_price_get_commodity (curr)))
    1179            0 :             return curr;
    1180              : 
    1181            0 :         node = g_list_next (node);
    1182              :     }
    1183              : 
    1184            0 :     return NULL;
    1185              : }
    1186              : 
    1187              : static GncJob*
    1188            0 : qofInvoiceGetJob (const GncInvoice *invoice)
    1189              : {
    1190            0 :     if (!invoice)
    1191              :     {
    1192            0 :         return NULL;
    1193              :     }
    1194            0 :     return invoice->job;
    1195              : }
    1196              : 
    1197              : static void
    1198            0 : qofInvoiceSetJob (GncInvoice *invoice, GncJob *job)
    1199              : {
    1200            0 :     if (!invoice)
    1201              :     {
    1202            0 :         return;
    1203              :     }
    1204            0 :     invoice->job = job;
    1205              : }
    1206              : 
    1207              : void
    1208            8 : gncInvoiceDetachFromLot (GNCLot *lot)
    1209              : {
    1210            8 :     if (!lot) return;
    1211              : 
    1212            8 :     gnc_lot_begin_edit (lot);
    1213            8 :     qof_instance_set (QOF_INSTANCE (lot), "invoice", NULL, NULL);
    1214            8 :     gnc_lot_commit_edit (lot);
    1215            8 :     gnc_lot_set_cached_invoice (lot, NULL);
    1216              : }
    1217              : 
    1218              : void
    1219           74 : gncInvoiceAttachToLot (GncInvoice *invoice, GNCLot *lot)
    1220              : {
    1221              :     GncGUID *guid;
    1222           74 :     if (!invoice || !lot)
    1223            0 :         return;
    1224              : 
    1225           74 :     if (invoice->posted_lot) return;    /* Cannot reset invoice's lot */
    1226           74 :     guid  = (GncGUID*)qof_instance_get_guid (QOF_INSTANCE(invoice));
    1227           74 :     gnc_lot_begin_edit (lot);
    1228           74 :     qof_instance_set (QOF_INSTANCE (lot), "invoice", guid, NULL);
    1229           74 :     gnc_lot_commit_edit (lot);
    1230           74 :     gnc_lot_set_cached_invoice (lot, invoice);
    1231           74 :     gncInvoiceSetPostedLot (invoice, lot);
    1232              : }
    1233              : 
    1234         1714 : GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot)
    1235              : {
    1236         1714 :     GncGUID *guid = NULL;
    1237              :     QofBook *book;
    1238         1714 :     GncInvoice *invoice = NULL;
    1239              : 
    1240         1714 :     if (!lot) return NULL;
    1241              : 
    1242         1684 :     invoice = gnc_lot_get_cached_invoice (lot);
    1243         1684 :     if (!invoice)
    1244              :     {
    1245          683 :         book = gnc_lot_get_book (lot);
    1246          683 :         qof_instance_get (QOF_INSTANCE(lot), "invoice", &guid, NULL);
    1247          683 :         invoice = gncInvoiceLookup (book, guid);
    1248          683 :         guid_free (guid);
    1249          683 :         gnc_lot_set_cached_invoice (lot, invoice);
    1250              :     }
    1251              : 
    1252         1684 :     return invoice;
    1253              : }
    1254              : 
    1255              : void
    1256           74 : gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn)
    1257              : {
    1258           74 :     if (!invoice || !txn)
    1259            0 :         return;
    1260              : 
    1261           74 :     if (invoice->posted_txn) return;    /* Cannot reset invoice's txn */
    1262              : 
    1263           74 :     xaccTransBeginEdit (txn);
    1264           74 :     qof_instance_set (QOF_INSTANCE (txn), "invoice", //Prop INVOICE
    1265           74 :               qof_instance_get_guid (QOF_INSTANCE (invoice)), NULL);
    1266           74 :     xaccTransSetTxnType (txn, TXN_TYPE_INVOICE);
    1267           74 :     xaccTransCommitEdit (txn);
    1268           74 :     gncInvoiceSetPostedTxn (invoice, txn);
    1269              : }
    1270              : 
    1271              : GncInvoice *
    1272          179 : gncInvoiceGetInvoiceFromTxn (const Transaction *txn)
    1273              : {
    1274          179 :     GncGUID *guid = NULL;
    1275              :     QofBook *book;
    1276          179 :     GncInvoice *invoice = NULL;
    1277              : 
    1278          179 :     if (!txn) return NULL;
    1279              : 
    1280          179 :     book = xaccTransGetBook (txn);
    1281          179 :     qof_instance_get (QOF_INSTANCE (txn), "invoice", &guid, NULL);
    1282          179 :     invoice = gncInvoiceLookup (book, guid);
    1283          179 :     guid_free (guid);
    1284          179 :     return invoice;
    1285              : }
    1286              : 
    1287           27 : gboolean gncInvoiceAmountPositive (const GncInvoice *invoice)
    1288              : {
    1289           27 :     switch (gncInvoiceGetType (invoice))
    1290              :     {
    1291           18 :     case GNC_INVOICE_CUST_INVOICE:
    1292              :     case GNC_INVOICE_VEND_CREDIT_NOTE:
    1293              :     case GNC_INVOICE_EMPL_CREDIT_NOTE:
    1294           18 :         return TRUE;
    1295            9 :     case GNC_INVOICE_CUST_CREDIT_NOTE:
    1296              :     case GNC_INVOICE_VEND_INVOICE:
    1297              :     case GNC_INVOICE_EMPL_INVOICE:
    1298            9 :         return FALSE;
    1299            0 :     case GNC_INVOICE_UNDEFINED:
    1300              :     default:
    1301              :         /* Should never be reached.
    1302              :          * If it is, perhaps a new value is added to GncInvoiceType ? */
    1303            0 :         g_assert_not_reached ();
    1304              :         return FALSE;
    1305              :     }
    1306              : }
    1307              : 
    1308            0 : GHashTable *gncInvoiceGetForeignCurrencies (const GncInvoice *invoice)
    1309              : {
    1310              :     EntryList *entries_iter;
    1311            0 :     gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
    1312            0 :     gboolean is_cn = gncInvoiceGetIsCreditNote (invoice);
    1313            0 :     GHashTable *amt_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
    1314              :                                                   NULL, g_free);
    1315            0 :     ENTER ("");
    1316              : 
    1317            0 :     for (entries_iter = invoice->entries; entries_iter != NULL; entries_iter = g_list_next(entries_iter))
    1318              :     {
    1319            0 :         GncEntry *entry = (GncEntry*)entries_iter->data;
    1320              :         Account *this_acc;
    1321              :         gnc_commodity *account_currency;
    1322            0 :         AccountValueList *tt_amts = NULL, *tt_iter;
    1323              : 
    1324              :         /* Check entry's account currency */
    1325            0 :         this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
    1326            0 :                     gncEntryGetBillAccount (entry));
    1327            0 :         account_currency = xaccAccountGetCommodity (this_acc);
    1328              : 
    1329            0 :         if (this_acc &&
    1330            0 :                 !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), account_currency))
    1331              :         {
    1332            0 :             gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, account_currency);
    1333            0 :             gnc_numeric *entry_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
    1334            0 :             *entry_amt = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
    1335            0 :             if (curr_amt)
    1336            0 :                 *entry_amt = gnc_numeric_add (*entry_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
    1337            0 :             g_hash_table_insert (amt_hash, account_currency, entry_amt);
    1338              :         }
    1339              : 
    1340              :         /* Check currencies of each account in the tax table linked
    1341              :          * to the current entry */
    1342            0 :         tt_amts = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
    1343              : 
    1344            0 :         if (!tt_amts)
    1345            0 :             continue;
    1346              : 
    1347            0 :         for (tt_iter = tt_amts; tt_iter != NULL; tt_iter = g_list_next(tt_iter))
    1348              :         {
    1349            0 :             GncAccountValue *tt_amt_val = (GncAccountValue*)tt_iter->data;
    1350            0 :             Account *tt_acc = tt_amt_val->account;
    1351            0 :             gnc_commodity *tt_acc_currency = xaccAccountGetCommodity (tt_acc);
    1352              : 
    1353            0 :             if (tt_acc &&
    1354            0 :                     !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), tt_acc_currency))
    1355              :             {
    1356            0 :                 gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, tt_acc_currency);
    1357            0 :                 gnc_numeric *tt_acc_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
    1358            0 :                 *tt_acc_amt = tt_amt_val->value;
    1359            0 :                 if (curr_amt)
    1360            0 :                     *tt_acc_amt = gnc_numeric_add (*tt_acc_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
    1361            0 :                 g_hash_table_insert (amt_hash, tt_acc_currency, tt_acc_amt);
    1362              :             }
    1363              :         }
    1364            0 :         gncAccountValueDestroy (tt_amts);
    1365              :     }
    1366              : 
    1367            0 :     LEAVE ("");
    1368            0 :     return amt_hash;
    1369              : }
    1370              : 
    1371           77 : static gboolean gncInvoicePostAddSplit (QofBook *book,
    1372              :                                         Account *acc,
    1373              :                                         Transaction *txn,
    1374              :                                         gnc_numeric value,
    1375              :                                         const gchar *memo,
    1376              :                                         const gchar *type,
    1377              :                                         GncInvoice *invoice)
    1378              : {
    1379              :     Split *split;
    1380              : 
    1381           77 :     ENTER ("");
    1382           77 :     split = xaccMallocSplit (book);
    1383              :     /* set action and memo? */
    1384              : 
    1385           77 :     xaccSplitSetMemo (split, memo);
    1386              :     /* set per book option */
    1387           77 :     gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
    1388              : 
    1389              :     /* Need to insert this split into the account AND txn before
    1390              :      * we set the Base Value.  Otherwise SetBaseValue complains
    1391              :      * that we don't have an account and fails to set the value.
    1392              :      */
    1393           77 :     xaccAccountBeginEdit (acc);
    1394           77 :     xaccAccountInsertSplit (acc, split);
    1395           77 :     xaccAccountCommitEdit (acc);
    1396           77 :     xaccTransAppendSplit (txn, split);
    1397              : 
    1398              :     /* General note on the split creations below:
    1399              :      * Invoice and bill amounts are always stored as positive values in entries
    1400              :      * So to convert them to proper splits, the amounts may have to be reverted
    1401              :      * to have the proper effect on the account balance.
    1402              :      * Credit notes have the opposite effect of invoices/bills, but their amounts
    1403              :      * are stored as negative values as well. So to convert them into splits
    1404              :      * they can be treated exactly the same as their invoice/bill counter parts.
    1405              :      * The net effect is that the owner type is sufficient to determine whether a
    1406              :      * value has to be reverted when converting an invoice/bill/cn amount to a split.
    1407              :      */
    1408           77 :     if (gnc_commodity_equal (xaccAccountGetCommodity (acc), invoice->currency))
    1409              :     {
    1410           77 :         xaccSplitSetBaseValue (split, value,
    1411           77 :                                invoice->currency);
    1412              :     }
    1413              :     else
    1414              :     {
    1415              :         /*need to do conversion */
    1416            0 :         GNCPrice *price = gncInvoiceGetPrice (invoice, xaccAccountGetCommodity (acc));
    1417              : 
    1418            0 :         if (price == NULL)
    1419              :         {
    1420              :             /*This is an error, which shouldn't even be able to happen.
    1421              :               We can't really do anything sensible about it, and this is
    1422              :                         a user-interface free zone so we can't try asking the user
    1423              :               again either, have to return NULL*/
    1424            0 :             PERR("Multiple commodities with no price.");
    1425            0 :             LEAVE ("FALSE");
    1426            0 :             return FALSE;
    1427              :         }
    1428              :         else
    1429              :         {
    1430              :             gnc_numeric converted_amount;
    1431            0 :             xaccSplitSetValue (split, value);
    1432            0 :             converted_amount = gnc_numeric_div (value, gnc_price_get_value (price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
    1433            0 :             DEBUG("converting from %f to %f\n", gnc_numeric_to_double (value), gnc_numeric_to_double (converted_amount));
    1434            0 :             xaccSplitSetAmount (split, converted_amount);
    1435              :         }
    1436              :     }
    1437              : 
    1438           77 :     LEAVE ("TRUE");
    1439           77 :     return TRUE;
    1440              : }
    1441              : 
    1442           74 : Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
    1443              :                                        time64 post_date, time64 due_date,
    1444              :                                        const char * memo, gboolean accumulatesplits,
    1445              :                                        gboolean autopay)
    1446              : {
    1447              :     Transaction *txn;
    1448              :     QofBook *book;
    1449           74 :     GNCLot *lot = NULL;
    1450              :     GList *iter;
    1451           74 :     GList *splitinfo = NULL;
    1452              :     gnc_numeric total;
    1453              :     gboolean is_cust_doc;
    1454              :     gboolean is_cn;
    1455              :     const char *name, *type;
    1456              :     char *lot_title;
    1457           74 :     Account *ccard_acct = NULL;
    1458              :     const GncOwner *owner;
    1459           74 :     int denom = xaccAccountGetCommoditySCU (acc);
    1460              :     AccountValueList *taxes;
    1461              : 
    1462           74 :     if (!invoice || !acc) return NULL;
    1463           74 :     if (gncInvoiceIsPosted (invoice)) return NULL;
    1464              : 
    1465           74 :     ENTER ("");
    1466           74 :     gncInvoiceBeginEdit (invoice);
    1467           74 :     book = qof_instance_get_book (invoice);
    1468              : 
    1469              :     /* Stabilize the Billing Terms of this invoice */
    1470           74 :     if (invoice->terms)
    1471            5 :         gncInvoiceSetTerms (invoice,
    1472              :                             gncBillTermReturnChild (invoice->terms, TRUE));
    1473              : 
    1474              :     /* GncEntry functions need to know if the invoice/credit note is for a customer or a vendor/employee. */
    1475           74 :     is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
    1476           74 :     is_cn = gncInvoiceGetIsCreditNote (invoice);
    1477              : 
    1478              :     /* Figure out if we need to separate out "credit-card" items */
    1479           74 :     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
    1480           74 :     if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE)
    1481           10 :         ccard_acct = gncEmployeeGetCCard (gncOwnerGetEmployee (owner));
    1482              : 
    1483              :     /* Create a new lot for this invoice */
    1484           74 :     lot = gnc_lot_new (book);
    1485           74 :     gncInvoiceAttachToLot (invoice, lot);
    1486           74 :     gnc_lot_begin_edit (lot);
    1487              : 
    1488           74 :     type = gncInvoiceGetTypeString (invoice);
    1489              : 
    1490              :     /* Set the lot title */
    1491           74 :     lot_title = g_strdup_printf ("%s %s", type, gncInvoiceGetID (invoice));
    1492           74 :     gnc_lot_set_title (lot, lot_title);
    1493           74 :     g_free (lot_title);
    1494              : 
    1495              :     /* Create a new transaction */
    1496           74 :     txn = xaccMallocTransaction (book);
    1497           74 :     xaccTransBeginEdit (txn);
    1498              : 
    1499           74 :     name = gncOwnerGetName (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)));
    1500              : 
    1501              :     /* Set Transaction Description (Owner Name) , Num (invoice ID or type, based
    1502              :      * on book option), Currency */
    1503           74 :     xaccTransSetDescription (txn, name ? name : "");
    1504           74 :     gnc_set_num_action (txn, NULL, gncInvoiceGetID (invoice), type);
    1505           74 :     xaccTransSetCurrency (txn, invoice->currency);
    1506              : 
    1507              :     /* Entered and Posted at date */
    1508           74 :     xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
    1509           74 :     xaccTransSetDatePostedSecsNormalized (txn, post_date);
    1510           74 :     gncInvoiceSetDatePosted (invoice, xaccTransRetDatePosted(txn));
    1511              : 
    1512           74 :     xaccTransSetDateDue (txn, due_date);
    1513              : 
    1514              :     /* Get invoice total and taxes. */
    1515           74 :     total = gncInvoiceGetTotal (invoice);
    1516           74 :     taxes = gncInvoiceGetTotalTaxList (invoice);
    1517              :     /* The two functions above return signs relative to the document
    1518              :      * We need to convert them to balance values before we can use them here
    1519              :      * Note the odd construct comparing two booleans is to xor them
    1520              :      * that is, only evaluate true if both are different.
    1521              :      */
    1522           74 :     if (is_cust_doc != is_cn)
    1523              :     {
    1524              :         GList *node;
    1525           57 :         total = gnc_numeric_neg (total);
    1526           62 :         for (node = taxes; node; node = node->next)
    1527              :         {
    1528            5 :             GncAccountValue *acc_val = node->data;
    1529            5 :             acc_val->value = gnc_numeric_neg (acc_val->value);
    1530              :         }
    1531              :     }
    1532              : 
    1533              :     /* Iterate through the entries; sum up everything for each account.
    1534              :      * then create the appropriate splits in this txn.
    1535              :      */
    1536              : 
    1537          182 :     for (iter = gncInvoiceGetEntries (invoice); iter; iter = iter->next)
    1538              :     {
    1539              :         gnc_numeric value, tax;
    1540          108 :         GncEntry * entry = iter->data;
    1541              :         Account *this_acc;
    1542              : 
    1543              :         /* Stabilize the TaxTable in this entry */
    1544          108 :         gncEntryBeginEdit (entry);
    1545          108 :         if (is_cust_doc)
    1546           88 :             gncEntrySetInvTaxTable
    1547              :             (entry, gncTaxTableReturnChild (gncEntryGetInvTaxTable (entry), TRUE));
    1548              :         else
    1549              :         {
    1550           20 :             gncEntrySetBillTaxTable
    1551              :             (entry, gncTaxTableReturnChild (gncEntryGetBillTaxTable (entry), TRUE));
    1552              : 
    1553              :             /* If this is a bill, and the entry came from an invoice originally, copy the price */
    1554           20 :             if (gncEntryGetBillable (entry))
    1555              :             {
    1556              :                 /* We need to set the net price since it may be another tax rate for invoices than bills */
    1557            0 :                 gncEntrySetInvPrice (entry, gncEntryGetPrice (entry, FALSE, TRUE));
    1558            0 :                 gncEntrySetInvTaxIncluded (entry, FALSE);
    1559              :             }
    1560              :         }
    1561          108 :         gncEntryCommitEdit (entry);
    1562              : 
    1563              :         /* Obtain the Entry's Value and TaxValues
    1564              :            Note we use rounded values here and below to prevent creating an imbalanced transaction */
    1565          108 :         value = gncEntryGetBalValue (entry, TRUE, is_cust_doc);
    1566          108 :         tax   = gncEntryGetBalTaxValue (entry, TRUE, is_cust_doc);
    1567              : 
    1568          108 :         DEBUG ("Tax %" PRId64 "/%" PRId64 " on entry value %" PRId64 "/%" PRId64,
    1569              :                tax.num, tax.denom, value.num, value.denom);
    1570              :         /* add the value for the account split */
    1571          108 :         this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
    1572           20 :                     gncEntryGetBillAccount (entry));
    1573          108 :         if (this_acc)
    1574              :         {
    1575          107 :             if (gnc_numeric_check (value) == GNC_ERROR_OK)
    1576              :             {
    1577          107 :                 if (accumulatesplits)
    1578          107 :                     splitinfo = gncAccountValueAdd (splitinfo, this_acc, value);
    1579              :                     /* Adding to total in case of accumulatesplits will be deferred to later when each split is effectively added */
    1580            0 :                 else if (!gncInvoicePostAddSplit (book, this_acc, txn, value,
    1581            0 :                                                   gncEntryGetDescription (entry),
    1582              :                                                   type, invoice))
    1583              :                 {
    1584              :                     /*This is an error, which shouldn't even be able to happen.
    1585              :                       We can't really do anything sensible about it, and this is
    1586              :                       a user-interface free zone so we can't try asking the user
    1587              :                       again either, have to return NULL*/
    1588            0 :                     PERR("Failed to add split %s", gncEntryGetDescription (entry));
    1589            0 :                     LEAVE ("NULL");
    1590            0 :                     return NULL;
    1591              :                 }
    1592              : 
    1593              :                 /* If there is a credit-card account, and this is a CCard
    1594              :                  * payment type, subtract it from the total, and instead
    1595              :                  * create a split to the CC Acct with a memo of the entry
    1596              :                  * description instead of the provided memo.  Note that the
    1597              :                  * value reversal is the same as the post account.
    1598              :                  *
    1599              :                  * Note: we don't have to worry about the tax values --
    1600              :                  * expense vouchers don't have them.
    1601              :                  */
    1602          107 :                 if (ccard_acct && gncEntryGetBillPayment (entry) == GNC_PAYMENT_CARD)
    1603              :                 {
    1604              :                     Split *split;
    1605              : 
    1606            0 :                     total = gnc_numeric_sub (total, value, denom,
    1607              :                                              GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1608              : 
    1609            0 :                     split = xaccMallocSplit (book);
    1610            0 :                     xaccSplitSetMemo (split, gncEntryGetDescription (entry));
    1611              :                     /* set action based on book option */
    1612            0 :                     gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
    1613            0 :                     xaccAccountBeginEdit (ccard_acct);
    1614            0 :                     xaccAccountInsertSplit (ccard_acct, split);
    1615            0 :                     xaccAccountCommitEdit (ccard_acct);
    1616            0 :                     xaccTransAppendSplit (txn, split);
    1617            0 :                     xaccSplitSetBaseValue (split, gnc_numeric_neg (value),
    1618            0 :                                            invoice->currency);
    1619              : 
    1620              :                 }
    1621              : 
    1622              :             }
    1623              :             else
    1624            0 :                 PWARN ("bad value in our entry");
    1625              :         }
    1626              : 
    1627              :         /* check the taxes */
    1628          108 :         if (gnc_numeric_check (tax) != GNC_ERROR_OK)
    1629            0 :             PWARN ("bad tax in our entry");
    1630              : 
    1631              :     } /* for */
    1632              : 
    1633              : 
    1634              :     /* now merge in the TaxValues */
    1635           74 :     splitinfo = gncAccountValueAddList (splitinfo, taxes);
    1636           74 :     gncAccountValueDestroy (taxes);
    1637              : 
    1638              :     /* Iterate through the splitinfo list and generate the splits */
    1639           74 :     if (splitinfo)
    1640           72 :         PINFO ("Processing Split List");
    1641          151 :     for (iter = splitinfo; iter; iter = iter->next)
    1642              :     {
    1643           77 :         GncAccountValue *acc_val = iter->data;
    1644              : 
    1645              :         //gnc_numeric amt_rounded = gnc_numeric_convert(acc_val->value,
    1646              :         //    denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1647           77 :         if (!gncInvoicePostAddSplit (book, acc_val->account, txn, acc_val->value,
    1648              :                                      memo, type, invoice))
    1649              :         {
    1650              :             /*This is an error, which shouldn't even be able to happen.
    1651              :               We can't really do anything sensible about it, and this is
    1652              :               a user-interface free zone so we can't try asking the user
    1653              :               again either, have to return NULL*/
    1654            0 :             PERR("Failed to add split %s, aborting accumulated splits.", memo);
    1655            0 :             return NULL;
    1656              :         }
    1657              :     }
    1658              : 
    1659              :     /* If there is a ccard account, we may have an additional "to_card" payment.
    1660              :      * we should make that now.
    1661              :      */
    1662           74 :     if (ccard_acct && !gnc_numeric_zero_p (invoice->to_charge_amount))
    1663              :     {
    1664            0 :         Split *split = xaccMallocSplit (book);
    1665              : 
    1666              :         /* To charge amount is stored in document value. We need balance value here
    1667              :          * so convert if necessary. */
    1668            0 :         gnc_numeric to_charge_bal_amount = (is_cn ? gnc_numeric_neg (invoice->to_charge_amount)
    1669            0 :                                             : invoice->to_charge_amount);
    1670              : 
    1671            0 :         PINFO ("Process to_card payment split");
    1672              :         /* Set memo. */
    1673            0 :         xaccSplitSetMemo (split, _("Extra to Charge Card"));
    1674              :         /* Set action based on book option */
    1675            0 :         gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
    1676              : 
    1677            0 :         xaccAccountBeginEdit (ccard_acct);
    1678            0 :         xaccAccountInsertSplit (ccard_acct, split);
    1679            0 :         xaccAccountCommitEdit (ccard_acct);
    1680            0 :         xaccTransAppendSplit (txn, split);
    1681            0 :         xaccSplitSetBaseValue (split, gnc_numeric_neg (to_charge_bal_amount),
    1682            0 :                                invoice->currency);
    1683              : 
    1684            0 :         total = gnc_numeric_sub (total, to_charge_bal_amount, denom,
    1685              :                                  GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
    1686              :     }
    1687              : 
    1688              :     /* Now create the Posted split (which is the opposite sign of the above splits) */
    1689              :     {
    1690           74 :         Split *split = xaccMallocSplit (book);
    1691              : 
    1692           74 :         PINFO ("Process to_card balancing split");
    1693              :         /* Set memo */
    1694           74 :         xaccSplitSetMemo (split, memo);
    1695              :         /* Set action based on book option */
    1696           74 :         gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
    1697              : 
    1698           74 :         xaccAccountBeginEdit (acc);
    1699           74 :         xaccAccountInsertSplit (acc, split);
    1700           74 :         xaccAccountCommitEdit (acc);
    1701           74 :         xaccTransAppendSplit (txn, split);
    1702           74 :         xaccSplitSetBaseValue (split, gnc_numeric_neg (total),
    1703           74 :                                invoice->currency);
    1704              : 
    1705              :         /* add this split to the lot */
    1706           74 :         gnc_lot_add_split (lot, split);
    1707              :     }
    1708              : 
    1709              :     /* Now attach this invoice to the txn and account */
    1710           74 :     gncInvoiceAttachToTxn (invoice, txn);
    1711           74 :     gncInvoiceSetPostedAcc (invoice, acc);
    1712              : 
    1713           74 :     xaccTransSetReadOnly (txn, _("Generated from an invoice. Try unposting the invoice."));
    1714           74 :     xaccTransCommitEdit (txn);
    1715              : 
    1716           74 :     gncAccountValueDestroy (splitinfo);
    1717              : 
    1718           74 :     gnc_lot_commit_edit (lot);
    1719              :     /* Not strictly necessary, since it was done by the Set calls
    1720              :      * above, but good insurance. */
    1721           74 :     DEBUG("Committing Invoice %s", invoice->id);
    1722           74 :     mark_invoice (invoice);
    1723           74 :     gncInvoiceCommitEdit (invoice);
    1724              : 
    1725              :     /* If requested, attempt to automatically apply open payments
    1726              :      * and reverse documents to this lot to close it (or at least
    1727              :      * reduce its balance) */
    1728           74 :     if (autopay)
    1729            0 :         gncInvoiceAutoApplyPayments (invoice);
    1730              : 
    1731           74 :     LEAVE ("");
    1732           74 :     return txn;
    1733              : }
    1734              : 
    1735              : gboolean
    1736            8 : gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables)
    1737              : {
    1738              :     Transaction *txn;
    1739              :     GNCLot *lot;
    1740              :     GList *lot_split_list, *lot_split_iter;
    1741              : 
    1742            8 :     if (!invoice) return FALSE;
    1743            8 :     if (!gncInvoiceIsPosted (invoice)) return FALSE;
    1744              : 
    1745            8 :     txn = gncInvoiceGetPostedTxn (invoice);
    1746            8 :     g_return_val_if_fail (txn, FALSE);
    1747              : 
    1748            8 :     lot = gncInvoiceGetPostedLot (invoice);
    1749            8 :     g_return_val_if_fail (lot, FALSE);
    1750              : 
    1751            8 :     ENTER ("");
    1752              :     /* Destroy the Posted Transaction */
    1753            8 :     xaccTransClearReadOnly (txn);
    1754            8 :     xaccTransBeginEdit (txn);
    1755            8 :     xaccTransDestroy (txn);
    1756            8 :     xaccTransCommitEdit (txn);
    1757              : 
    1758              :     /* Disconnect the lot from the invoice; re-attach to the invoice owner */
    1759            8 :     gncInvoiceDetachFromLot (lot);
    1760            8 :     gncOwnerAttachToLot (&invoice->owner, lot);
    1761              : 
    1762              :     /* Check if this invoice was linked to other lots (payments/inverse signed
    1763              :      * invoices).
    1764              :      * If this is the case, recreate the link transaction between all the remaining lots.
    1765              :      *
    1766              :      * Note that before GnuCash 2.6 payments were not stored in separate lots, but
    1767              :      * always ended up in invoice lots when matched to an invoice. Over-payments
    1768              :      * were copied to a new lot, to which later an invoice was added again and so on.
    1769              :      * These over-payments were handled with automatic payment forward transactions.
    1770              :      * You could consider these transactions to be links between lots as well, but
    1771              :      * to avoid some unexpected behavior, these will not be altered here.
    1772              :      */
    1773              : 
    1774              :     // Note: make a copy of the lot list here, when splits are deleted from the lot,
    1775              :     //       the original list may be destroyed by the lot code.
    1776            8 :     lot_split_list = g_list_copy (gnc_lot_get_split_list (lot));
    1777            8 :     if (lot_split_list)
    1778            0 :         PINFO ("Recreating link transactions for remaining lots");
    1779            8 :     for (lot_split_iter = lot_split_list; lot_split_iter; lot_split_iter = lot_split_iter->next)
    1780              :     {
    1781            0 :         Split *split = lot_split_iter->data;
    1782              :         GList *other_split_list, *list_iter;
    1783            0 :         Transaction *other_txn = xaccSplitGetParent (split);
    1784            0 :         GList *lot_list = NULL;
    1785              : 
    1786              :         /* Only work with transactions that link invoices and payments.
    1787              :          * Note: this check also catches the possible case of NULL splits. */
    1788            0 :         if (xaccTransGetTxnType (other_txn) != TXN_TYPE_LINK)
    1789            0 :             continue;
    1790              : 
    1791              :         /* Save a list of lots this linking transaction linked to */
    1792            0 :         other_split_list = xaccTransGetSplitList (other_txn);
    1793            0 :         for (list_iter = other_split_list; list_iter; list_iter = list_iter->next)
    1794              :         {
    1795            0 :             Split *other_split = list_iter->data;
    1796            0 :             GNCLot *other_lot = xaccSplitGetLot (other_split);
    1797              : 
    1798              :             /* Omit the lot we are about to delete */
    1799            0 :             if (other_lot == lot)
    1800            0 :                 continue;
    1801              : 
    1802            0 :             lot_list = g_list_prepend (lot_list, other_lot);
    1803              :         }
    1804              :         /* Maintain original split order */
    1805            0 :         lot_list = g_list_reverse (lot_list);
    1806              : 
    1807              :         /* Now remove this link transaction. */
    1808            0 :         xaccTransClearReadOnly (other_txn);
    1809            0 :         xaccTransBeginEdit (other_txn);
    1810            0 :         xaccTransDestroy (other_txn);
    1811            0 :         xaccTransCommitEdit (other_txn);
    1812              : 
    1813              :         /* Re-balance the saved lots as well as is possible */
    1814            0 :         gncOwnerAutoApplyPaymentsWithLots (&invoice->owner, lot_list);
    1815              : 
    1816              :         /* If any of the saved lots has no more splits, then destroy it.
    1817              :          * Otherwise if any has an invoice associated with it,
    1818              :          * send it a modified event to reset its paid status */
    1819            0 :         for (list_iter = lot_list; list_iter; list_iter = list_iter->next)
    1820              :         {
    1821            0 :             GNCLot *other_lot = list_iter->data;
    1822            0 :             GncInvoice *other_invoice = gncInvoiceGetInvoiceFromLot (other_lot);
    1823              : 
    1824            0 :             if (!gnc_lot_count_splits (other_lot))
    1825            0 :                 gnc_lot_destroy (other_lot);
    1826            0 :             else if (other_invoice)
    1827            0 :                 qof_event_gen (QOF_INSTANCE(other_invoice), QOF_EVENT_MODIFY, NULL);
    1828              :         }
    1829            0 :         g_list_free (lot_list);
    1830              :     }
    1831            8 :     g_list_free (lot_split_list);
    1832              : 
    1833              :     /* If the lot has no splits, then destroy it */
    1834            8 :     if (!gnc_lot_count_splits (lot))
    1835            8 :         gnc_lot_destroy (lot);
    1836              : 
    1837              :     /* Clear out the invoice posted information */
    1838            8 :     gncInvoiceBeginEdit (invoice);
    1839              : 
    1840            8 :     invoice->posted_acc = NULL;
    1841            8 :     invoice->posted_txn = NULL;
    1842            8 :     invoice->posted_lot = NULL;
    1843            8 :     invoice->date_posted = INT64_MAX;
    1844              : 
    1845              :     /* if we've been asked to reset the tax tables, then do so */
    1846            8 :     if (reset_tax_tables)
    1847              :     {
    1848            8 :         gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
    1849              :         GList *iter;
    1850              : 
    1851           15 :         for (iter = gncInvoiceGetEntries (invoice); iter; iter = iter->next)
    1852              :         {
    1853            7 :             GncEntry *entry = iter->data;
    1854              : 
    1855            7 :             gncEntryBeginEdit (entry);
    1856            7 :             if (is_cust_doc)
    1857            7 :                 gncEntrySetInvTaxTable (entry,
    1858            7 :                                         gncTaxTableGetParent (gncEntryGetInvTaxTable( entry)));
    1859              :             else
    1860            0 :                 gncEntrySetBillTaxTable (entry,
    1861            0 :                                          gncTaxTableGetParent (gncEntryGetBillTaxTable (entry)));
    1862            7 :             gncEntryCommitEdit (entry);
    1863              :         }
    1864              :     }
    1865              : 
    1866            8 :     mark_invoice (invoice);
    1867            8 :     gncInvoiceCommitEdit (invoice);
    1868              : 
    1869            8 :     LEAVE ("TRUE");
    1870              : 
    1871            8 :     return TRUE;
    1872              : }
    1873              : 
    1874              : struct lotmatch
    1875              : {
    1876              :     const GncOwner *owner;
    1877              :     gboolean positive_balance;
    1878              : };
    1879              : 
    1880              : static gboolean
    1881            0 : gnc_lot_match_owner_balancing (GNCLot *lot, gpointer user_data)
    1882              : {
    1883            0 :     struct lotmatch *lm = user_data;
    1884              :     GncOwner owner_def;
    1885              :     const GncOwner *owner;
    1886            0 :     gnc_numeric balance = gnc_lot_get_balance (lot);
    1887              : 
    1888              :     /* Could (part of) this lot serve to balance the lot
    1889              :      * for which this query was run ?*/
    1890            0 :     if (lm->positive_balance == gnc_numeric_positive_p (balance))
    1891            0 :         return FALSE;
    1892              : 
    1893              :     /* Is it ours? Either the lot owner or the lot invoice owner should match */
    1894            0 :     if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
    1895              :     {
    1896            0 :         const GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
    1897            0 :         if (!invoice)
    1898            0 :             return FALSE;
    1899            0 :         owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
    1900              :     }
    1901              :     else
    1902            0 :         owner = gncOwnerGetEndOwner (&owner_def);
    1903              : 
    1904            0 :     return gncOwnerEqual (owner, lm->owner);
    1905              : }
    1906              : 
    1907            1 : void gncInvoiceAutoApplyPayments (GncInvoice *invoice)
    1908              : {
    1909              :     GNCLot *inv_lot;
    1910              :     Account *acct;
    1911              :     const GncOwner *owner;
    1912              :     GList *lot_list;
    1913              :     struct lotmatch lm;
    1914              : 
    1915              :     /* General note: "paying" in this context means balancing
    1916              :      * a lot, by linking opposite signed lots together. So below the term
    1917              :      * "payment" can both mean a true payment or it can mean a document of
    1918              :      * the opposite sign (invoice vs credit note). It just
    1919              :      * depends on what type of document was given as parameter
    1920              :      * to this function. */
    1921              : 
    1922              :     /* Payments can only be applied to posted invoices */
    1923            1 :     g_return_if_fail (invoice);
    1924            1 :     g_return_if_fail (invoice->posted_lot);
    1925              : 
    1926            1 :     inv_lot = invoice->posted_lot;
    1927            1 :     acct = invoice->posted_acc;
    1928            1 :     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
    1929              : 
    1930              :     /* Find all lots whose balance (or part of their balance) could be
    1931              :      * used to close this lot.
    1932              :      * To be eligible, the lots have to have an opposite signed balance
    1933              :      * and be for the same owner.
    1934              :      * For example, for an invoice lot, payment lots and credit note lots
    1935              :      * could be used. */
    1936            1 :     lm.positive_balance =  gnc_numeric_positive_p (gnc_lot_get_balance (inv_lot));
    1937            1 :     lm.owner = owner;
    1938            1 :     lot_list = xaccAccountFindOpenLots (acct, gnc_lot_match_owner_balancing,
    1939              :                                         &lm, NULL);
    1940              : 
    1941            1 :     lot_list = g_list_prepend (lot_list, inv_lot);
    1942            1 :     gncOwnerAutoApplyPaymentsWithLots (owner, lot_list);
    1943            1 :     g_list_free (lot_list);
    1944              : }
    1945              : 
    1946              : /*
    1947              :  * Create a payment of "amount" for the invoice owner and attempt
    1948              :  * to balance it with the given invoice.
    1949              :  */
    1950              : void
    1951           13 : gncInvoiceApplyPayment (const GncInvoice *invoice, Transaction *txn,
    1952              :                         Account *xfer_acc, gnc_numeric amount,
    1953              :                         gnc_numeric exch, time64 date,
    1954              :                         const char *memo, const char *num)
    1955              : {
    1956              :     GNCLot *payment_lot;
    1957           13 :     GList *selected_lots = NULL;
    1958              :     const GncOwner *owner;
    1959              : 
    1960              :     /* Verify our arguments */
    1961           13 :     if (!invoice || !gncInvoiceIsPosted (invoice) || !xfer_acc) return;
    1962              : 
    1963           13 :     owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
    1964           13 :     g_return_if_fail (owner->owner.undefined);
    1965              : 
    1966              :     /* Create a lot for this payment */
    1967           13 :     payment_lot = gncOwnerCreatePaymentLotSecs (owner, &txn,
    1968           13 :                                                 invoice->posted_acc,
    1969              :                                                 xfer_acc, amount, exch,
    1970              :                                                 date, memo, num);
    1971              : 
    1972              :     /* Select the invoice as only payment candidate */
    1973           13 :     selected_lots = g_list_prepend (selected_lots, invoice->posted_lot);
    1974              : 
    1975              :     /* And link the invoice lot and the payment lot together as well as possible. */
    1976           13 :     if (payment_lot)
    1977           13 :         selected_lots = g_list_prepend (selected_lots, payment_lot);
    1978           13 :     gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
    1979              : }
    1980              : 
    1981          126 : gboolean gncInvoiceIsPosted (const GncInvoice *invoice)
    1982              : {
    1983          126 :     if (!invoice) return FALSE;
    1984          126 :     return GNC_IS_TRANSACTION(gncInvoiceGetPostedTxn (invoice));
    1985              : }
    1986              : 
    1987           15 : gboolean gncInvoiceIsPaid (const GncInvoice *invoice)
    1988              : {
    1989           15 :     if (!invoice) return FALSE;
    1990           15 :     if (!invoice->posted_lot) return FALSE;
    1991           15 :     return gnc_lot_is_closed (invoice->posted_lot);
    1992              : }
    1993              : 
    1994              : /* ================================================================== */
    1995              : 
    1996          838 : void gncInvoiceBeginEdit (GncInvoice *invoice)
    1997              : {
    1998          838 :     qof_begin_edit (&invoice->inst);
    1999          838 : }
    2000              : 
    2001            0 : static void gncInvoiceOnError (QofInstance *inst, QofBackendError errcode)
    2002              : {
    2003            0 :     PERR("Invoice QofBackend Failure: %d", errcode);
    2004            0 :     gnc_engine_signal_commit_error (errcode);
    2005            0 : }
    2006              : 
    2007          447 : static void gncInvoiceOnDone (QofInstance *invoice) { }
    2008              : 
    2009           28 : static void invoice_free (QofInstance *inst)
    2010              : {
    2011           28 :     GncInvoice *invoice = (GncInvoice *) inst;
    2012           28 :     gncInvoiceFree (invoice);
    2013           28 : }
    2014              : 
    2015          838 : void gncInvoiceCommitEdit (GncInvoice *invoice)
    2016              : {
    2017          838 :     if (!qof_commit_edit (QOF_INSTANCE(invoice))) return;
    2018          475 :     qof_commit_edit_part2 (&invoice->inst, gncInvoiceOnError,
    2019              :                            gncInvoiceOnDone, invoice_free);
    2020              : }
    2021              : 
    2022            0 : int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b)
    2023              : {
    2024              :     int compare;
    2025              : 
    2026            0 :     if (a == b) return 0;
    2027            0 :     if (!a) return -1;
    2028            0 :     if (!b) return 1;
    2029              : 
    2030            0 :     compare = g_strcmp0 (a->id, b->id);
    2031            0 :     if (compare) return compare;
    2032            0 :     if (a->date_opened != b->date_opened) return a->date_opened - b->date_opened;
    2033            0 :     if (a->date_posted != b->date_posted) return a->date_posted - b->date_posted;
    2034            0 :     return qof_instance_guid_compare(a, b);
    2035              : }
    2036              : 
    2037            0 : gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b)
    2038              : {
    2039            0 :     if (a == NULL && b == NULL) return TRUE;
    2040            0 :     if (a == NULL || b == NULL) return FALSE;
    2041              : 
    2042            0 :     g_return_val_if_fail (GNC_IS_INVOICE(a), FALSE);
    2043            0 :     g_return_val_if_fail (GNC_IS_INVOICE(b), FALSE);
    2044              : 
    2045            0 :     if (g_strcmp0 (a->id, b->id) != 0)
    2046              :     {
    2047            0 :         PWARN("IDs differ: %s vs %s", a->id, b->id);
    2048            0 :         return FALSE;
    2049              :     }
    2050              : 
    2051            0 :     if (g_strcmp0 (a->notes, b->notes) != 0)
    2052              :     {
    2053            0 :         PWARN("Notes differ: %s vs %s", a->notes, b->notes);
    2054            0 :         return FALSE;
    2055              :     }
    2056              : 
    2057            0 :     if (g_strcmp0 (a->billing_id, b->billing_id) != 0)
    2058              :     {
    2059            0 :         PWARN("Billing IDs differ: %s vs %s", a->billing_id, b->billing_id);
    2060            0 :         return FALSE;
    2061              :     }
    2062              : 
    2063            0 :     if (g_strcmp0 (a->printname, b->printname) != 0)
    2064              :     {
    2065            0 :         PWARN("Printnames differ: %s vs %s", a->printname, b->printname);
    2066            0 :         return FALSE;
    2067              :     }
    2068              : 
    2069            0 :     if (a->active != b->active)
    2070              :     {
    2071            0 :         PWARN("Active flags differ");
    2072            0 :         return FALSE;
    2073              :     }
    2074              : 
    2075            0 :     if (!gncBillTermEqual (a->terms, b->terms))
    2076              :     {
    2077            0 :         PWARN("Billterms differ");
    2078            0 :         return FALSE;
    2079              :     }
    2080              : 
    2081            0 :     if (!gncJobEqual (a->job, b->job))
    2082              :     {
    2083            0 :         PWARN("Jobs differ");
    2084            0 :         return FALSE;
    2085              :     }
    2086              : 
    2087            0 :     if (!gnc_commodity_equal (a->currency, b->currency))
    2088              :     {
    2089            0 :         PWARN("Currencies differ");
    2090            0 :         return FALSE;
    2091              :     }
    2092              : 
    2093            0 :     if (!xaccAccountEqual (a->posted_acc, b->posted_acc, TRUE))
    2094              :     {
    2095            0 :         PWARN("Posted accounts differ");
    2096            0 :         return FALSE;
    2097              :     }
    2098              : 
    2099            0 :     if (!xaccTransEqual (a->posted_txn, b->posted_txn, TRUE, TRUE, TRUE, FALSE))
    2100              :     {
    2101            0 :         PWARN("Posted tx differ");
    2102            0 :         return FALSE;
    2103              :     }
    2104              : 
    2105              : #if 0
    2106              :     if (!gncLotEqual (a->posted_lot, b->posted_lot))
    2107              :     {
    2108              :         PWARN("Posted lots differ");
    2109              :         return FALSE;
    2110              :     }
    2111              : #endif
    2112              : 
    2113              :     /* FIXME: Need real checks */
    2114              : #if 0
    2115              :     GList       *entries;
    2116              :     GList       *prices;
    2117              :     GncOwner    owner;
    2118              :     GncOwner    billto;
    2119              :     time64      date_opened;
    2120              :     time64      date_posted;
    2121              : 
    2122              :     gnc_numeric to_charge_amount;
    2123              : #endif
    2124              : 
    2125            0 :     return TRUE;
    2126              : }
    2127              : 
    2128              : /* ============================================================= */
    2129              : /* Package-Private functions */
    2130              : 
    2131            0 : static const char * _gncInvoicePrintable (gpointer obj)
    2132              : {
    2133            0 :     GncInvoice *invoice = obj;
    2134              : 
    2135            0 :     g_return_val_if_fail (invoice, NULL);
    2136              : 
    2137            0 :     if (qof_instance_get_dirty_flag (invoice) || invoice->printname == NULL)
    2138              :     {
    2139            0 :         if (invoice->printname) g_free (invoice->printname);
    2140              : 
    2141            0 :         invoice->printname =
    2142            0 :             g_strdup_printf ("%s%s", invoice->id,
    2143            0 :                              gncInvoiceIsPosted (invoice) ? _(" (posted)") : "");
    2144              :     }
    2145              : 
    2146            0 :     return invoice->printname;
    2147              : }
    2148              : 
    2149              : static void
    2150           14 : destroy_invoice_on_book_close (QofInstance *ent, gpointer data)
    2151              : {
    2152           14 :     GncInvoice* invoice = GNC_INVOICE(ent);
    2153              : 
    2154           14 :     gncInvoiceBeginEdit (invoice);
    2155           14 :     gncInvoiceDestroy (invoice);
    2156           14 : }
    2157              : 
    2158              : static void
    2159          182 : gnc_invoice_book_end (QofBook* book)
    2160              : {
    2161              :     QofCollection *col;
    2162              : 
    2163          182 :     col = qof_book_get_collection (book, GNC_ID_INVOICE);
    2164          182 :     qof_collection_foreach (col, destroy_invoice_on_book_close, NULL);
    2165          182 : }
    2166              : 
    2167              : static QofObject gncInvoiceDesc =
    2168              : {
    2169              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2170              :     DI(.e_type            = ) _GNC_MOD_NAME,
    2171              :     DI(.type_label        = ) "Invoice",
    2172              :     DI(.create            = ) (gpointer)gncInvoiceCreate,
    2173              :     DI(.book_begin        = ) NULL,
    2174              :     DI(.book_end          = ) gnc_invoice_book_end,
    2175              :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2176              :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2177              :     DI(.foreach           = ) qof_collection_foreach,
    2178              :     DI(.printable         = ) _gncInvoicePrintable,
    2179              :     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
    2180              : };
    2181              : 
    2182              : static void
    2183           86 : reg_lot (void)
    2184              : {
    2185              :     static QofParam params[] =
    2186              :     {
    2187              :         {
    2188              :             INVOICE_FROM_LOT, _GNC_MOD_NAME,
    2189              :             (QofAccessFunc)gncInvoiceGetInvoiceFromLot, NULL
    2190              :         },
    2191              :         { NULL },
    2192              :     };
    2193              : 
    2194           86 :     qof_class_register (GNC_ID_LOT, NULL, params);
    2195           86 : }
    2196              : 
    2197              : static void
    2198           86 : reg_txn (void)
    2199              : {
    2200              :     static QofParam params[] =
    2201              :     {
    2202              :         {
    2203              :             INVOICE_FROM_TXN, _GNC_MOD_NAME,
    2204              :             (QofAccessFunc)gncInvoiceGetInvoiceFromTxn, NULL
    2205              :         },
    2206              :         { NULL },
    2207              :     };
    2208              : 
    2209           86 :     qof_class_register (GNC_ID_TRANS, NULL, params);
    2210           86 : }
    2211              : 
    2212           86 : gboolean gncInvoiceRegister (void)
    2213              : {
    2214              :     static QofParam params[] =
    2215              :     {
    2216              :         { INVOICE_ID,        QOF_TYPE_NATURAL, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID },
    2217              :         { INVOICE_OWNER,     GNC_ID_OWNER,     (QofAccessFunc)gncInvoiceGetOwner, NULL },
    2218              :         { INVOICE_OPENED,    QOF_TYPE_DATE,    (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened },
    2219              :         { INVOICE_DUE,       QOF_TYPE_DATE,    (QofAccessFunc)gncInvoiceGetDateDue, NULL },
    2220              :         { INVOICE_POSTED,    QOF_TYPE_DATE,    (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted },
    2221              :         { INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL },
    2222              :         { INVOICE_IS_PAID,   QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid,    NULL },
    2223              :         { INVOICE_BILLINGID, QOF_TYPE_NATURAL, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
    2224              :         { INVOICE_NOTES,     QOF_TYPE_STRING,  (QofAccessFunc)gncInvoiceGetNotes,   (QofSetterFunc)gncInvoiceSetNotes },
    2225              :         { INVOICE_DOCLINK, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetDocLink, (QofSetterFunc)gncInvoiceSetDocLink },
    2226              :         { INVOICE_ACC,       GNC_ID_ACCOUNT,   (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc },
    2227              :         { INVOICE_POST_TXN,  GNC_ID_TRANS,     (QofAccessFunc)gncInvoiceGetPostedTxn, (QofSetterFunc)gncInvoiceSetPostedTxn },
    2228              :         { INVOICE_POST_LOT,  GNC_ID_LOT,       (QofAccessFunc)gncInvoiceGetPostedLot, NULL/*(QofSetterFunc)gncInvoiceSetPostedLot*/ },
    2229              :         { INVOICE_TYPE,      QOF_TYPE_INT32,   (QofAccessFunc)gncInvoiceGetType,    NULL },
    2230              :         { INVOICE_TYPE_STRING, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetTypeString,    NULL },
    2231              :         { INVOICE_TERMS,     GNC_ID_BILLTERM,  (QofAccessFunc)gncInvoiceGetTerms,   (QofSetterFunc)gncInvoiceSetTerms },
    2232              :         { INVOICE_BILLTO,    GNC_ID_OWNER,     (QofAccessFunc)gncInvoiceGetBillTo, NULL  },
    2233              :         { INVOICE_JOB,       GNC_ID_JOB,       (QofAccessFunc)qofInvoiceGetJob,     (QofSetterFunc)qofInvoiceSetJob },
    2234              :         { QOF_PARAM_ACTIVE,  QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, (QofSetterFunc)gncInvoiceSetActive },
    2235              :         { INVOICE_IS_CN,     QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetIsCreditNote, (QofSetterFunc)gncInvoiceSetIsCreditNote },
    2236              :         { QOF_PARAM_BOOK,    QOF_ID_BOOK,      (QofAccessFunc)qof_instance_get_book, NULL },
    2237              :         { QOF_PARAM_GUID,    QOF_TYPE_GUID,    (QofAccessFunc)qof_instance_get_guid, NULL },
    2238              :         { NULL },
    2239              :     };
    2240              : 
    2241           86 :     qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncInvoiceCompare, params);
    2242           86 :     reg_lot ();
    2243           86 :     reg_txn ();
    2244              : 
    2245              :     /* Make the compiler happy... */
    2246              :     if (0)
    2247              :     {
    2248              :         qofInvoiceSetOwner (NULL, NULL);
    2249              :         qofInvoiceGetOwner (NULL);
    2250              :         qofInvoiceSetBillTo (NULL, NULL);
    2251              :         qofInvoiceGetBillTo (NULL);
    2252              :     }
    2253           86 :     return qof_object_register (&gncInvoiceDesc);
    2254              : }
    2255              : 
    2256            0 : gchar *gncInvoiceNextID (QofBook *book, const GncOwner *owner)
    2257              : {
    2258              :     gchar *nextID;
    2259            0 :     switch (gncOwnerGetType (gncOwnerGetEndOwner (owner)))
    2260              :     {
    2261            0 :     case GNC_OWNER_CUSTOMER:
    2262            0 :         nextID = qof_book_increment_and_format_counter (book, "gncInvoice");
    2263            0 :         break;
    2264            0 :     case GNC_OWNER_VENDOR:
    2265            0 :         nextID = qof_book_increment_and_format_counter (book, "gncBill");
    2266            0 :         break;
    2267            0 :     case GNC_OWNER_EMPLOYEE:
    2268            0 :         nextID = qof_book_increment_and_format_counter (book, "gncExpVoucher");
    2269            0 :         break;
    2270            0 :     default:
    2271            0 :         nextID = qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
    2272            0 :         break;
    2273              :     }
    2274            0 :     return nextID;
    2275              : }
        

Generated by: LCOV version 2.0-1