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