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