GnuCash c935c2f+
Loading...
Searching...
No Matches
gncInvoice.c
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
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
78{
79 QofInstanceClass parent_class;
80};
81
82static 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
95static void mark_invoice (GncInvoice *invoice);
96static void
97mark_invoice (GncInvoice *invoice)
98{
99 qof_instance_set_dirty (&invoice->inst);
100 qof_event_gen (&invoice->inst, QOF_EVENT_MODIFY, NULL);
101}
102
103QofBook * gncInvoiceGetBook (GncInvoice *x)
104{
105 return qof_instance_get_book (QOF_INSTANCE(x));
106}
107
108/* ================================================================== */
109
110enum
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 */
132G_DEFINE_TYPE(GncInvoice, gnc_invoice, QOF_TYPE_INSTANCE)
133
134static void
135gnc_invoice_init (GncInvoice* inv)
136{
137 inv->date_posted = INT64_MAX;
138 inv->date_opened = INT64_MAX;
139}
140
141static void
142gnc_invoice_dispose (GObject *invp)
143{
144 G_OBJECT_CLASS(gnc_invoice_parent_class)->dispose(invp);
145}
146
147static void
148gnc_invoice_finalize (GObject* invp)
149{
150 G_OBJECT_CLASS(gnc_invoice_parent_class)->finalize(invp);
151}
152
153static void
154gnc_invoice_get_property (GObject *object,
155 guint prop_id,
156 GValue *value,
157 GParamSpec *pspec)
158{
159 GncInvoice *inv;
160
161 g_return_if_fail (GNC_IS_INVOICE(object));
162
163 inv = GNC_INVOICE(object);
164 switch (prop_id)
165 {
166 case PROP_NOTES:
167 g_value_set_string (value, inv->notes);
168 break;
169 default:
170 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
171 break;
172 }
173}
174
175static void
176gnc_invoice_set_property (GObject *object,
177 guint prop_id,
178 const GValue *value,
179 GParamSpec *pspec)
180{
181 GncInvoice *inv;
182
183 g_return_if_fail (GNC_IS_INVOICE(object));
184
185 inv = GNC_INVOICE(object);
186 g_assert (qof_instance_get_editlevel (inv));
187
188 switch (prop_id)
189 {
190 case PROP_NOTES:
191 gncInvoiceSetNotes (inv, g_value_get_string (value));
192 break;
193 default:
194 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
195 break;
196 }
197}
198
200static gchar*
201impl_get_display_name (const QofInstance* inst)
202{
203 GncInvoice* inv;
204 QofInstance* owner;
205 gchar* s;
206
207 g_return_val_if_fail (inst != NULL, FALSE);
208 g_return_val_if_fail (GNC_IS_INVOICE(inst), FALSE);
209
210 inv = GNC_INVOICE(inst);
211 owner = qofOwnerGetOwner (&inv->owner);
212 if (owner != NULL)
213 {
214 gchar* display_name;
215
216 display_name = qof_instance_get_display_name (owner);
217 s = g_strdup_printf ("Invoice %s (%s)", inv->id, display_name);
218 g_free (display_name);
219 }
220 else
221 {
222 s = g_strdup_printf ("Invoice %s", inv->id);
223 }
224
225 return s;
226}
227
229static gboolean
230impl_refers_to_object (const QofInstance* inst, const QofInstance* ref)
231{
232 GncInvoice* inv;
233
234 g_return_val_if_fail (inst != NULL, FALSE);
235 g_return_val_if_fail (GNC_IS_INVOICE(inst), FALSE);
236
237 inv = GNC_INVOICE(inst);
238
239 if (GNC_IS_BILLTERM(ref))
240 {
241 return (inv->terms == GNC_BILLTERM(ref));
242 }
243 else if (GNC_IS_JOB(ref))
244 {
245 return (inv->job == GNC_JOB(ref));
246 }
247 else if (GNC_IS_COMMODITY(ref))
248 {
249 return (inv->currency == GNC_COMMODITY(ref));
250 }
251 else if (GNC_IS_ACCOUNT(ref))
252 {
253 return (inv->posted_acc == GNC_ACCOUNT(ref));
254 }
255 else if (GNC_IS_TRANSACTION(ref))
256 {
257 return (inv->posted_txn == GNC_TRANSACTION(ref));
258 }
259 else if (GNC_IS_LOT(ref))
260 {
261 return (inv->posted_lot == GNC_LOT(ref));
262 }
263
264 return FALSE;
265}
266
273static GList*
274impl_get_typed_referring_object_list (const QofInstance* inst, const QofInstance* ref)
275{
276 if (!GNC_IS_BILLTERM(ref) && !GNC_IS_JOB(ref) && !GNC_IS_COMMODITY(ref) && !GNC_IS_ACCOUNT(ref)
277 && !GNC_IS_TRANSACTION(ref) && !GNC_IS_LOT(ref))
278 {
279 return NULL;
280 }
281
283}
284
285static void
286gnc_invoice_class_init (GncInvoiceClass *klass)
287{
288 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
289 QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
290
291 gobject_class->dispose = gnc_invoice_dispose;
292 gobject_class->finalize = gnc_invoice_finalize;
293 gobject_class->set_property = gnc_invoice_set_property;
294 gobject_class->get_property = gnc_invoice_get_property;
295
296 qof_class->get_display_name = impl_get_display_name;
297 qof_class->refers_to_object = impl_refers_to_object;
298 qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
299
300 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}
311
312/* Create/Destroy Functions */
313GncInvoice *gncInvoiceCreate (QofBook *book)
314{
315 GncInvoice *invoice;
316
317 if (!book) return NULL;
318
319 invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
320 qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
321
322 invoice->id = CACHE_INSERT ("");
323 invoice->notes = CACHE_INSERT ("");
324 invoice->billing_id = CACHE_INSERT ("");
325
326 invoice->billto.type = GNC_OWNER_CUSTOMER;
327 invoice->active = TRUE;
328
329 invoice->to_charge_amount = gnc_numeric_zero ();
330
331 qof_event_gen (&invoice->inst, QOF_EVENT_CREATE, NULL);
332
333 return invoice;
334}
335
336GncInvoice *gncInvoiceCopy (const GncInvoice *from)
337{
338 GncInvoice *invoice;
339 QofBook* book;
340 GList *node;
341 GValue v = G_VALUE_INIT;
342
343 g_assert (from);
344 book = qof_instance_get_book (from);
345 g_assert (book);
346
347 invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
348 qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
349
350 gncInvoiceBeginEdit (invoice);
351
352 invoice->id = CACHE_INSERT (from->id);
353 invoice->notes = CACHE_INSERT (from->notes);
354 invoice->billing_id = CACHE_INSERT (from->billing_id);
355 invoice->active = from->active;
356
357 qof_instance_get_kvp (QOF_INSTANCE (from), &v, 1, GNC_INVOICE_IS_CN);
358 if (G_VALUE_HOLDS_INT64 (&v))
359 qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
360 g_value_unset (&v);
361
362 invoice->terms = from->terms;
363 gncBillTermIncRef (invoice->terms);
364
365 gncOwnerCopy (&from->billto, &invoice->billto);
366 gncOwnerCopy (&from->owner, &invoice->owner);
367 invoice->job = from->job; // FIXME: Need IncRef or similar here?!?
368
369 invoice->to_charge_amount = from->to_charge_amount;
370 invoice->date_opened = from->date_opened;
371
372 // Oops. Do not forget to copy the pointer to the correct currency here.
373 invoice->currency = from->currency;
374
375 gncInvoiceSetDocLink (invoice, gncInvoiceGetDocLink (from));
376
377 // Copy all invoice->entries
378 for (node = from->entries; node; node = node->next)
379 {
380 GncEntry *from_entry = node->data;
381 GncEntry *to_entry = gncEntryCreate (book);
382 gncEntryCopy (from_entry, to_entry, FALSE);
383
384 switch (gncInvoiceGetOwnerType (invoice))
385 {
386 case GNC_OWNER_VENDOR:
387 case GNC_OWNER_EMPLOYEE:
388 // this is a vendor bill, or an expense voucher
389 gncBillAddEntry (invoice, to_entry);
390 break;
391 case GNC_OWNER_CUSTOMER:
392 default:
393 // this is an invoice
394 gncInvoiceAddEntry (invoice, to_entry);
395 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 mark_invoice (invoice);
404 gncInvoiceCommitEdit (invoice);
405
406 return invoice;
407}
408
409void gncInvoiceDestroy (GncInvoice *invoice)
410{
411 if (!invoice) return;
412 qof_instance_set_destroying (invoice, TRUE);
413 gncInvoiceCommitEdit (invoice);
414}
415
416static void gncInvoiceFree (GncInvoice *invoice)
417{
418 if (!invoice) return;
419
420 qof_event_gen (&invoice->inst, QOF_EVENT_DESTROY, NULL);
421
422 CACHE_REMOVE (invoice->id);
423 CACHE_REMOVE (invoice->notes);
424 CACHE_REMOVE (invoice->billing_id);
425 g_list_free (invoice->entries);
426 g_list_free (invoice->prices);
427
428 if (invoice->printname)
429 g_free (invoice->printname);
430
431 if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(invoice))))
432 {
433 if (invoice->terms)
434 gncBillTermDecRef (invoice->terms);
435 }
436
437 /* qof_instance_release (&invoice->inst); */
438 g_object_unref (invoice);
439}
440
441/* ================================================================== */
442/* Set Functions */
443
444void gncInvoiceSetID (GncInvoice *invoice, const char *id)
445{
446 if (!invoice || !id) return;
447 SET_STR (invoice, invoice->id, id);
448 mark_invoice (invoice);
449 gncInvoiceCommitEdit (invoice);
450}
451
452void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner)
453{
454 if (!invoice || !owner) return;
455 if (gncOwnerEqual (&invoice->owner, owner)) return;
456 gncInvoiceBeginEdit (invoice);
457 gncOwnerCopy (owner, &invoice->owner);
458 mark_invoice (invoice);
459 gncInvoiceCommitEdit (invoice);
460}
461
462static void
463qofInvoiceSetOwner (GncInvoice *invoice, QofInstance *ent)
464{
465 if (!invoice || !ent)
466 {
467 return;
468 }
469 gncInvoiceBeginEdit (invoice);
470 qofOwnerSetEntity (&invoice->owner, ent);
471 mark_invoice (invoice);
472 gncInvoiceCommitEdit (invoice);
473}
474
475static void
476qofInvoiceSetBillTo (GncInvoice *invoice, QofInstance *ent)
477{
478 if (!invoice || !ent)
479 {
480 return;
481 }
482 gncInvoiceBeginEdit (invoice);
483 qofOwnerSetEntity (&invoice->billto, ent);
484 mark_invoice (invoice);
485 gncInvoiceCommitEdit (invoice);
486}
487
488void gncInvoiceSetDateOpenedGDate (GncInvoice *invoice, const GDate *date)
489{
490 g_assert (date);
491 gncInvoiceSetDateOpened(invoice, time64CanonicalDayTime (gdate_to_time64 (*date)));
492}
493
494void gncInvoiceSetDateOpened (GncInvoice *invoice, time64 date)
495{
496 if (!invoice) return;
497 if (date == invoice->date_opened) return;
498 gncInvoiceBeginEdit (invoice);
499 invoice->date_opened = date;
500 mark_invoice (invoice);
501 gncInvoiceCommitEdit (invoice);
502}
503
504void gncInvoiceSetDatePosted (GncInvoice *invoice, time64 date)
505{
506 if (!invoice) return;
507 if (date == invoice->date_posted) return;
508 gncInvoiceBeginEdit (invoice);
509 invoice->date_posted = date;
510 mark_invoice (invoice);
511 gncInvoiceCommitEdit (invoice);
512}
513
514void gncInvoiceSetTerms (GncInvoice *invoice, GncBillTerm *terms)
515{
516 if (!invoice) return;
517 if (invoice->terms == terms) return;
518 gncInvoiceBeginEdit (invoice);
519 if (invoice->terms)
520 gncBillTermDecRef (invoice->terms);
521 invoice->terms = terms;
522 if (invoice->terms)
523 gncBillTermIncRef (invoice->terms);
524 mark_invoice (invoice);
525 gncInvoiceCommitEdit (invoice);
526}
527
528void gncInvoiceSetBillingID (GncInvoice *invoice, const char *billing_id)
529{
530 if (!invoice) return;
531 SET_STR (invoice, invoice->billing_id, billing_id);
532 mark_invoice (invoice);
533 gncInvoiceCommitEdit (invoice);
534}
535
536void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes)
537{
538 if (!invoice || !notes) return;
539 SET_STR (invoice, invoice->notes, notes);
540 mark_invoice (invoice);
541 gncInvoiceCommitEdit (invoice);
542}
543
544void gncInvoiceSetDocLink (GncInvoice *invoice, const char *doclink)
545{
546 if (!invoice || !doclink) return;
547
548 gncInvoiceBeginEdit (invoice);
549
550 if (doclink[0] == '\0')
551 {
552 qof_instance_set_kvp (QOF_INSTANCE (invoice), NULL, 1, GNC_INVOICE_DOCLINK);
553 }
554 else
555 {
556 GValue v = G_VALUE_INIT;
557 g_value_init (&v, G_TYPE_STRING);
558 g_value_set_static_string (&v, doclink);
559 qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_DOCLINK);
560 g_value_unset (&v);
561 }
562 qof_instance_set_dirty (QOF_INSTANCE(invoice));
563 gncInvoiceCommitEdit (invoice);
564}
565
566void gncInvoiceSetActive (GncInvoice *invoice, gboolean active)
567{
568 if (!invoice) return;
569 if (invoice->active == active) return;
570 gncInvoiceBeginEdit (invoice);
571 invoice->active = active;
572 mark_invoice (invoice);
573 gncInvoiceCommitEdit (invoice);
574}
575
576void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note)
577{
578 GValue v = G_VALUE_INIT;
579 if (!invoice) return;
580 gncInvoiceBeginEdit (invoice);
581 g_value_init (&v, G_TYPE_INT64);
582 g_value_set_int64 (&v, credit_note ? 1 : 0);
583 qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
584 g_value_unset (&v);
585 mark_invoice (invoice);
586 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 if (credit_note)
592 gnc_features_set_used (gncInvoiceGetBook (invoice), GNC_FEATURE_CREDIT_NOTES);
593}
594
595void gncInvoiceSetCurrency (GncInvoice *invoice, gnc_commodity *currency)
596{
597 if (!invoice || !currency) return;
598 if (invoice->currency &&
599 gnc_commodity_equal (invoice->currency, currency))
600 return;
601 gncInvoiceBeginEdit (invoice);
602 invoice->currency = currency;
603 mark_invoice (invoice);
604 gncInvoiceCommitEdit (invoice);
605}
606
607void gncInvoiceSetBillTo (GncInvoice *invoice, GncOwner *billto)
608{
609 if (!invoice || !billto) return;
610 if (gncOwnerEqual (&invoice->billto, billto)) return;
611
612 gncInvoiceBeginEdit (invoice);
613 gncOwnerCopy (billto, &invoice->billto);
614 mark_invoice (invoice);
615 gncInvoiceCommitEdit (invoice);
616}
617
618void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount)
619{
620 if (!invoice) return;
621 if (gnc_numeric_equal (invoice->to_charge_amount, amount)) return;
622 gncInvoiceBeginEdit (invoice);
623 invoice->to_charge_amount = amount;
624 mark_invoice (invoice);
625 gncInvoiceCommitEdit (invoice);
626}
627
628void gncInvoiceSetPostedTxn (GncInvoice *invoice, Transaction *txn)
629{
630 if (!invoice) return;
631 g_return_if_fail (invoice->posted_txn == NULL);
632
633 gncInvoiceBeginEdit (invoice);
634 invoice->posted_txn = txn;
635 mark_invoice (invoice);
636 gncInvoiceCommitEdit (invoice);
637}
638
639void gncInvoiceSetPostedLot (GncInvoice *invoice, GNCLot *lot)
640{
641 if (!invoice) return;
642 g_return_if_fail (invoice->posted_lot == NULL);
643
644 gncInvoiceBeginEdit (invoice);
645 invoice->posted_lot = lot;
646 mark_invoice (invoice);
647 gncInvoiceCommitEdit (invoice);
648}
649
650void gncInvoiceSetPostedAcc (GncInvoice *invoice, Account *acc)
651{
652 if (!invoice) return;
653 g_return_if_fail (invoice->posted_acc == NULL);
654
655 gncInvoiceBeginEdit (invoice);
656 invoice->posted_acc = acc;
657 mark_invoice (invoice);
658 gncInvoiceCommitEdit (invoice);
659}
660
661void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry)
662{
663 GncInvoice *old;
664
665 g_assert (invoice);
666 g_assert (entry);
667 if (!invoice || !entry) return;
668
669 old = gncEntryGetInvoice (entry);
670 if (old == invoice) return; /* I already own this one */
671 if (old) gncInvoiceRemoveEntry (old, entry);
672
673 gncInvoiceBeginEdit (invoice);
674 gncEntrySetInvoice (entry, invoice);
675 invoice->entries = g_list_insert_sorted (invoice->entries, entry,
676 (GCompareFunc)gncEntryCompare);
677 mark_invoice (invoice);
678 gncInvoiceCommitEdit (invoice);
679}
680
681void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry)
682{
683 if (!invoice || !entry) return;
684
685 gncInvoiceBeginEdit (invoice);
686 gncEntrySetInvoice (entry, NULL);
687 invoice->entries = g_list_remove (invoice->entries, entry);
688 mark_invoice (invoice);
689 gncInvoiceCommitEdit (invoice);
690}
691
692void gncInvoiceAddPrice (GncInvoice *invoice, GNCPrice *price)
693{
694 GList *node;
695 gnc_commodity *commodity;
696
697 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 node = g_list_first (invoice->prices);
702 commodity = gnc_price_get_commodity (price);
703 while (node != NULL)
704 {
705 GNCPrice *curr = (GNCPrice*)node->data;
706 if (gnc_commodity_equal (commodity, gnc_price_get_commodity (curr)))
707 break;
708 node = g_list_next (node);
709 }
710
711 gncInvoiceBeginEdit (invoice);
712 if (node)
713 invoice->prices = g_list_delete_link (invoice->prices, node);
714 invoice->prices = g_list_prepend (invoice->prices, price);
715 mark_invoice (invoice);
716 gncInvoiceCommitEdit (invoice);
717}
718
719void gncBillAddEntry (GncInvoice *bill, GncEntry *entry)
720{
721 GncInvoice *old;
722
723 g_assert (bill);
724 g_assert (entry);
725 if (!bill || !entry) return;
726
727 old = gncEntryGetBill (entry);
728 if (old == bill) return; /* I already own this one */
729 if (old) gncBillRemoveEntry (old, entry);
730
731 gncInvoiceBeginEdit (bill);
732 gncEntrySetBill (entry, bill);
733 bill->entries = g_list_insert_sorted (bill->entries, entry,
734 (GCompareFunc)gncEntryCompare);
735 mark_invoice (bill);
736 gncInvoiceCommitEdit (bill);
737}
738
739void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry)
740{
741 if (!bill || !entry) return;
742
743 gncInvoiceBeginEdit (bill);
744 gncEntrySetBill (entry, NULL);
745 bill->entries = g_list_remove (bill->entries, entry);
746 mark_invoice (bill);
747 gncInvoiceCommitEdit (bill);
748}
749
750void gncInvoiceSortEntries (GncInvoice *invoice)
751{
752 if (!invoice) return;
753 invoice->entries = g_list_sort (invoice->entries,
754 (GCompareFunc)gncEntryCompare);
755 gncInvoiceBeginEdit (invoice);
756 mark_invoice (invoice);
757 gncInvoiceCommitEdit (invoice);
758}
759
760void gncInvoiceRemoveEntries (GncInvoice *invoice)
761{
762 if (!invoice) return;
763
764 // gnc{Bill,Invoice}RemoveEntry free the "entry" node.
765 // Make sure to save "next" first.
766 for (GList *next, *node = invoice->entries; node; node = next)
767 {
768 next = node->next;
769 GncEntry *entry = node->data;
770
771 switch (gncInvoiceGetOwnerType (invoice))
772 {
773 case GNC_OWNER_VENDOR:
774 case GNC_OWNER_EMPLOYEE:
775 // this is a vendor bill, or an expense voucher
776 gncBillRemoveEntry (invoice, entry);
777 break;
778 case GNC_OWNER_CUSTOMER:
779 default:
780 // this is an invoice
781 gncInvoiceRemoveEntry (invoice, entry);
782 break;
783 }
784
785 /* If the entry is no longer referenced by any document,
786 * remove it.
787 */
788 if (!(gncEntryGetInvoice (entry) ||
789 gncEntryGetBill (entry) ||
790 gncEntryGetOrder (entry)))
791 {
792 gncEntryBeginEdit (entry);
793 gncEntryDestroy (entry);
794 }
795 }
796}
797
798/* ================================================================== */
799/* Get Functions */
800
801const char * gncInvoiceGetID (const GncInvoice *invoice)
802{
803 if (!invoice) return NULL;
804 return invoice->id;
805}
806
807const GncOwner * gncInvoiceGetOwner (const GncInvoice *invoice)
808{
809 if (!invoice) return NULL;
810 return &invoice->owner;
811}
812
813static QofInstance * qofInvoiceGetOwner (GncInvoice *invoice)
814{
815 GncOwner *owner;
816
817 if (!invoice)
818 {
819 return NULL;
820 }
821 owner = &invoice->owner;
822 return QOF_INSTANCE(owner);
823}
824
825static QofInstance * qofInvoiceGetBillTo (GncInvoice *invoice)
826{
827 GncOwner *billto;
828
829 if (!invoice)
830 {
831 return NULL;
832 }
833 billto = &invoice->billto;
834 return QOF_INSTANCE(billto);
835}
836
837time64 gncInvoiceGetDateOpened (const GncInvoice *invoice)
838{
839 if (!invoice) return INT64_MAX;
840 return invoice->date_opened;
841}
842
843time64 gncInvoiceGetDatePosted (const GncInvoice *invoice)
844{
845 if (!invoice) return INT64_MAX;
846 return invoice->date_posted;
847}
848
849time64 gncInvoiceGetDateDue (const GncInvoice *invoice)
850{
851 Transaction *txn;
852 if (!invoice) return INT64_MAX;
853 txn = gncInvoiceGetPostedTxn (invoice);
854 if (!txn) return INT64_MAX;
855 return xaccTransRetDateDue (txn);
856}
857
858GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice)
859{
860 if (!invoice) return NULL;
861 return invoice->terms;
862}
863
864const char * gncInvoiceGetBillingID (const GncInvoice *invoice)
865{
866 if (!invoice) return NULL;
867 return invoice->billing_id;
868}
869
870const char * gncInvoiceGetNotes (const GncInvoice *invoice)
871{
872 if (!invoice) return NULL;
873 return invoice->notes;
874}
875
876const char * gncInvoiceGetDocLink (const GncInvoice *invoice)
877{
878 if (!invoice) return NULL;
879
880 GValue v = G_VALUE_INIT;
881 qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_DOCLINK);
882 const char *rv = G_VALUE_HOLDS_STRING(&v) ? g_value_get_string (&v) : NULL;
883 g_value_unset (&v);
884
885 return rv;
886}
887
888GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice)
889{
890 const GncOwner *owner;
891 g_return_val_if_fail (invoice, GNC_OWNER_NONE);
892
893 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
894 return (gncOwnerGetType (owner));
895}
896
897static gnc_numeric gncInvoiceSumTaxesInternal (AccountValueList *taxes)
898{
899 gnc_numeric tt = gnc_numeric_zero ();
900
901 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 for (node = taxes; node; node=node->next)
908 {
909 GncAccountValue *acc_val = node->data;
910 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 return tt;
915}
916
917static gnc_numeric gncInvoiceGetNetAndTaxesInternal (GncInvoice *invoice, gboolean use_value,
918 AccountValueList **taxes,
919 gboolean use_payment_type,
920 GncEntryPaymentType type)
921{
922 GList *node;
923 gnc_numeric net_total = gnc_numeric_zero ();
924 gboolean is_cust_doc, is_cn;
925 AccountValueList *tv_list = NULL;
926 int denom = gnc_commodity_get_fraction (gncInvoiceGetCurrency (invoice));
927
928 g_return_val_if_fail (invoice, net_total);
929
930 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 is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
935 is_cn = gncInvoiceGetIsCreditNote (invoice);
936
937
938 for (node = gncInvoiceGetEntries (invoice); node; node = node->next)
939 {
940 GncEntry *entry = node->data;
941 gnc_numeric value;
942
943 if (use_payment_type && gncEntryGetBillPayment (entry) != type)
944 continue;
945
946 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 value = gncEntryGetDocValue (entry, TRUE, is_cust_doc, is_cn);
951 if (gnc_numeric_check (value) == GNC_ERROR_OK)
952 net_total = gnc_numeric_add (net_total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
953 else
954 PWARN ("bad value in our entry");
955 }
956
957 if (taxes)
958 {
959 AccountValueList *entrytaxes = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
960 tv_list = gncAccountValueAddList (tv_list, entrytaxes);
961 gncAccountValueDestroy (entrytaxes);
962 }
963 }
964
965 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 for (node = tv_list; node; node=node->next)
971 {
972 GncAccountValue *acc_val = node->data;
973 acc_val->value = gnc_numeric_convert (acc_val->value,
974 denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
975 }
976 *taxes = tv_list;
977 }
978
979 LEAVE ("%" PRId64 "/%" PRId64, net_total.num, net_total.denom);
980 return net_total;
981}
982
983static 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 if (!invoice) return gnc_numeric_zero ();
991
992 ENTER ("");
993 total = gncInvoiceGetNetAndTaxesInternal (invoice, use_value, use_tax? &taxes : NULL, use_payment_type, type);
994
995 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 total = gnc_numeric_add (total, gncInvoiceSumTaxesInternal (taxes),
1001 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1002 gncAccountValueDestroy (taxes);
1003 }
1004 LEAVE ("%" PRId64 "/%" PRId64, total.num, total.denom);
1005 return total;
1006}
1007
1008gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice)
1009{
1010 if (!invoice) return gnc_numeric_zero ();
1011 return gncInvoiceGetTotalInternal (invoice, TRUE, TRUE, FALSE, 0);
1012}
1013
1014gnc_numeric gncInvoiceGetTotalSubtotal (GncInvoice *invoice)
1015{
1016 if (!invoice) return gnc_numeric_zero ();
1017 return gncInvoiceGetTotalInternal (invoice, TRUE, FALSE, FALSE, 0);
1018}
1019
1020gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice)
1021{
1022 if (!invoice) return gnc_numeric_zero ();
1023 return gncInvoiceGetTotalInternal (invoice, FALSE, TRUE, FALSE, 0);
1024}
1025
1026gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type)
1027{
1028 if (!invoice) return gnc_numeric_zero ();
1029 return gncInvoiceGetTotalInternal (invoice, TRUE, TRUE, TRUE, type);
1030}
1031
1032AccountValueList *gncInvoiceGetTotalTaxList (GncInvoice *invoice)
1033{
1034 AccountValueList *taxes;
1035 if (!invoice) return NULL;
1036
1037 gncInvoiceGetNetAndTaxesInternal (invoice, FALSE, &taxes, FALSE, 0);
1038 return taxes;
1039}
1040
1041GList * gncInvoiceGetTypeListForOwnerType (GncOwnerType type)
1042{
1043 GList *type_list = NULL;
1044 switch (type)
1045 {
1046 case GNC_OWNER_CUSTOMER:
1047 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_INVOICE));
1048 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_CREDIT_NOTE));
1049 return type_list;
1050 case GNC_OWNER_VENDOR:
1051 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_INVOICE));
1052 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_CREDIT_NOTE));
1053 return type_list;
1054 case GNC_OWNER_EMPLOYEE:
1055 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_INVOICE));
1056 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_CREDIT_NOTE));
1057 return type_list;
1058 default:
1059 PWARN("Bad owner type, no invoices.");
1060 return NULL;
1061 }
1062
1063}
1064
1065GncInvoiceType gncInvoiceGetType (const GncInvoice *invoice)
1066{
1067 if (!invoice) return GNC_INVOICE_UNDEFINED;
1068 switch (gncInvoiceGetOwnerType (invoice))
1069 {
1070 case GNC_OWNER_CUSTOMER:
1071 return (gncInvoiceGetIsCreditNote (invoice) ?
1072 GNC_INVOICE_CUST_CREDIT_NOTE :
1073 GNC_INVOICE_CUST_INVOICE);
1074 case GNC_OWNER_VENDOR:
1075 return (gncInvoiceGetIsCreditNote (invoice) ?
1076 GNC_INVOICE_VEND_CREDIT_NOTE :
1077 GNC_INVOICE_VEND_INVOICE);
1078 case GNC_OWNER_EMPLOYEE:
1079 return (gncInvoiceGetIsCreditNote (invoice) ?
1080 GNC_INVOICE_EMPL_CREDIT_NOTE :
1081 GNC_INVOICE_EMPL_INVOICE);
1082 default:
1083 PWARN ("No invoice types defined for owner %d",
1084 gncInvoiceGetOwnerType (invoice));
1085 return GNC_INVOICE_UNDEFINED;
1086 }
1087}
1088
1089const char * gncInvoiceGetTypeString (const GncInvoice *invoice)
1090{
1091 GncInvoiceType type = gncInvoiceGetType (invoice);
1092 switch (type)
1093 {
1094 case GNC_INVOICE_CUST_INVOICE:
1095 return _("Invoice");
1096 case GNC_INVOICE_VEND_INVOICE:
1097 return _("Bill");
1098 case GNC_INVOICE_EMPL_INVOICE:
1099 return _("Expense");
1100 case GNC_INVOICE_CUST_CREDIT_NOTE:
1101 case GNC_INVOICE_VEND_CREDIT_NOTE:
1102 case GNC_INVOICE_EMPL_CREDIT_NOTE:
1103 return _("Credit Note");
1104 default:
1105 PWARN("Unknown invoice type");
1106 return NULL;
1107 }
1108}
1109
1110gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice)
1111{
1112 if (!invoice) return NULL;
1113 return invoice->currency;
1114}
1115
1116GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice)
1117{
1118 if (!invoice) return NULL;
1119 return &invoice->billto;
1120}
1121
1122GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice)
1123{
1124 if (!invoice) return NULL;
1125 return invoice->posted_lot;
1126}
1127
1128Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice)
1129{
1130 if (!invoice) return NULL;
1131 return invoice->posted_txn;
1132}
1133
1134Account * gncInvoiceGetPostedAcc (const GncInvoice *invoice)
1135{
1136 if (!invoice) return NULL;
1137 return invoice->posted_acc;
1138}
1139
1140gboolean gncInvoiceGetActive (const GncInvoice *invoice)
1141{
1142 if (!invoice) return FALSE;
1143 return invoice->active;
1144}
1145
1146gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice)
1147{
1148 GValue v = G_VALUE_INIT;
1149 gboolean retval;
1150 if (!invoice) return FALSE;
1151 qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_IS_CN);
1152 retval = G_VALUE_HOLDS_INT64(&v) && g_value_get_int64 (&v);
1153 g_value_unset (&v);
1154 return retval;
1155}
1156
1157
1158gnc_numeric gncInvoiceGetToChargeAmount (const GncInvoice *invoice)
1159{
1160 if (!invoice) return gnc_numeric_zero ();
1161 return invoice->to_charge_amount;
1162}
1163
1164EntryList * gncInvoiceGetEntries (GncInvoice *invoice)
1165{
1166 if (!invoice) return NULL;
1167 return invoice->entries;
1168}
1169
1170GNCPrice * gncInvoiceGetPrice (GncInvoice *invoice, gnc_commodity *commodity)
1171{
1172 GList *node = g_list_first (invoice->prices);
1173
1174 while (node != NULL)
1175 {
1176 GNCPrice *curr = (GNCPrice*)node->data;
1177
1178 if (gnc_commodity_equal (commodity, gnc_price_get_commodity (curr)))
1179 return curr;
1180
1181 node = g_list_next (node);
1182 }
1183
1184 return NULL;
1185}
1186
1187static GncJob*
1188qofInvoiceGetJob (const GncInvoice *invoice)
1189{
1190 if (!invoice)
1191 {
1192 return NULL;
1193 }
1194 return invoice->job;
1195}
1196
1197static void
1198qofInvoiceSetJob (GncInvoice *invoice, GncJob *job)
1199{
1200 if (!invoice)
1201 {
1202 return;
1203 }
1204 invoice->job = job;
1205}
1206
1207void
1208gncInvoiceDetachFromLot (GNCLot *lot)
1209{
1210 if (!lot) return;
1211
1212 gnc_lot_begin_edit (lot);
1213 qof_instance_set (QOF_INSTANCE (lot), "invoice", NULL, NULL);
1214 gnc_lot_commit_edit (lot);
1215 gnc_lot_set_cached_invoice (lot, NULL);
1216}
1217
1218void
1219gncInvoiceAttachToLot (GncInvoice *invoice, GNCLot *lot)
1220{
1221 GncGUID *guid;
1222 if (!invoice || !lot)
1223 return;
1224
1225 if (invoice->posted_lot) return; /* Cannot reset invoice's lot */
1226 guid = (GncGUID*)qof_instance_get_guid (QOF_INSTANCE(invoice));
1227 gnc_lot_begin_edit (lot);
1228 qof_instance_set (QOF_INSTANCE (lot), "invoice", guid, NULL);
1229 gnc_lot_commit_edit (lot);
1230 gnc_lot_set_cached_invoice (lot, invoice);
1231 gncInvoiceSetPostedLot (invoice, lot);
1232}
1233
1234GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot)
1235{
1236 GncGUID *guid = NULL;
1237 QofBook *book;
1238 GncInvoice *invoice = NULL;
1239
1240 if (!lot) return NULL;
1241
1242 invoice = gnc_lot_get_cached_invoice (lot);
1243 if (!invoice)
1244 {
1245 book = gnc_lot_get_book (lot);
1246 qof_instance_get (QOF_INSTANCE(lot), "invoice", &guid, NULL);
1247 invoice = gncInvoiceLookup (book, guid);
1248 guid_free (guid);
1249 gnc_lot_set_cached_invoice (lot, invoice);
1250 }
1251
1252 return invoice;
1253}
1254
1255void
1256gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn)
1257{
1258 if (!invoice || !txn)
1259 return;
1260
1261 if (invoice->posted_txn) return; /* Cannot reset invoice's txn */
1262
1263 xaccTransBeginEdit (txn);
1264 qof_instance_set (QOF_INSTANCE (txn), "invoice", //Prop INVOICE
1265 qof_instance_get_guid (QOF_INSTANCE (invoice)), NULL);
1267 xaccTransCommitEdit (txn);
1268 gncInvoiceSetPostedTxn (invoice, txn);
1269}
1270
1271GncInvoice *
1272gncInvoiceGetInvoiceFromTxn (const Transaction *txn)
1273{
1274 GncGUID *guid = NULL;
1275 QofBook *book;
1276 GncInvoice *invoice = NULL;
1277
1278 if (!txn) return NULL;
1279
1280 book = xaccTransGetBook (txn);
1281 qof_instance_get (QOF_INSTANCE (txn), "invoice", &guid, NULL);
1282 invoice = gncInvoiceLookup (book, guid);
1283 guid_free (guid);
1284 return invoice;
1285}
1286
1287gboolean gncInvoiceAmountPositive (const GncInvoice *invoice)
1288{
1289 switch (gncInvoiceGetType (invoice))
1290 {
1291 case GNC_INVOICE_CUST_INVOICE:
1292 case GNC_INVOICE_VEND_CREDIT_NOTE:
1293 case GNC_INVOICE_EMPL_CREDIT_NOTE:
1294 return TRUE;
1295 case GNC_INVOICE_CUST_CREDIT_NOTE:
1296 case GNC_INVOICE_VEND_INVOICE:
1297 case GNC_INVOICE_EMPL_INVOICE:
1298 return FALSE;
1299 case GNC_INVOICE_UNDEFINED:
1300 default:
1301 /* Should never be reached.
1302 * If it is, perhaps a new value is added to GncInvoiceType ? */
1303 g_assert_not_reached ();
1304 return FALSE;
1305 }
1306}
1307
1308GHashTable *gncInvoiceGetForeignCurrencies (const GncInvoice *invoice)
1309{
1310 EntryList *entries_iter;
1311 gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
1312 gboolean is_cn = gncInvoiceGetIsCreditNote (invoice);
1313 GHashTable *amt_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1314 NULL, g_free);
1315 ENTER ("");
1316
1317 for (entries_iter = invoice->entries; entries_iter != NULL; entries_iter = g_list_next(entries_iter))
1318 {
1319 GncEntry *entry = (GncEntry*)entries_iter->data;
1320 Account *this_acc;
1321 gnc_commodity *account_currency;
1322 AccountValueList *tt_amts = NULL, *tt_iter;
1323
1324 /* Check entry's account currency */
1325 this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
1326 gncEntryGetBillAccount (entry));
1327 account_currency = xaccAccountGetCommodity (this_acc);
1328
1329 if (this_acc &&
1330 !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), account_currency))
1331 {
1332 gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, account_currency);
1333 gnc_numeric *entry_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
1334 *entry_amt = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
1335 if (curr_amt)
1336 *entry_amt = gnc_numeric_add (*entry_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
1337 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 tt_amts = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
1343
1344 if (!tt_amts)
1345 continue;
1346
1347 for (tt_iter = tt_amts; tt_iter != NULL; tt_iter = g_list_next(tt_iter))
1348 {
1349 GncAccountValue *tt_amt_val = (GncAccountValue*)tt_iter->data;
1350 Account *tt_acc = tt_amt_val->account;
1351 gnc_commodity *tt_acc_currency = xaccAccountGetCommodity (tt_acc);
1352
1353 if (tt_acc &&
1354 !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), tt_acc_currency))
1355 {
1356 gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, tt_acc_currency);
1357 gnc_numeric *tt_acc_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
1358 *tt_acc_amt = tt_amt_val->value;
1359 if (curr_amt)
1360 *tt_acc_amt = gnc_numeric_add (*tt_acc_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
1361 g_hash_table_insert (amt_hash, tt_acc_currency, tt_acc_amt);
1362 }
1363 }
1364 gncAccountValueDestroy (tt_amts);
1365 }
1366
1367 LEAVE ("");
1368 return amt_hash;
1369}
1370
1371static 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 ENTER ("");
1382 split = xaccMallocSplit (book);
1383 /* set action and memo? */
1384
1385 xaccSplitSetMemo (split, memo);
1386 /* set per book option */
1387 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 */
1394 xaccAccountInsertSplit (acc, split);
1396 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 if (gnc_commodity_equal (xaccAccountGetCommodity (acc), invoice->currency))
1409 {
1410 xaccSplitSetBaseValue (split, value,
1411 invoice->currency);
1412 }
1413 else
1414 {
1415 /*need to do conversion */
1416 GNCPrice *price = gncInvoiceGetPrice (invoice, xaccAccountGetCommodity (acc));
1417
1418 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 PERR("Multiple commodities with no price.");
1425 LEAVE ("FALSE");
1426 return FALSE;
1427 }
1428 else
1429 {
1430 gnc_numeric converted_amount;
1431 xaccSplitSetValue (split, value);
1432 converted_amount = gnc_numeric_div (value, gnc_price_get_value (price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
1433 DEBUG("converting from %f to %f\n", gnc_numeric_to_double (value), gnc_numeric_to_double (converted_amount));
1434 xaccSplitSetAmount (split, converted_amount);
1435 }
1436 }
1437
1438 LEAVE ("TRUE");
1439 return TRUE;
1440}
1441
1442Transaction * 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 GNCLot *lot = NULL;
1450 GList *iter;
1451 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 Account *ccard_acct = NULL;
1458 const GncOwner *owner;
1459 int denom = xaccAccountGetCommoditySCU (acc);
1460 AccountValueList *taxes;
1461
1462 if (!invoice || !acc) return NULL;
1463 if (gncInvoiceIsPosted (invoice)) return NULL;
1464
1465 ENTER ("");
1466 gncInvoiceBeginEdit (invoice);
1467 book = qof_instance_get_book (invoice);
1468
1469 /* Stabilize the Billing Terms of this invoice */
1470 if (invoice->terms)
1471 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 is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
1476 is_cn = gncInvoiceGetIsCreditNote (invoice);
1477
1478 /* Figure out if we need to separate out "credit-card" items */
1479 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
1480 if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE)
1481 ccard_acct = gncEmployeeGetCCard (gncOwnerGetEmployee (owner));
1482
1483 /* Create a new lot for this invoice */
1484 lot = gnc_lot_new (book);
1485 gncInvoiceAttachToLot (invoice, lot);
1486 gnc_lot_begin_edit (lot);
1487
1488 type = gncInvoiceGetTypeString (invoice);
1489
1490 /* Set the lot title */
1491 lot_title = g_strdup_printf ("%s %s", type, gncInvoiceGetID (invoice));
1492 gnc_lot_set_title (lot, lot_title);
1493 g_free (lot_title);
1494
1495 /* Create a new transaction */
1496 txn = xaccMallocTransaction (book);
1497 xaccTransBeginEdit (txn);
1498
1499 name = gncOwnerGetName (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)));
1500
1501 /* Set Transaction Description (Owner Name) , Num (invoice ID or type, based
1502 * on book option), Currency */
1503 xaccTransSetDescription (txn, name ? name : "");
1504 gnc_set_num_action (txn, NULL, gncInvoiceGetID (invoice), type);
1505 xaccTransSetCurrency (txn, invoice->currency);
1506
1507 /* Entered and Posted at date */
1509 xaccTransSetDatePostedSecsNormalized (txn, post_date);
1510 gncInvoiceSetDatePosted (invoice, xaccTransRetDatePosted(txn));
1511
1512 xaccTransSetDateDue (txn, due_date);
1513
1514 /* Get invoice total and taxes. */
1515 total = gncInvoiceGetTotal (invoice);
1516 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 if (is_cust_doc != is_cn)
1523 {
1524 GList *node;
1525 total = gnc_numeric_neg (total);
1526 for (node = taxes; node; node = node->next)
1527 {
1528 GncAccountValue *acc_val = node->data;
1529 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 for (iter = gncInvoiceGetEntries (invoice); iter; iter = iter->next)
1538 {
1539 gnc_numeric value, tax;
1540 GncEntry * entry = iter->data;
1541 Account *this_acc;
1542
1543 /* Stabilize the TaxTable in this entry */
1544 gncEntryBeginEdit (entry);
1545 if (is_cust_doc)
1546 gncEntrySetInvTaxTable
1547 (entry, gncTaxTableReturnChild (gncEntryGetInvTaxTable (entry), TRUE));
1548 else
1549 {
1550 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 if (gncEntryGetBillable (entry))
1555 {
1556 /* We need to set the net price since it may be another tax rate for invoices than bills */
1557 gncEntrySetInvPrice (entry, gncEntryGetPrice (entry, FALSE, TRUE));
1558 gncEntrySetInvTaxIncluded (entry, FALSE);
1559 }
1560 }
1561 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 value = gncEntryGetBalValue (entry, TRUE, is_cust_doc);
1566 tax = gncEntryGetBalTaxValue (entry, TRUE, is_cust_doc);
1567
1568 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 this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
1572 gncEntryGetBillAccount (entry));
1573 if (this_acc)
1574 {
1575 if (gnc_numeric_check (value) == GNC_ERROR_OK)
1576 {
1577 if (accumulatesplits)
1578 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 else if (!gncInvoicePostAddSplit (book, this_acc, txn, value,
1581 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 PERR("Failed to add split %s", gncEntryGetDescription (entry));
1589 LEAVE ("NULL");
1590 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 if (ccard_acct && gncEntryGetBillPayment (entry) == GNC_PAYMENT_CARD)
1603 {
1604 Split *split;
1605
1606 total = gnc_numeric_sub (total, value, denom,
1608
1609 split = xaccMallocSplit (book);
1610 xaccSplitSetMemo (split, gncEntryGetDescription (entry));
1611 /* set action based on book option */
1612 gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1613 xaccAccountBeginEdit (ccard_acct);
1614 xaccAccountInsertSplit (ccard_acct, split);
1615 xaccAccountCommitEdit (ccard_acct);
1616 xaccTransAppendSplit (txn, split);
1617 xaccSplitSetBaseValue (split, gnc_numeric_neg (value),
1618 invoice->currency);
1619
1620 }
1621
1622 }
1623 else
1624 PWARN ("bad value in our entry");
1625 }
1626
1627 /* check the taxes */
1628 if (gnc_numeric_check (tax) != GNC_ERROR_OK)
1629 PWARN ("bad tax in our entry");
1630
1631 } /* for */
1632
1633
1634 /* now merge in the TaxValues */
1635 splitinfo = gncAccountValueAddList (splitinfo, taxes);
1636 gncAccountValueDestroy (taxes);
1637
1638 /* Iterate through the splitinfo list and generate the splits */
1639 if (splitinfo)
1640 PINFO ("Processing Split List");
1641 for (iter = splitinfo; iter; iter = iter->next)
1642 {
1643 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 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 PERR("Failed to add split %s, aborting accumulated splits.", memo);
1655 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 if (ccard_acct && !gnc_numeric_zero_p (invoice->to_charge_amount))
1663 {
1664 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 gnc_numeric to_charge_bal_amount = (is_cn ? gnc_numeric_neg (invoice->to_charge_amount)
1669 : invoice->to_charge_amount);
1670
1671 PINFO ("Process to_card payment split");
1672 /* Set memo. */
1673 xaccSplitSetMemo (split, _("Extra to Charge Card"));
1674 /* Set action based on book option */
1675 gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1676
1677 xaccAccountBeginEdit (ccard_acct);
1678 xaccAccountInsertSplit (ccard_acct, split);
1679 xaccAccountCommitEdit (ccard_acct);
1680 xaccTransAppendSplit (txn, split);
1681 xaccSplitSetBaseValue (split, gnc_numeric_neg (to_charge_bal_amount),
1682 invoice->currency);
1683
1684 total = gnc_numeric_sub (total, to_charge_bal_amount, denom,
1686 }
1687
1688 /* Now create the Posted split (which is the opposite sign of the above splits) */
1689 {
1690 Split *split = xaccMallocSplit (book);
1691
1692 PINFO ("Process to_card balancing split");
1693 /* Set memo */
1694 xaccSplitSetMemo (split, memo);
1695 /* Set action based on book option */
1696 gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1697
1699 xaccAccountInsertSplit (acc, split);
1701 xaccTransAppendSplit (txn, split);
1702 xaccSplitSetBaseValue (split, gnc_numeric_neg (total),
1703 invoice->currency);
1704
1705 /* add this split to the lot */
1706 gnc_lot_add_split (lot, split);
1707 }
1708
1709 /* Now attach this invoice to the txn and account */
1710 gncInvoiceAttachToTxn (invoice, txn);
1711 gncInvoiceSetPostedAcc (invoice, acc);
1712
1713 xaccTransSetReadOnly (txn, _("Generated from an invoice. Try unposting the invoice."));
1714 xaccTransCommitEdit (txn);
1715
1716 gncAccountValueDestroy (splitinfo);
1717
1718 gnc_lot_commit_edit (lot);
1719 /* Not strictly necessary, since it was done by the Set calls
1720 * above, but good insurance. */
1721 DEBUG("Committing Invoice %s", invoice->id);
1722 mark_invoice (invoice);
1723 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 if (autopay)
1730
1731 LEAVE ("");
1732 return txn;
1733}
1734
1735gboolean
1736gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables)
1737{
1738 Transaction *txn;
1739 GNCLot *lot;
1740 GList *lot_split_list, *lot_split_iter;
1741
1742 if (!invoice) return FALSE;
1743 if (!gncInvoiceIsPosted (invoice)) return FALSE;
1744
1745 txn = gncInvoiceGetPostedTxn (invoice);
1746 g_return_val_if_fail (txn, FALSE);
1747
1748 lot = gncInvoiceGetPostedLot (invoice);
1749 g_return_val_if_fail (lot, FALSE);
1750
1751 ENTER ("");
1752 /* Destroy the Posted Transaction */
1753 xaccTransClearReadOnly (txn);
1754 xaccTransBeginEdit (txn);
1755 xaccTransDestroy (txn);
1756 xaccTransCommitEdit (txn);
1757
1758 /* Disconnect the lot from the invoice; re-attach to the invoice owner */
1759 gncInvoiceDetachFromLot (lot);
1760 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 lot_split_list = g_list_copy (gnc_lot_get_split_list (lot));
1777 if (lot_split_list)
1778 PINFO ("Recreating link transactions for remaining lots");
1779 for (lot_split_iter = lot_split_list; lot_split_iter; lot_split_iter = lot_split_iter->next)
1780 {
1781 Split *split = lot_split_iter->data;
1782 GList *other_split_list, *list_iter;
1783 Transaction *other_txn = xaccSplitGetParent (split);
1784 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 if (xaccTransGetTxnType (other_txn) != TXN_TYPE_LINK)
1789 continue;
1790
1791 /* Save a list of lots this linking transaction linked to */
1792 other_split_list = xaccTransGetSplitList (other_txn);
1793 for (list_iter = other_split_list; list_iter; list_iter = list_iter->next)
1794 {
1795 Split *other_split = list_iter->data;
1796 GNCLot *other_lot = xaccSplitGetLot (other_split);
1797
1798 /* Omit the lot we are about to delete */
1799 if (other_lot == lot)
1800 continue;
1801
1802 lot_list = g_list_prepend (lot_list, other_lot);
1803 }
1804 /* Maintain original split order */
1805 lot_list = g_list_reverse (lot_list);
1806
1807 /* Now remove this link transaction. */
1808 xaccTransClearReadOnly (other_txn);
1809 xaccTransBeginEdit (other_txn);
1810 xaccTransDestroy (other_txn);
1811 xaccTransCommitEdit (other_txn);
1812
1813 /* Re-balance the saved lots as well as is possible */
1814 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 for (list_iter = lot_list; list_iter; list_iter = list_iter->next)
1820 {
1821 GNCLot *other_lot = list_iter->data;
1822 GncInvoice *other_invoice = gncInvoiceGetInvoiceFromLot (other_lot);
1823
1824 if (!gnc_lot_count_splits (other_lot))
1825 gnc_lot_destroy (other_lot);
1826 else if (other_invoice)
1827 qof_event_gen (QOF_INSTANCE(other_invoice), QOF_EVENT_MODIFY, NULL);
1828 }
1829 g_list_free (lot_list);
1830 }
1831 g_list_free (lot_split_list);
1832
1833 /* If the lot has no splits, then destroy it */
1834 if (!gnc_lot_count_splits (lot))
1835 gnc_lot_destroy (lot);
1836
1837 /* Clear out the invoice posted information */
1838 gncInvoiceBeginEdit (invoice);
1839
1840 invoice->posted_acc = NULL;
1841 invoice->posted_txn = NULL;
1842 invoice->posted_lot = NULL;
1843 invoice->date_posted = INT64_MAX;
1844
1845 /* if we've been asked to reset the tax tables, then do so */
1846 if (reset_tax_tables)
1847 {
1848 gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
1849 GList *iter;
1850
1851 for (iter = gncInvoiceGetEntries (invoice); iter; iter = iter->next)
1852 {
1853 GncEntry *entry = iter->data;
1854
1855 gncEntryBeginEdit (entry);
1856 if (is_cust_doc)
1857 gncEntrySetInvTaxTable (entry,
1858 gncTaxTableGetParent (gncEntryGetInvTaxTable( entry)));
1859 else
1860 gncEntrySetBillTaxTable (entry,
1861 gncTaxTableGetParent (gncEntryGetBillTaxTable (entry)));
1862 gncEntryCommitEdit (entry);
1863 }
1864 }
1865
1866 mark_invoice (invoice);
1867 gncInvoiceCommitEdit (invoice);
1868
1869 LEAVE ("TRUE");
1870
1871 return TRUE;
1872}
1873
1875{
1876 const GncOwner *owner;
1877 gboolean positive_balance;
1878};
1879
1880static gboolean
1881gnc_lot_match_owner_balancing (GNCLot *lot, gpointer user_data)
1882{
1883 struct lotmatch *lm = user_data;
1884 GncOwner owner_def;
1885 const GncOwner *owner;
1886 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 if (lm->positive_balance == gnc_numeric_positive_p (balance))
1891 return FALSE;
1892
1893 /* Is it ours? Either the lot owner or the lot invoice owner should match */
1894 if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
1895 {
1896 const GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
1897 if (!invoice)
1898 return FALSE;
1899 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
1900 }
1901 else
1902 owner = gncOwnerGetEndOwner (&owner_def);
1903
1904 return gncOwnerEqual (owner, lm->owner);
1905}
1906
1907void 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 g_return_if_fail (invoice);
1924 g_return_if_fail (invoice->posted_lot);
1925
1926 inv_lot = invoice->posted_lot;
1927 acct = invoice->posted_acc;
1928 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 lm.positive_balance = gnc_numeric_positive_p (gnc_lot_get_balance (inv_lot));
1937 lm.owner = owner;
1938 lot_list = xaccAccountFindOpenLots (acct, gnc_lot_match_owner_balancing,
1939 &lm, NULL);
1940
1941 lot_list = g_list_prepend (lot_list, inv_lot);
1942 gncOwnerAutoApplyPaymentsWithLots (owner, lot_list);
1943 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 */
1950void
1951gncInvoiceApplyPayment (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 GList *selected_lots = NULL;
1958 const GncOwner *owner;
1959
1960 /* Verify our arguments */
1961 if (!invoice || !gncInvoiceIsPosted (invoice) || !xfer_acc) return;
1962
1963 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
1964 g_return_if_fail (owner->owner.undefined);
1965
1966 /* Create a lot for this payment */
1967 payment_lot = gncOwnerCreatePaymentLotSecs (owner, &txn,
1968 invoice->posted_acc,
1969 xfer_acc, amount, exch,
1970 date, memo, num);
1971
1972 /* Select the invoice as only payment candidate */
1973 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 if (payment_lot)
1977 selected_lots = g_list_prepend (selected_lots, payment_lot);
1978 gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
1979}
1980
1981gboolean gncInvoiceIsPosted (const GncInvoice *invoice)
1982{
1983 if (!invoice) return FALSE;
1984 return GNC_IS_TRANSACTION(gncInvoiceGetPostedTxn (invoice));
1985}
1986
1987gboolean gncInvoiceIsPaid (const GncInvoice *invoice)
1988{
1989 if (!invoice) return FALSE;
1990 if (!invoice->posted_lot) return FALSE;
1991 return gnc_lot_is_closed (invoice->posted_lot);
1992}
1993
1994/* ================================================================== */
1995
1996void gncInvoiceBeginEdit (GncInvoice *invoice)
1997{
1998 qof_begin_edit (&invoice->inst);
1999}
2000
2001static void gncInvoiceOnError (QofInstance *inst, QofBackendError errcode)
2002{
2003 PERR("Invoice QofBackend Failure: %d", errcode);
2004 gnc_engine_signal_commit_error (errcode);
2005}
2006
2007static void gncInvoiceOnDone (QofInstance *invoice) { }
2008
2009static void invoice_free (QofInstance *inst)
2010{
2011 GncInvoice *invoice = (GncInvoice *) inst;
2012 gncInvoiceFree (invoice);
2013}
2014
2015void gncInvoiceCommitEdit (GncInvoice *invoice)
2016{
2017 if (!qof_commit_edit (QOF_INSTANCE(invoice))) return;
2018 qof_commit_edit_part2 (&invoice->inst, gncInvoiceOnError,
2019 gncInvoiceOnDone, invoice_free);
2020}
2021
2022int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b)
2023{
2024 int compare;
2025
2026 if (a == b) return 0;
2027 if (!a) return -1;
2028 if (!b) return 1;
2029
2030 compare = g_strcmp0 (a->id, b->id);
2031 if (compare) return compare;
2032 if (a->date_opened != b->date_opened) return a->date_opened - b->date_opened;
2033 if (a->date_posted != b->date_posted) return a->date_posted - b->date_posted;
2034 return qof_instance_guid_compare(a, b);
2035}
2036
2037gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b)
2038{
2039 if (a == NULL && b == NULL) return TRUE;
2040 if (a == NULL || b == NULL) return FALSE;
2041
2042 g_return_val_if_fail (GNC_IS_INVOICE(a), FALSE);
2043 g_return_val_if_fail (GNC_IS_INVOICE(b), FALSE);
2044
2045 if (g_strcmp0 (a->id, b->id) != 0)
2046 {
2047 PWARN("IDs differ: %s vs %s", a->id, b->id);
2048 return FALSE;
2049 }
2050
2051 if (g_strcmp0 (a->notes, b->notes) != 0)
2052 {
2053 PWARN("Notes differ: %s vs %s", a->notes, b->notes);
2054 return FALSE;
2055 }
2056
2057 if (g_strcmp0 (a->billing_id, b->billing_id) != 0)
2058 {
2059 PWARN("Billing IDs differ: %s vs %s", a->billing_id, b->billing_id);
2060 return FALSE;
2061 }
2062
2063 if (g_strcmp0 (a->printname, b->printname) != 0)
2064 {
2065 PWARN("Printnames differ: %s vs %s", a->printname, b->printname);
2066 return FALSE;
2067 }
2068
2069 if (a->active != b->active)
2070 {
2071 PWARN("Active flags differ");
2072 return FALSE;
2073 }
2074
2075 if (!gncBillTermEqual (a->terms, b->terms))
2076 {
2077 PWARN("Billterms differ");
2078 return FALSE;
2079 }
2080
2081 if (!gncJobEqual (a->job, b->job))
2082 {
2083 PWARN("Jobs differ");
2084 return FALSE;
2085 }
2086
2087 if (!gnc_commodity_equal (a->currency, b->currency))
2088 {
2089 PWARN("Currencies differ");
2090 return FALSE;
2091 }
2092
2093 if (!xaccAccountEqual (a->posted_acc, b->posted_acc, TRUE))
2094 {
2095 PWARN("Posted accounts differ");
2096 return FALSE;
2097 }
2098
2099 if (!xaccTransEqual (a->posted_txn, b->posted_txn, TRUE, TRUE, TRUE, FALSE))
2100 {
2101 PWARN("Posted tx differ");
2102 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 return TRUE;
2126}
2127
2128/* ============================================================= */
2129/* Package-Private functions */
2130
2131static const char * _gncInvoicePrintable (gpointer obj)
2132{
2133 GncInvoice *invoice = obj;
2134
2135 g_return_val_if_fail (invoice, NULL);
2136
2137 if (qof_instance_get_dirty_flag (invoice) || invoice->printname == NULL)
2138 {
2139 if (invoice->printname) g_free (invoice->printname);
2140
2141 invoice->printname =
2142 g_strdup_printf ("%s%s", invoice->id,
2143 gncInvoiceIsPosted (invoice) ? _(" (posted)") : "");
2144 }
2145
2146 return invoice->printname;
2147}
2148
2149static void
2150destroy_invoice_on_book_close (QofInstance *ent, gpointer data)
2151{
2152 GncInvoice* invoice = GNC_INVOICE(ent);
2153
2154 gncInvoiceBeginEdit (invoice);
2155 gncInvoiceDestroy (invoice);
2156}
2157
2158static void
2159gnc_invoice_book_end (QofBook* book)
2160{
2161 QofCollection *col;
2162
2163 col = qof_book_get_collection (book, GNC_ID_INVOICE);
2164 qof_collection_foreach (col, destroy_invoice_on_book_close, NULL);
2165}
2166
2167static 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
2182static void
2183reg_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 qof_class_register (GNC_ID_LOT, NULL, params);
2195}
2196
2197static void
2198reg_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 qof_class_register (GNC_ID_TRANS, NULL, params);
2210}
2211
2212gboolean gncInvoiceRegister (void)
2213{
2214 static QofParam params[] =
2215 {
2216 { INVOICE_ID, QOF_TYPE_STRING, (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_STRING, (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 qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncInvoiceCompare, params);
2242 reg_lot ();
2243 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 return qof_object_register (&gncInvoiceDesc);
2254}
2255
2256gchar *gncInvoiceNextID (QofBook *book, const GncOwner *owner)
2257{
2258 gchar *nextID;
2259 switch (gncOwnerGetType (gncOwnerGetEndOwner (owner)))
2260 {
2261 case GNC_OWNER_CUSTOMER:
2262 nextID = qof_book_increment_and_format_counter (book, "gncInvoice");
2263 break;
2264 case GNC_OWNER_VENDOR:
2265 nextID = qof_book_increment_and_format_counter (book, "gncBill");
2266 break;
2267 case GNC_OWNER_EMPLOYEE:
2268 nextID = qof_book_increment_and_format_counter (book, "gncExpVoucher");
2269 break;
2270 default:
2271 nextID = qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
2272 break;
2273 }
2274 return nextID;
2275}
Account handling public routines.
API for Transactions and Splits (journal entries)
Utility functions for file access.
Business Entry Interface.
Business Invoice Interface.
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2745
gboolean xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
Compare two accounts for equality - this is a deep compare.
Definition Account.cpp:1666
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition qofbackend.h:58
gboolean gncBillTermEqual(const GncBillTerm *a, const GncBillTerm *b)
Check if all internal fields of a and b match.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition qofbook.cpp:521
gboolean qof_book_shutting_down(const QofBook *book)
Is the book shutting down?
Definition qofbook.cpp:447
gchar * qof_book_increment_and_format_counter(QofBook *book, const char *counter_name)
This will increment the named counter for this book and format it.
Definition qofbook.cpp:637
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition qofclass.cpp:86
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition qofclass.h:168
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
time64 time64CanonicalDayTime(time64 t)
convert a time64 on a certain day (localtime) to the time64 representing midday on that day.
Definition gnc-date.cpp:404
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
char xaccTransGetTxnType(Transaction *trans)
Returns the Transaction Type: note this type will be derived from the transaction splits,...
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
void xaccTransSetTxnType(Transaction *trans, char type)
Set the Transaction Type: note the type will be saved into the Transaction kvp property as a backward...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
#define TXN_TYPE_INVOICE
Transaction is an invoice.
LotList * xaccAccountFindOpenLots(const Account *acc, gboolean(*match_func)(GNCLot *lot, gpointer user_data), gpointer user_data, GCompareFunc sort_func)
Find a list of open lots that match the match_func.
Definition Account.cpp:3982
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3)).
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
#define xaccTransGetBook(X)
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
time64 xaccTransRetDateDue(const Transaction *trans)
Dates and txn-type for A/R and A/P "invoice" postings.
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account.
Definition Account.h:1069
void xaccTransSetReadOnly(Transaction *trans, const char *reason)
Set the transaction to be ReadOnly by setting a non-NULL value as "reason".
void xaccTransSetDateDue(Transaction *trans, time64 time)
Dates and txn-type for A/R and A/P "invoice" postings.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
#define TXN_TYPE_LINK
Transaction is a link between (invoice and payment) lots
gboolean xaccTransEqual(const Transaction *ta, const Transaction *tb, gboolean check_guids, gboolean check_splits, gboolean check_balances, gboolean assume_ordered)
Equality.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
Definition qofid.cpp:232
AccountValueList * gncEntryGetDocTaxValues(GncEntry *entry, gboolean is_cust_doc, gboolean is_cn)
Careful: the returned list is NOT owned by the entry and should be freed by the caller.
Definition gncEntry.c:1548
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition qofevent.cpp:231
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
QofCollection * qof_instance_get_collection(gconstpointer ptr)
Return the collection this instance belongs to.
void qof_instance_set(QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
gchar * qof_instance_get_display_name(const QofInstance *inst)
Returns a displayable name for this object.
GList * qof_instance_get_referring_object_list_from_collection(const QofCollection *coll, const QofInstance *ref)
Returns a list of objects from the collection which refer to the specific object.
gnc_numeric gncInvoiceGetTotal(GncInvoice *invoice)
Return the "total" amount of the invoice as seen on the document (and shown to the user in the report...
GHashTable * gncInvoiceGetForeignCurrencies(const GncInvoice *invoice)
Return an overview of amounts on this invoice that will be posted to accounts in currencies that are ...
AccountValueList * gncInvoiceGetTotalTaxList(GncInvoice *invoice)
Return a list of tax totals accumulated per tax account.
void gncInvoiceAutoApplyPayments(GncInvoice *invoice)
Attempt to pay the invoice using open payment lots and lots for documents of the opposite sign (credi...
GncInvoice * gncInvoiceCopy(const GncInvoice *from)
Create a new GncInvoice object as a deep copy of the given other invoice.
Definition gncInvoice.c:336
void gncInvoiceSortEntries(GncInvoice *invoice)
Call this function when an Entry is changed and you want to re-sort the list of entries.
Definition gncInvoice.c:750
gboolean gncInvoiceUnpost(GncInvoice *invoice, gboolean reset_tax_tables)
Unpost this invoice.
void gncBillAddEntry(GncInvoice *bill, GncEntry *entry)
Call this function when adding an entry to a bill instead of an invoice.
Definition gncInvoice.c:719
Transaction * gncInvoicePostToAccount(GncInvoice *invoice, Account *acc, time64 post_date, time64 due_date, const char *memo, gboolean accumulatesplits, gboolean autopay)
Post this invoice to an account.
GncInvoice * gncInvoiceGetInvoiceFromTxn(const Transaction *txn)
Given a transaction, find and return the Invoice.
gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b)
Test support function used by test-dbi-business-stuff.c.
void gncInvoiceRemoveEntries(GncInvoice *invoice)
Remove all entries from an invoice.
Definition gncInvoice.c:760
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
gboolean gncInvoiceAmountPositive(const GncInvoice *invoice)
Depending on the invoice type, invoices have a different effect on the balance.
void gncInvoiceApplyPayment(const GncInvoice *invoice, Transaction *txn, Account *xfer_acc, gnc_numeric amount, gnc_numeric exch, time64 date, const char *memo, const char *num)
A convenience function to apply a payment to an invoice.
void gncInvoiceSetDateOpenedGDate(GncInvoice *invoice, const GDate *date)
Set the DateOpened using a GDate argument.
Definition gncInvoice.c:488
void qof_instance_get_kvp(QofInstance *, GValue *value, unsigned count,...)
Retrieves the contents of a KVP slot into a provided GValue.
void qof_instance_set_kvp(QofInstance *, GValue const *value, unsigned count,...)
Sets a KVP slot to a value from a GValue.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
void gnc_lot_add_split(GNCLot *lot, Split *split)
Adds a split to this lot.
Definition gnc-lot.cpp:579
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Returns the lot balance.
Definition gnc-lot.cpp:487
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Returns a list of all the splits in this lot.
Definition gnc-lot.cpp:425
gboolean gnc_lot_is_closed(GNCLot *lot)
Returns closed status of the given lot.
Definition gnc-lot.cpp:367
GncInvoice * gnc_lot_get_cached_invoice(const GNCLot *lot)
Returns the invoice with which this lot is associated.
Definition gnc-lot.cpp:385
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_ERROR_OK
No error.
@ GNC_HOW_RND_ROUND_HALF_UP
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
@ GNC_HOW_DENOM_EXACT
Use any denominator which gives an exactly correct ratio of numerator to denominator.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition qofid.cpp:238
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition qofobject.h:63
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
void gncOwnerAutoApplyPaymentsWithLots(const GncOwner *owner, GList *lots)
Given a list of lots, try to balance as many of them as possible by creating balancing transactions b...
Definition gncOwner.c:1256
gboolean gncOwnerGetOwnerFromLot(GNCLot *lot, GncOwner *owner)
Get the owner from the lot.
Definition gncOwner.c:636
void gncOwnerAttachToLot(const GncOwner *owner, GNCLot *lot)
Attach an owner to a lot.
Definition gncOwner.c:622
GncEmployee * gncOwnerGetEmployee(const GncOwner *owner)
If the given owner is of type GNC_OWNER_EMPLOYEE, returns the pointer to the employee object.
Definition gncOwner.c:390
GNCLot * gncOwnerCreatePaymentLotSecs(const GncOwner *owner, Transaction **preset_txn, Account *posted_acc, Account *xfer_acc, gnc_numeric amount, gnc_numeric exch, time64 date, const char *memo, const char *num)
Create a lot for a payment to the owner using the other parameters passed in.
Definition gncOwner.c:750
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Get the "parent" Owner or GncGUID thereof.
Definition gncOwner.c:572
gboolean gncOwnerEqual(const GncOwner *a, const GncOwner *b)
Assess equality by checking.
Definition gncOwner.c:404
GncOwnerType gncOwnerGetType(const GncOwner *owner)
Returns the GncOwnerType of this owner.
Definition gncOwner.c:200
QofInstance * qofOwnerGetOwner(const GncOwner *owner)
return the owner itself as an entity.
Definition gncOwner.c:275
void qofOwnerSetEntity(GncOwner *owner, QofInstance *ent)
set the owner from the entity.
Definition gncOwner.c:319
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition qofquery.h:108
void gncAccountValueDestroy(GList *list)
Destroy a list of accountvalues.
GList * gncAccountValueAdd(GList *list, Account *acc, gnc_numeric value)
This will add value to the account-value for acc, creating a new list object if necessary.
GList * gncAccountValueAddList(GList *l1, GList *l2)
Merge l2 into l1.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
Split * xaccMallocSplit(QofBook *book)
Constructor.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Definition Split.cpp:1920
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
Depending on the base_currency, set either the value or the amount of this split or both: If the base...
Definition Split.cpp:1355
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
QofBook reference.
Definition qofbook-p.hpp:47