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