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