Branch data 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 : 638 : mark_invoice (GncInvoice *invoice)
98 : : {
99 : 638 : qof_instance_set_dirty (&invoice->inst);
100 : 638 : qof_event_gen (&invoice->inst, QOF_EVENT_MODIFY, NULL);
101 : 638 : }
102 : :
103 : 97 : QofBook * gncInvoiceGetBook (GncInvoice *x)
104 : : {
105 : 97 : 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 : 241 : G_DEFINE_TYPE(GncInvoice, gnc_invoice, QOF_TYPE_INSTANCE)
133 : :
134 : : static void
135 : 86 : gnc_invoice_init (GncInvoice* inv)
136 : : {
137 : 86 : inv->date_posted = INT64_MAX;
138 : 86 : inv->date_opened = INT64_MAX;
139 : 86 : }
140 : :
141 : : static void
142 : 26 : gnc_invoice_dispose (GObject *invp)
143 : : {
144 : 26 : G_OBJECT_CLASS(gnc_invoice_parent_class)->dispose(invp);
145 : 26 : }
146 : :
147 : : static void
148 : 26 : gnc_invoice_finalize (GObject* invp)
149 : : {
150 : 26 : G_OBJECT_CLASS(gnc_invoice_parent_class)->finalize(invp);
151 : 26 : }
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 : 10 : gnc_invoice_class_init (GncInvoiceClass *klass)
287 : : {
288 : 10 : GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
289 : 10 : QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
290 : :
291 : 10 : gobject_class->dispose = gnc_invoice_dispose;
292 : 10 : gobject_class->finalize = gnc_invoice_finalize;
293 : 10 : gobject_class->set_property = gnc_invoice_set_property;
294 : 10 : gobject_class->get_property = gnc_invoice_get_property;
295 : :
296 : 10 : qof_class->get_display_name = impl_get_display_name;
297 : 10 : qof_class->refers_to_object = impl_refers_to_object;
298 : 10 : qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
299 : :
300 : 10 : 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 : 10 : }
311 : :
312 : : /* Create/Destroy Functions */
313 : 55 : GncInvoice *gncInvoiceCreate (QofBook *book)
314 : : {
315 : : GncInvoice *invoice;
316 : :
317 : 55 : if (!book) return NULL;
318 : :
319 : 55 : invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
320 : 55 : qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
321 : :
322 : 55 : invoice->id = CACHE_INSERT ("");
323 : 55 : invoice->notes = CACHE_INSERT ("");
324 : 55 : invoice->billing_id = CACHE_INSERT ("");
325 : :
326 : 55 : invoice->billto.type = GNC_OWNER_CUSTOMER;
327 : 55 : invoice->active = TRUE;
328 : :
329 : 55 : invoice->to_charge_amount = gnc_numeric_zero ();
330 : :
331 : 55 : qof_event_gen (&invoice->inst, QOF_EVENT_CREATE, NULL);
332 : :
333 : 55 : return invoice;
334 : : }
335 : :
336 : 31 : GncInvoice *gncInvoiceCopy (const GncInvoice *from)
337 : : {
338 : : GncInvoice *invoice;
339 : : QofBook* book;
340 : : GList *node;
341 : 31 : GValue v = G_VALUE_INIT;
342 : :
343 : 31 : g_assert (from);
344 : 31 : book = qof_instance_get_book (from);
345 : 31 : g_assert (book);
346 : :
347 : 31 : invoice = g_object_new (GNC_TYPE_INVOICE, NULL);
348 : 31 : qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book);
349 : :
350 : 31 : gncInvoiceBeginEdit (invoice);
351 : :
352 : 31 : invoice->id = CACHE_INSERT (from->id);
353 : 31 : invoice->notes = CACHE_INSERT (from->notes);
354 : 31 : invoice->billing_id = CACHE_INSERT (from->billing_id);
355 : 31 : invoice->active = from->active;
356 : :
357 : 31 : qof_instance_get_kvp (QOF_INSTANCE (from), &v, 1, GNC_INVOICE_IS_CN);
358 : 31 : if (G_VALUE_HOLDS_INT64 (&v))
359 : 0 : qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
360 : 31 : g_value_unset (&v);
361 : :
362 : 31 : invoice->terms = from->terms;
363 : 31 : gncBillTermIncRef (invoice->terms);
364 : :
365 : 31 : gncOwnerCopy (&from->billto, &invoice->billto);
366 : 31 : gncOwnerCopy (&from->owner, &invoice->owner);
367 : 31 : invoice->job = from->job; // FIXME: Need IncRef or similar here?!?
368 : :
369 : 31 : invoice->to_charge_amount = from->to_charge_amount;
370 : 31 : invoice->date_opened = from->date_opened;
371 : :
372 : : // Oops. Do not forget to copy the pointer to the correct currency here.
373 : 31 : invoice->currency = from->currency;
374 : :
375 : 31 : gncInvoiceSetDocLink (invoice, gncInvoiceGetDocLink (from));
376 : :
377 : : // Copy all invoice->entries
378 : 31 : 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 : 31 : mark_invoice (invoice);
404 : 31 : gncInvoiceCommitEdit (invoice);
405 : :
406 : 31 : return invoice;
407 : : }
408 : :
409 : 26 : void gncInvoiceDestroy (GncInvoice *invoice)
410 : : {
411 : 26 : if (!invoice) return;
412 : 26 : qof_instance_set_destroying (invoice, TRUE);
413 : 26 : gncInvoiceCommitEdit (invoice);
414 : : }
415 : :
416 : 26 : static void gncInvoiceFree (GncInvoice *invoice)
417 : : {
418 : 26 : if (!invoice) return;
419 : :
420 : 26 : qof_event_gen (&invoice->inst, QOF_EVENT_DESTROY, NULL);
421 : :
422 : 26 : CACHE_REMOVE (invoice->id);
423 : 26 : CACHE_REMOVE (invoice->notes);
424 : 26 : CACHE_REMOVE (invoice->billing_id);
425 : 26 : g_list_free (invoice->entries);
426 : 26 : g_list_free (invoice->prices);
427 : :
428 : 26 : if (invoice->printname)
429 : 0 : g_free (invoice->printname);
430 : :
431 : 26 : if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(invoice))))
432 : : {
433 : 13 : if (invoice->terms)
434 : 0 : gncBillTermDecRef (invoice->terms);
435 : : }
436 : :
437 : : /* qof_instance_release (&invoice->inst); */
438 : 26 : g_object_unref (invoice);
439 : : }
440 : :
441 : : /* ================================================================== */
442 : : /* Set Functions */
443 : :
444 : 9 : void gncInvoiceSetID (GncInvoice *invoice, const char *id)
445 : : {
446 : 9 : if (!invoice || !id) return;
447 : 9 : SET_STR (invoice, invoice->id, id);
448 : 9 : mark_invoice (invoice);
449 : 9 : gncInvoiceCommitEdit (invoice);
450 : : }
451 : :
452 : 42 : void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner)
453 : : {
454 : 42 : if (!invoice || !owner) return;
455 : 42 : if (gncOwnerEqual (&invoice->owner, owner)) return;
456 : 42 : gncInvoiceBeginEdit (invoice);
457 : 42 : gncOwnerCopy (owner, &invoice->owner);
458 : 42 : mark_invoice (invoice);
459 : 42 : 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 : 14 : void gncInvoiceSetDateOpened (GncInvoice *invoice, time64 date)
495 : : {
496 : 14 : if (!invoice) return;
497 : 14 : if (date == invoice->date_opened) return;
498 : 14 : gncInvoiceBeginEdit (invoice);
499 : 14 : invoice->date_opened = date;
500 : 14 : mark_invoice (invoice);
501 : 14 : gncInvoiceCommitEdit (invoice);
502 : : }
503 : :
504 : 67 : void gncInvoiceSetDatePosted (GncInvoice *invoice, time64 date)
505 : : {
506 : 67 : if (!invoice) return;
507 : 67 : if (date == invoice->date_posted) return;
508 : 67 : gncInvoiceBeginEdit (invoice);
509 : 67 : invoice->date_posted = date;
510 : 67 : mark_invoice (invoice);
511 : 67 : gncInvoiceCommitEdit (invoice);
512 : : }
513 : :
514 : 8 : void gncInvoiceSetTerms (GncInvoice *invoice, GncBillTerm *terms)
515 : : {
516 : 8 : if (!invoice) return;
517 : 8 : if (invoice->terms == terms) return;
518 : 8 : gncInvoiceBeginEdit (invoice);
519 : 8 : if (invoice->terms)
520 : 4 : gncBillTermDecRef (invoice->terms);
521 : 8 : invoice->terms = terms;
522 : 8 : if (invoice->terms)
523 : 8 : gncBillTermIncRef (invoice->terms);
524 : 8 : mark_invoice (invoice);
525 : 8 : gncInvoiceCommitEdit (invoice);
526 : : }
527 : :
528 : 10 : void gncInvoiceSetBillingID (GncInvoice *invoice, const char *billing_id)
529 : : {
530 : 10 : if (!invoice) return;
531 : 10 : SET_STR (invoice, invoice->billing_id, billing_id);
532 : 9 : mark_invoice (invoice);
533 : 9 : gncInvoiceCommitEdit (invoice);
534 : : }
535 : :
536 : 26 : void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes)
537 : : {
538 : 26 : if (!invoice || !notes) return;
539 : 26 : SET_STR (invoice, invoice->notes, notes);
540 : 25 : mark_invoice (invoice);
541 : 25 : gncInvoiceCommitEdit (invoice);
542 : : }
543 : :
544 : 35 : void gncInvoiceSetDocLink (GncInvoice *invoice, const char *doclink)
545 : : {
546 : 35 : 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 : 4 : void gncInvoiceSetActive (GncInvoice *invoice, gboolean active)
567 : : {
568 : 4 : if (!invoice) return;
569 : 4 : 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 : 16 : void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note)
577 : : {
578 : 16 : GValue v = G_VALUE_INIT;
579 : 16 : if (!invoice) return;
580 : 16 : gncInvoiceBeginEdit (invoice);
581 : 16 : g_value_init (&v, G_TYPE_INT64);
582 : 16 : g_value_set_int64 (&v, credit_note ? 1 : 0);
583 : 16 : qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
584 : 16 : g_value_unset (&v);
585 : 16 : mark_invoice (invoice);
586 : 16 : 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 : 16 : if (credit_note)
592 : 16 : gnc_features_set_used (gncInvoiceGetBook (invoice), GNC_FEATURE_CREDIT_NOTES);
593 : : }
594 : :
595 : 56 : void gncInvoiceSetCurrency (GncInvoice *invoice, gnc_commodity *currency)
596 : : {
597 : 56 : if (!invoice || !currency) return;
598 : 68 : if (invoice->currency &&
599 : 12 : gnc_commodity_equal (invoice->currency, currency))
600 : 12 : return;
601 : 44 : gncInvoiceBeginEdit (invoice);
602 : 44 : invoice->currency = currency;
603 : 44 : mark_invoice (invoice);
604 : 44 : 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 : 66 : void gncInvoiceSetPostedTxn (GncInvoice *invoice, Transaction *txn)
629 : : {
630 : 66 : if (!invoice) return;
631 : 66 : g_return_if_fail (invoice->posted_txn == NULL);
632 : :
633 : 66 : gncInvoiceBeginEdit (invoice);
634 : 66 : invoice->posted_txn = txn;
635 : 66 : mark_invoice (invoice);
636 : 66 : gncInvoiceCommitEdit (invoice);
637 : : }
638 : :
639 : 66 : void gncInvoiceSetPostedLot (GncInvoice *invoice, GNCLot *lot)
640 : : {
641 : 66 : if (!invoice) return;
642 : 66 : g_return_if_fail (invoice->posted_lot == NULL);
643 : :
644 : 66 : gncInvoiceBeginEdit (invoice);
645 : 66 : invoice->posted_lot = lot;
646 : 66 : mark_invoice (invoice);
647 : 66 : gncInvoiceCommitEdit (invoice);
648 : : }
649 : :
650 : 66 : void gncInvoiceSetPostedAcc (GncInvoice *invoice, Account *acc)
651 : : {
652 : 66 : if (!invoice) return;
653 : 66 : g_return_if_fail (invoice->posted_acc == NULL);
654 : :
655 : 66 : gncInvoiceBeginEdit (invoice);
656 : 66 : invoice->posted_acc = acc;
657 : 66 : mark_invoice (invoice);
658 : 66 : gncInvoiceCommitEdit (invoice);
659 : : }
660 : :
661 : 91 : void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry)
662 : : {
663 : : GncInvoice *old;
664 : :
665 : 91 : g_assert (invoice);
666 : 91 : g_assert (entry);
667 : 91 : if (!invoice || !entry) return;
668 : :
669 : 91 : old = gncEntryGetInvoice (entry);
670 : 91 : if (old == invoice) return; /* I already own this one */
671 : 91 : if (old) gncInvoiceRemoveEntry (old, entry);
672 : :
673 : 91 : gncInvoiceBeginEdit (invoice);
674 : 91 : gncEntrySetInvoice (entry, invoice);
675 : 91 : invoice->entries = g_list_insert_sorted (invoice->entries, entry,
676 : : (GCompareFunc)gncEntryCompare);
677 : 91 : mark_invoice (invoice);
678 : 91 : 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 : 321 : const char * gncInvoiceGetID (const GncInvoice *invoice)
802 : : {
803 : 321 : if (!invoice) return NULL;
804 : 321 : return invoice->id;
805 : : }
806 : :
807 : 2651 : const GncOwner * gncInvoiceGetOwner (const GncInvoice *invoice)
808 : : {
809 : 2651 : if (!invoice) return NULL;
810 : 1811 : 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 : 97 : time64 gncInvoiceGetDatePosted (const GncInvoice *invoice)
844 : : {
845 : 97 : if (!invoice) return INT64_MAX;
846 : 97 : 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 : 30 : GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice)
859 : : {
860 : 30 : if (!invoice) return NULL;
861 : 30 : 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 : 48 : const char * gncInvoiceGetDocLink (const GncInvoice *invoice)
877 : : {
878 : 48 : if (!invoice) return NULL;
879 : :
880 : 48 : GValue v = G_VALUE_INIT;
881 : 48 : qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_DOCLINK);
882 : 48 : const char *rv = G_VALUE_HOLDS_STRING(&v) ? g_value_get_string (&v) : NULL;
883 : 48 : g_value_unset (&v);
884 : :
885 : 48 : return rv;
886 : : }
887 : :
888 : 678 : GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice)
889 : : {
890 : : const GncOwner *owner;
891 : 678 : g_return_val_if_fail (invoice, GNC_OWNER_NONE);
892 : :
893 : 678 : owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
894 : 678 : return (gncOwnerGetType (owner));
895 : : }
896 : :
897 : 209 : static gnc_numeric gncInvoiceSumTaxesInternal (AccountValueList *taxes)
898 : : {
899 : 209 : gnc_numeric tt = gnc_numeric_zero ();
900 : :
901 : 209 : 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 : 32 : for (node = taxes; node; node=node->next)
908 : : {
909 : 16 : GncAccountValue *acc_val = node->data;
910 : 16 : 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 : 209 : return tt;
915 : : }
916 : :
917 : 353 : 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 : 353 : gnc_numeric net_total = gnc_numeric_zero ();
924 : : gboolean is_cust_doc, is_cn;
925 : 353 : AccountValueList *tv_list = NULL;
926 : 353 : int denom = gnc_commodity_get_fraction (gncInvoiceGetCurrency (invoice));
927 : :
928 : 353 : g_return_val_if_fail (invoice, net_total);
929 : :
930 : 353 : 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 : 353 : is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
935 : 353 : is_cn = gncInvoiceGetIsCreditNote (invoice);
936 : :
937 : :
938 : 893 : for (node = gncInvoiceGetEntries (invoice); node; node = node->next)
939 : : {
940 : 540 : GncEntry *entry = node->data;
941 : : gnc_numeric value;
942 : :
943 : 540 : if (use_payment_type && gncEntryGetBillPayment (entry) != type)
944 : 0 : continue;
945 : :
946 : 540 : 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 : 362 : value = gncEntryGetDocValue (entry, TRUE, is_cust_doc, is_cn);
951 : 362 : if (gnc_numeric_check (value) == GNC_ERROR_OK)
952 : 362 : 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 : 540 : if (taxes)
958 : : {
959 : 456 : AccountValueList *entrytaxes = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
960 : 456 : tv_list = gncAccountValueAddList (tv_list, entrytaxes);
961 : 456 : gncAccountValueDestroy (entrytaxes);
962 : : }
963 : : }
964 : :
965 : 353 : 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 : 320 : for (node = tv_list; node; node=node->next)
971 : : {
972 : 23 : GncAccountValue *acc_val = node->data;
973 : 23 : acc_val->value = gnc_numeric_convert (acc_val->value,
974 : : denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
975 : : }
976 : 297 : *taxes = tv_list;
977 : : }
978 : :
979 : 353 : LEAVE ("%" PRId64 "/%" PRId64, net_total.num, net_total.denom);
980 : 353 : return net_total;
981 : : }
982 : :
983 : 265 : 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 : 265 : if (!invoice) return gnc_numeric_zero ();
991 : :
992 : 265 : ENTER ("");
993 : 265 : total = gncInvoiceGetNetAndTaxesInternal (invoice, use_value, use_tax? &taxes : NULL, use_payment_type, type);
994 : :
995 : 265 : 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 : 209 : total = gnc_numeric_add (total, gncInvoiceSumTaxesInternal (taxes),
1001 : : GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1002 : 209 : gncAccountValueDestroy (taxes);
1003 : : }
1004 : 265 : LEAVE ("%" PRId64 "/%" PRId64, total.num, total.denom);
1005 : 265 : return total;
1006 : : }
1007 : :
1008 : 174 : gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice)
1009 : : {
1010 : 174 : if (!invoice) return gnc_numeric_zero ();
1011 : 174 : 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 : 88 : AccountValueList *gncInvoiceGetTotalTaxList (GncInvoice *invoice)
1033 : : {
1034 : : AccountValueList *taxes;
1035 : 88 : if (!invoice) return NULL;
1036 : :
1037 : 88 : gncInvoiceGetNetAndTaxesInternal (invoice, FALSE, &taxes, FALSE, 0);
1038 : 88 : 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 : 182 : GncInvoiceType gncInvoiceGetType (const GncInvoice *invoice)
1066 : : {
1067 : 182 : if (!invoice) return GNC_INVOICE_UNDEFINED;
1068 : 182 : switch (gncInvoiceGetOwnerType (invoice))
1069 : : {
1070 : 126 : case GNC_OWNER_CUSTOMER:
1071 : 126 : return (gncInvoiceGetIsCreditNote (invoice) ?
1072 : 126 : GNC_INVOICE_CUST_CREDIT_NOTE :
1073 : : GNC_INVOICE_CUST_INVOICE);
1074 : 28 : case GNC_OWNER_VENDOR:
1075 : 28 : return (gncInvoiceGetIsCreditNote (invoice) ?
1076 : 28 : GNC_INVOICE_VEND_CREDIT_NOTE :
1077 : : GNC_INVOICE_VEND_INVOICE);
1078 : 28 : case GNC_OWNER_EMPLOYEE:
1079 : 28 : return (gncInvoiceGetIsCreditNote (invoice) ?
1080 : 28 : 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 : 128 : const char * gncInvoiceGetTypeString (const GncInvoice *invoice)
1090 : : {
1091 : 128 : GncInvoiceType type = gncInvoiceGetType (invoice);
1092 : 128 : switch (type)
1093 : : {
1094 : 79 : case GNC_INVOICE_CUST_INVOICE:
1095 : 79 : return _("Invoice");
1096 : 8 : case GNC_INVOICE_VEND_INVOICE:
1097 : 8 : return _("Bill");
1098 : 8 : case GNC_INVOICE_EMPL_INVOICE:
1099 : 8 : return _("Expense");
1100 : 33 : case GNC_INVOICE_CUST_CREDIT_NOTE:
1101 : : case GNC_INVOICE_VEND_CREDIT_NOTE:
1102 : : case GNC_INVOICE_EMPL_CREDIT_NOTE:
1103 : 33 : return _("Credit Note");
1104 : 0 : default:
1105 : 0 : PWARN("Unknown invoice type");
1106 : 0 : return NULL;
1107 : : }
1108 : : }
1109 : :
1110 : 580 : gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice)
1111 : : {
1112 : 580 : if (!invoice) return NULL;
1113 : 580 : return invoice->currency;
1114 : : }
1115 : :
1116 : 903 : GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice)
1117 : : {
1118 : 903 : if (!invoice) return NULL;
1119 : 105 : return &invoice->billto;
1120 : : }
1121 : :
1122 : 113 : GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice)
1123 : : {
1124 : 113 : if (!invoice) return NULL;
1125 : 113 : return invoice->posted_lot;
1126 : : }
1127 : :
1128 : 299 : Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice)
1129 : : {
1130 : 299 : if (!invoice) return NULL;
1131 : 293 : 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 : 648 : gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice)
1147 : : {
1148 : 648 : GValue v = G_VALUE_INIT;
1149 : : gboolean retval;
1150 : 648 : if (!invoice) return FALSE;
1151 : 648 : qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_IS_CN);
1152 : 648 : retval = G_VALUE_HOLDS_INT64(&v) && g_value_get_int64 (&v);
1153 : 648 : g_value_unset (&v);
1154 : 648 : 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 : 484 : EntryList * gncInvoiceGetEntries (GncInvoice *invoice)
1165 : : {
1166 : 484 : if (!invoice) return NULL;
1167 : 484 : 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 QofCollection*
1188 : 0 : qofInvoiceGetEntries (GncInvoice *invoice)
1189 : : {
1190 : : QofCollection *entry_coll;
1191 : : GList *list;
1192 : : QofInstance *entry;
1193 : :
1194 : 0 : entry_coll = qof_collection_new (GNC_ID_ENTRY);
1195 : 0 : for (list = gncInvoiceGetEntries (invoice); list != NULL; list = list->next)
1196 : : {
1197 : 0 : entry = QOF_INSTANCE(list->data);
1198 : 0 : qof_collection_add_entity (entry_coll, entry);
1199 : : }
1200 : 0 : return entry_coll;
1201 : : }
1202 : :
1203 : : static void
1204 : 0 : qofInvoiceEntryCB (QofInstance *ent, gpointer user_data)
1205 : : {
1206 : : GncInvoice *invoice;
1207 : :
1208 : 0 : invoice = (GncInvoice*)user_data;
1209 : 0 : if (!invoice || !ent)
1210 : : {
1211 : 0 : return;
1212 : : }
1213 : 0 : switch (gncInvoiceGetOwnerType (invoice))
1214 : : {
1215 : 0 : case GNC_OWNER_VENDOR:
1216 : : {
1217 : 0 : gncBillAddEntry (invoice, (GncEntry*) ent);
1218 : 0 : break;
1219 : : }
1220 : 0 : default :
1221 : : {
1222 : 0 : gncInvoiceAddEntry (invoice, (GncEntry*)ent);
1223 : 0 : break;
1224 : : }
1225 : : }
1226 : : }
1227 : :
1228 : : static void
1229 : 0 : qofInvoiceSetEntries (GncInvoice *invoice, QofCollection *entry_coll)
1230 : : {
1231 : 0 : if (!entry_coll)
1232 : : {
1233 : 0 : return;
1234 : : }
1235 : 0 : if (0 == g_strcmp0 (qof_collection_get_type (entry_coll), GNC_ID_ENTRY))
1236 : : {
1237 : 0 : qof_collection_foreach (entry_coll, qofInvoiceEntryCB, invoice);
1238 : : }
1239 : : }
1240 : :
1241 : : static GncJob*
1242 : 0 : qofInvoiceGetJob (const GncInvoice *invoice)
1243 : : {
1244 : 0 : if (!invoice)
1245 : : {
1246 : 0 : return NULL;
1247 : : }
1248 : 0 : return invoice->job;
1249 : : }
1250 : :
1251 : : static void
1252 : 0 : qofInvoiceSetJob (GncInvoice *invoice, GncJob *job)
1253 : : {
1254 : 0 : if (!invoice)
1255 : : {
1256 : 0 : return;
1257 : : }
1258 : 0 : invoice->job = job;
1259 : : }
1260 : :
1261 : : void
1262 : 8 : gncInvoiceDetachFromLot (GNCLot *lot)
1263 : : {
1264 : 8 : if (!lot) return;
1265 : :
1266 : 8 : gnc_lot_begin_edit (lot);
1267 : 8 : qof_instance_set (QOF_INSTANCE (lot), "invoice", NULL, NULL);
1268 : 8 : gnc_lot_commit_edit (lot);
1269 : 8 : gnc_lot_set_cached_invoice (lot, NULL);
1270 : : }
1271 : :
1272 : : void
1273 : 64 : gncInvoiceAttachToLot (GncInvoice *invoice, GNCLot *lot)
1274 : : {
1275 : : GncGUID *guid;
1276 : 64 : if (!invoice || !lot)
1277 : 0 : return;
1278 : :
1279 : 64 : if (invoice->posted_lot) return; /* Cannot reset invoice's lot */
1280 : 64 : guid = (GncGUID*)qof_instance_get_guid (QOF_INSTANCE(invoice));
1281 : 64 : gnc_lot_begin_edit (lot);
1282 : 64 : qof_instance_set (QOF_INSTANCE (lot), "invoice", guid, NULL);
1283 : 64 : gnc_lot_commit_edit (lot);
1284 : 64 : gnc_lot_set_cached_invoice (lot, invoice);
1285 : 64 : gncInvoiceSetPostedLot (invoice, lot);
1286 : : }
1287 : :
1288 : 1406 : GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot)
1289 : : {
1290 : 1406 : GncGUID *guid = NULL;
1291 : : QofBook *book;
1292 : 1406 : GncInvoice *invoice = NULL;
1293 : :
1294 : 1406 : if (!lot) return NULL;
1295 : :
1296 : 1376 : invoice = gnc_lot_get_cached_invoice (lot);
1297 : 1376 : if (!invoice)
1298 : : {
1299 : 528 : book = gnc_lot_get_book (lot);
1300 : 528 : qof_instance_get (QOF_INSTANCE(lot), "invoice", &guid, NULL);
1301 : 528 : invoice = gncInvoiceLookup (book, guid);
1302 : 528 : guid_free (guid);
1303 : 528 : gnc_lot_set_cached_invoice (lot, invoice);
1304 : : }
1305 : :
1306 : 1376 : return invoice;
1307 : : }
1308 : :
1309 : : void
1310 : 64 : gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn)
1311 : : {
1312 : 64 : if (!invoice || !txn)
1313 : 0 : return;
1314 : :
1315 : 64 : if (invoice->posted_txn) return; /* Cannot reset invoice's txn */
1316 : :
1317 : 64 : xaccTransBeginEdit (txn);
1318 : 64 : qof_instance_set (QOF_INSTANCE (txn), "invoice", //Prop INVOICE
1319 : 64 : qof_instance_get_guid (QOF_INSTANCE (invoice)), NULL);
1320 : 64 : xaccTransSetTxnType (txn, TXN_TYPE_INVOICE);
1321 : 64 : xaccTransCommitEdit (txn);
1322 : 64 : gncInvoiceSetPostedTxn (invoice, txn);
1323 : : }
1324 : :
1325 : : GncInvoice *
1326 : 1978 : gncInvoiceGetInvoiceFromTxn (const Transaction *txn)
1327 : : {
1328 : 1978 : GncGUID *guid = NULL;
1329 : : QofBook *book;
1330 : 1978 : GncInvoice *invoice = NULL;
1331 : :
1332 : 1978 : if (!txn) return NULL;
1333 : :
1334 : 1978 : book = xaccTransGetBook (txn);
1335 : 1978 : qof_instance_get (QOF_INSTANCE (txn), "invoice", &guid, NULL);
1336 : 1978 : invoice = gncInvoiceLookup (book, guid);
1337 : 1978 : guid_free (guid);
1338 : 1978 : return invoice;
1339 : : }
1340 : :
1341 : 27 : gboolean gncInvoiceAmountPositive (const GncInvoice *invoice)
1342 : : {
1343 : 27 : switch (gncInvoiceGetType (invoice))
1344 : : {
1345 : 18 : case GNC_INVOICE_CUST_INVOICE:
1346 : : case GNC_INVOICE_VEND_CREDIT_NOTE:
1347 : : case GNC_INVOICE_EMPL_CREDIT_NOTE:
1348 : 18 : return TRUE;
1349 : 9 : case GNC_INVOICE_CUST_CREDIT_NOTE:
1350 : : case GNC_INVOICE_VEND_INVOICE:
1351 : : case GNC_INVOICE_EMPL_INVOICE:
1352 : 9 : return FALSE;
1353 : 0 : case GNC_INVOICE_UNDEFINED:
1354 : : default:
1355 : : /* Should never be reached.
1356 : : * If it is, perhaps a new value is added to GncInvoiceType ? */
1357 : 0 : g_assert_not_reached ();
1358 : : return FALSE;
1359 : : }
1360 : : }
1361 : :
1362 : 0 : GHashTable *gncInvoiceGetForeignCurrencies (const GncInvoice *invoice)
1363 : : {
1364 : : EntryList *entries_iter;
1365 : 0 : gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
1366 : 0 : gboolean is_cn = gncInvoiceGetIsCreditNote (invoice);
1367 : 0 : GHashTable *amt_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1368 : : NULL, g_free);
1369 : 0 : ENTER ("");
1370 : :
1371 : 0 : for (entries_iter = invoice->entries; entries_iter != NULL; entries_iter = g_list_next(entries_iter))
1372 : : {
1373 : 0 : GncEntry *entry = (GncEntry*)entries_iter->data;
1374 : : Account *this_acc;
1375 : : gnc_commodity *account_currency;
1376 : 0 : AccountValueList *tt_amts = NULL, *tt_iter;
1377 : :
1378 : : /* Check entry's account currency */
1379 : 0 : this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
1380 : 0 : gncEntryGetBillAccount (entry));
1381 : 0 : account_currency = xaccAccountGetCommodity (this_acc);
1382 : :
1383 : 0 : if (this_acc &&
1384 : 0 : !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), account_currency))
1385 : : {
1386 : 0 : gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, account_currency);
1387 : 0 : gnc_numeric *entry_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
1388 : 0 : *entry_amt = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
1389 : 0 : if (curr_amt)
1390 : 0 : *entry_amt = gnc_numeric_add (*entry_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
1391 : 0 : g_hash_table_insert (amt_hash, account_currency, entry_amt);
1392 : : }
1393 : :
1394 : : /* Check currencies of each account in the tax table linked
1395 : : * to the current entry */
1396 : 0 : tt_amts = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
1397 : :
1398 : 0 : if (!tt_amts)
1399 : 0 : continue;
1400 : :
1401 : 0 : for (tt_iter = tt_amts; tt_iter != NULL; tt_iter = g_list_next(tt_iter))
1402 : : {
1403 : 0 : GncAccountValue *tt_amt_val = (GncAccountValue*)tt_iter->data;
1404 : 0 : Account *tt_acc = tt_amt_val->account;
1405 : 0 : gnc_commodity *tt_acc_currency = xaccAccountGetCommodity (tt_acc);
1406 : :
1407 : 0 : if (tt_acc &&
1408 : 0 : !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), tt_acc_currency))
1409 : : {
1410 : 0 : gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, tt_acc_currency);
1411 : 0 : gnc_numeric *tt_acc_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
1412 : 0 : *tt_acc_amt = tt_amt_val->value;
1413 : 0 : if (curr_amt)
1414 : 0 : *tt_acc_amt = gnc_numeric_add (*tt_acc_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
1415 : 0 : g_hash_table_insert (amt_hash, tt_acc_currency, tt_acc_amt);
1416 : : }
1417 : : }
1418 : 0 : gncAccountValueDestroy (tt_amts);
1419 : : }
1420 : :
1421 : 0 : LEAVE ("");
1422 : 0 : return amt_hash;
1423 : : }
1424 : :
1425 : 66 : static gboolean gncInvoicePostAddSplit (QofBook *book,
1426 : : Account *acc,
1427 : : Transaction *txn,
1428 : : gnc_numeric value,
1429 : : const gchar *memo,
1430 : : const gchar *type,
1431 : : GncInvoice *invoice)
1432 : : {
1433 : : Split *split;
1434 : :
1435 : 66 : ENTER ("");
1436 : 66 : split = xaccMallocSplit (book);
1437 : : /* set action and memo? */
1438 : :
1439 : 66 : xaccSplitSetMemo (split, memo);
1440 : : /* set per book option */
1441 : 66 : gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1442 : :
1443 : : /* Need to insert this split into the account AND txn before
1444 : : * we set the Base Value. Otherwise SetBaseValue complains
1445 : : * that we don't have an account and fails to set the value.
1446 : : */
1447 : 66 : xaccAccountBeginEdit (acc);
1448 : 66 : xaccAccountInsertSplit (acc, split);
1449 : 66 : xaccAccountCommitEdit (acc);
1450 : 66 : xaccTransAppendSplit (txn, split);
1451 : :
1452 : : /* General note on the split creations below:
1453 : : * Invoice and bill amounts are always stored as positive values in entries
1454 : : * So to convert them to proper splits, the amounts may have to be reverted
1455 : : * to have the proper effect on the account balance.
1456 : : * Credit notes have the opposite effect of invoices/bills, but their amounts
1457 : : * are stored as negative values as well. So to convert them into splits
1458 : : * they can be treated exactly the same as their invoice/bill counter parts.
1459 : : * The net effect is that the owner type is sufficient to determine whether a
1460 : : * value has to be reverted when converting an invoice/bill/cn amount to a split.
1461 : : */
1462 : 66 : if (gnc_commodity_equal (xaccAccountGetCommodity (acc), invoice->currency))
1463 : : {
1464 : 66 : xaccSplitSetBaseValue (split, value,
1465 : 66 : invoice->currency);
1466 : : }
1467 : : else
1468 : : {
1469 : : /*need to do conversion */
1470 : 0 : GNCPrice *price = gncInvoiceGetPrice (invoice, xaccAccountGetCommodity (acc));
1471 : :
1472 : 0 : if (price == NULL)
1473 : : {
1474 : : /*This is an error, which shouldn't even be able to happen.
1475 : : We can't really do anything sensible about it, and this is
1476 : : a user-interface free zone so we can't try asking the user
1477 : : again either, have to return NULL*/
1478 : 0 : PERR("Multiple commodities with no price.");
1479 : 0 : LEAVE ("FALSE");
1480 : 0 : return FALSE;
1481 : : }
1482 : : else
1483 : : {
1484 : : gnc_numeric converted_amount;
1485 : 0 : xaccSplitSetValue (split, value);
1486 : 0 : converted_amount = gnc_numeric_div (value, gnc_price_get_value (price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
1487 : 0 : DEBUG("converting from %f to %f\n", gnc_numeric_to_double (value), gnc_numeric_to_double (converted_amount));
1488 : 0 : xaccSplitSetAmount (split, converted_amount);
1489 : : }
1490 : : }
1491 : :
1492 : 66 : LEAVE ("TRUE");
1493 : 66 : return TRUE;
1494 : : }
1495 : :
1496 : 64 : Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
1497 : : time64 post_date, time64 due_date,
1498 : : const char * memo, gboolean accumulatesplits,
1499 : : gboolean autopay)
1500 : : {
1501 : : Transaction *txn;
1502 : : QofBook *book;
1503 : 64 : GNCLot *lot = NULL;
1504 : : GList *iter;
1505 : 64 : GList *splitinfo = NULL;
1506 : : gnc_numeric total;
1507 : : gboolean is_cust_doc;
1508 : : gboolean is_cn;
1509 : : const char *name, *type;
1510 : : char *lot_title;
1511 : 64 : Account *ccard_acct = NULL;
1512 : : const GncOwner *owner;
1513 : 64 : int denom = xaccAccountGetCommoditySCU (acc);
1514 : : AccountValueList *taxes;
1515 : :
1516 : 64 : if (!invoice || !acc) return NULL;
1517 : 64 : if (gncInvoiceIsPosted (invoice)) return NULL;
1518 : :
1519 : 64 : ENTER ("");
1520 : 64 : gncInvoiceBeginEdit (invoice);
1521 : 64 : book = qof_instance_get_book (invoice);
1522 : :
1523 : : /* Stabilize the Billing Terms of this invoice */
1524 : 64 : if (invoice->terms)
1525 : 4 : gncInvoiceSetTerms (invoice,
1526 : : gncBillTermReturnChild (invoice->terms, TRUE));
1527 : :
1528 : : /* GncEntry functions need to know if the invoice/credit note is for a customer or a vendor/employee. */
1529 : 64 : is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
1530 : 64 : is_cn = gncInvoiceGetIsCreditNote (invoice);
1531 : :
1532 : : /* Figure out if we need to separate out "credit-card" items */
1533 : 64 : owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
1534 : 64 : if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE)
1535 : 8 : ccard_acct = gncEmployeeGetCCard (gncOwnerGetEmployee (owner));
1536 : :
1537 : : /* Create a new lot for this invoice */
1538 : 64 : lot = gnc_lot_new (book);
1539 : 64 : gncInvoiceAttachToLot (invoice, lot);
1540 : 64 : gnc_lot_begin_edit (lot);
1541 : :
1542 : 64 : type = gncInvoiceGetTypeString (invoice);
1543 : :
1544 : : /* Set the lot title */
1545 : 64 : lot_title = g_strdup_printf ("%s %s", type, gncInvoiceGetID (invoice));
1546 : 64 : gnc_lot_set_title (lot, lot_title);
1547 : 64 : g_free (lot_title);
1548 : :
1549 : : /* Create a new transaction */
1550 : 64 : txn = xaccMallocTransaction (book);
1551 : 64 : xaccTransBeginEdit (txn);
1552 : :
1553 : 64 : name = gncOwnerGetName (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)));
1554 : :
1555 : : /* Set Transaction Description (Owner Name) , Num (invoice ID or type, based
1556 : : * on book option), Currency */
1557 : 64 : xaccTransSetDescription (txn, name ? name : "");
1558 : 64 : gnc_set_num_action (txn, NULL, gncInvoiceGetID (invoice), type);
1559 : 64 : xaccTransSetCurrency (txn, invoice->currency);
1560 : :
1561 : : /* Entered and Posted at date */
1562 : 64 : xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
1563 : 64 : xaccTransSetDatePostedSecsNormalized (txn, post_date);
1564 : 64 : gncInvoiceSetDatePosted (invoice, xaccTransRetDatePosted(txn));
1565 : :
1566 : 64 : xaccTransSetDateDue (txn, due_date);
1567 : :
1568 : : /* Get invoice total and taxes. */
1569 : 64 : total = gncInvoiceGetTotal (invoice);
1570 : 64 : taxes = gncInvoiceGetTotalTaxList (invoice);
1571 : : /* The two functions above return signs relative to the document
1572 : : * We need to convert them to balance values before we can use them here
1573 : : * Note the odd construct comparing two booleans is to xor them
1574 : : * that is, only evaluate true if both are different.
1575 : : */
1576 : 64 : if (is_cust_doc != is_cn)
1577 : : {
1578 : : GList *node;
1579 : 50 : total = gnc_numeric_neg (total);
1580 : 54 : for (node = taxes; node; node = node->next)
1581 : : {
1582 : 4 : GncAccountValue *acc_val = node->data;
1583 : 4 : acc_val->value = gnc_numeric_neg (acc_val->value);
1584 : : }
1585 : : }
1586 : :
1587 : : /* Iterate through the entries; sum up everything for each account.
1588 : : * then create the appropriate splits in this txn.
1589 : : */
1590 : :
1591 : 155 : for (iter = gncInvoiceGetEntries (invoice); iter; iter = iter->next)
1592 : : {
1593 : : gnc_numeric value, tax;
1594 : 91 : GncEntry * entry = iter->data;
1595 : : Account *this_acc;
1596 : :
1597 : : /* Stabilize the TaxTable in this entry */
1598 : 91 : gncEntryBeginEdit (entry);
1599 : 91 : if (is_cust_doc)
1600 : 75 : gncEntrySetInvTaxTable
1601 : : (entry, gncTaxTableReturnChild (gncEntryGetInvTaxTable (entry), TRUE));
1602 : : else
1603 : : {
1604 : 16 : gncEntrySetBillTaxTable
1605 : : (entry, gncTaxTableReturnChild (gncEntryGetBillTaxTable (entry), TRUE));
1606 : :
1607 : : /* If this is a bill, and the entry came from an invoice originally, copy the price */
1608 : 16 : if (gncEntryGetBillable (entry))
1609 : : {
1610 : : /* We need to set the net price since it may be another tax rate for invoices than bills */
1611 : 0 : gncEntrySetInvPrice (entry, gncEntryGetPrice (entry, FALSE, TRUE));
1612 : 0 : gncEntrySetInvTaxIncluded (entry, FALSE);
1613 : : }
1614 : : }
1615 : 91 : gncEntryCommitEdit (entry);
1616 : :
1617 : : /* Obtain the Entry's Value and TaxValues
1618 : : Note we use rounded values here and below to prevent creating an imbalanced transaction */
1619 : 91 : value = gncEntryGetBalValue (entry, TRUE, is_cust_doc);
1620 : 91 : tax = gncEntryGetBalTaxValue (entry, TRUE, is_cust_doc);
1621 : :
1622 : 91 : DEBUG ("Tax %" PRId64 "/%" PRId64 " on entry value %" PRId64 "/%" PRId64,
1623 : : tax.num, tax.denom, value.num, value.denom);
1624 : : /* add the value for the account split */
1625 : 91 : this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
1626 : 16 : gncEntryGetBillAccount (entry));
1627 : 91 : if (this_acc)
1628 : : {
1629 : 90 : if (gnc_numeric_check (value) == GNC_ERROR_OK)
1630 : : {
1631 : 90 : if (accumulatesplits)
1632 : 90 : splitinfo = gncAccountValueAdd (splitinfo, this_acc, value);
1633 : : /* Adding to total in case of accumulatesplits will be deferred to later when each split is effectively added */
1634 : 0 : else if (!gncInvoicePostAddSplit (book, this_acc, txn, value,
1635 : 0 : gncEntryGetDescription (entry),
1636 : : type, invoice))
1637 : : {
1638 : : /*This is an error, which shouldn't even be able to happen.
1639 : : We can't really do anything sensible about it, and this is
1640 : : a user-interface free zone so we can't try asking the user
1641 : : again either, have to return NULL*/
1642 : 0 : PERR("Failed to add split %s", gncEntryGetDescription (entry));
1643 : 0 : LEAVE ("NULL");
1644 : 0 : return NULL;
1645 : : }
1646 : :
1647 : : /* If there is a credit-card account, and this is a CCard
1648 : : * payment type, subtract it from the total, and instead
1649 : : * create a split to the CC Acct with a memo of the entry
1650 : : * description instead of the provided memo. Note that the
1651 : : * value reversal is the same as the post account.
1652 : : *
1653 : : * Note: we don't have to worry about the tax values --
1654 : : * expense vouchers don't have them.
1655 : : */
1656 : 90 : if (ccard_acct && gncEntryGetBillPayment (entry) == GNC_PAYMENT_CARD)
1657 : : {
1658 : : Split *split;
1659 : :
1660 : 0 : total = gnc_numeric_sub (total, value, denom,
1661 : : GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1662 : :
1663 : 0 : split = xaccMallocSplit (book);
1664 : 0 : xaccSplitSetMemo (split, gncEntryGetDescription (entry));
1665 : : /* set action based on book option */
1666 : 0 : gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1667 : 0 : xaccAccountBeginEdit (ccard_acct);
1668 : 0 : xaccAccountInsertSplit (ccard_acct, split);
1669 : 0 : xaccAccountCommitEdit (ccard_acct);
1670 : 0 : xaccTransAppendSplit (txn, split);
1671 : 0 : xaccSplitSetBaseValue (split, gnc_numeric_neg (value),
1672 : 0 : invoice->currency);
1673 : :
1674 : : }
1675 : :
1676 : : }
1677 : : else
1678 : 0 : PWARN ("bad value in our entry");
1679 : : }
1680 : :
1681 : : /* check the taxes */
1682 : 91 : if (gnc_numeric_check (tax) != GNC_ERROR_OK)
1683 : 0 : PWARN ("bad tax in our entry");
1684 : :
1685 : : } /* for */
1686 : :
1687 : :
1688 : : /* now merge in the TaxValues */
1689 : 64 : splitinfo = gncAccountValueAddList (splitinfo, taxes);
1690 : 64 : gncAccountValueDestroy (taxes);
1691 : :
1692 : : /* Iterate through the splitinfo list and generate the splits */
1693 : 64 : if (splitinfo)
1694 : 62 : PINFO ("Processing Split List");
1695 : 130 : for (iter = splitinfo; iter; iter = iter->next)
1696 : : {
1697 : 66 : GncAccountValue *acc_val = iter->data;
1698 : :
1699 : : //gnc_numeric amt_rounded = gnc_numeric_convert(acc_val->value,
1700 : : // denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1701 : 66 : if (!gncInvoicePostAddSplit (book, acc_val->account, txn, acc_val->value,
1702 : : memo, type, invoice))
1703 : : {
1704 : : /*This is an error, which shouldn't even be able to happen.
1705 : : We can't really do anything sensible about it, and this is
1706 : : a user-interface free zone so we can't try asking the user
1707 : : again either, have to return NULL*/
1708 : 0 : PERR("Failed to add split %s, aborting accumulated splits.", memo);
1709 : 0 : return NULL;
1710 : : }
1711 : : }
1712 : :
1713 : : /* If there is a ccard account, we may have an additional "to_card" payment.
1714 : : * we should make that now.
1715 : : */
1716 : 64 : if (ccard_acct && !gnc_numeric_zero_p (invoice->to_charge_amount))
1717 : : {
1718 : 0 : Split *split = xaccMallocSplit (book);
1719 : :
1720 : : /* To charge amount is stored in document value. We need balance value here
1721 : : * so convert if necessary. */
1722 : 0 : gnc_numeric to_charge_bal_amount = (is_cn ? gnc_numeric_neg (invoice->to_charge_amount)
1723 : 0 : : invoice->to_charge_amount);
1724 : :
1725 : 0 : PINFO ("Process to_card payment split");
1726 : : /* Set memo. */
1727 : 0 : xaccSplitSetMemo (split, _("Extra to Charge Card"));
1728 : : /* Set action based on book option */
1729 : 0 : gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1730 : :
1731 : 0 : xaccAccountBeginEdit (ccard_acct);
1732 : 0 : xaccAccountInsertSplit (ccard_acct, split);
1733 : 0 : xaccAccountCommitEdit (ccard_acct);
1734 : 0 : xaccTransAppendSplit (txn, split);
1735 : 0 : xaccSplitSetBaseValue (split, gnc_numeric_neg (to_charge_bal_amount),
1736 : 0 : invoice->currency);
1737 : :
1738 : 0 : total = gnc_numeric_sub (total, to_charge_bal_amount, denom,
1739 : : GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1740 : : }
1741 : :
1742 : : /* Now create the Posted split (which is the opposite sign of the above splits) */
1743 : : {
1744 : 64 : Split *split = xaccMallocSplit (book);
1745 : :
1746 : 64 : PINFO ("Process to_card balancing split");
1747 : : /* Set memo */
1748 : 64 : xaccSplitSetMemo (split, memo);
1749 : : /* Set action based on book option */
1750 : 64 : gnc_set_num_action (NULL, split, gncInvoiceGetID (invoice), type);
1751 : :
1752 : 64 : xaccAccountBeginEdit (acc);
1753 : 64 : xaccAccountInsertSplit (acc, split);
1754 : 64 : xaccAccountCommitEdit (acc);
1755 : 64 : xaccTransAppendSplit (txn, split);
1756 : 64 : xaccSplitSetBaseValue (split, gnc_numeric_neg (total),
1757 : 64 : invoice->currency);
1758 : :
1759 : : /* add this split to the lot */
1760 : 64 : gnc_lot_add_split (lot, split);
1761 : : }
1762 : :
1763 : : /* Now attach this invoice to the txn and account */
1764 : 64 : gncInvoiceAttachToTxn (invoice, txn);
1765 : 64 : gncInvoiceSetPostedAcc (invoice, acc);
1766 : :
1767 : 64 : xaccTransSetReadOnly (txn, _("Generated from an invoice. Try unposting the invoice."));
1768 : 64 : xaccTransCommitEdit (txn);
1769 : :
1770 : 64 : gncAccountValueDestroy (splitinfo);
1771 : :
1772 : 64 : gnc_lot_commit_edit (lot);
1773 : : /* Not strictly necessary, since it was done by the Set calls
1774 : : * above, but good insurance. */
1775 : 64 : DEBUG("Committing Invoice %s", invoice->id);
1776 : 64 : mark_invoice (invoice);
1777 : 64 : gncInvoiceCommitEdit (invoice);
1778 : :
1779 : : /* If requested, attempt to automatically apply open payments
1780 : : * and reverse documents to this lot to close it (or at least
1781 : : * reduce its balance) */
1782 : 64 : if (autopay)
1783 : 0 : gncInvoiceAutoApplyPayments (invoice);
1784 : :
1785 : 64 : LEAVE ("");
1786 : 64 : return txn;
1787 : : }
1788 : :
1789 : : gboolean
1790 : 8 : gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables)
1791 : : {
1792 : : Transaction *txn;
1793 : : GNCLot *lot;
1794 : : GList *lot_split_list, *lot_split_iter;
1795 : :
1796 : 8 : if (!invoice) return FALSE;
1797 : 8 : if (!gncInvoiceIsPosted (invoice)) return FALSE;
1798 : :
1799 : 8 : txn = gncInvoiceGetPostedTxn (invoice);
1800 : 8 : g_return_val_if_fail (txn, FALSE);
1801 : :
1802 : 8 : lot = gncInvoiceGetPostedLot (invoice);
1803 : 8 : g_return_val_if_fail (lot, FALSE);
1804 : :
1805 : 8 : ENTER ("");
1806 : : /* Destroy the Posted Transaction */
1807 : 8 : xaccTransClearReadOnly (txn);
1808 : 8 : xaccTransBeginEdit (txn);
1809 : 8 : xaccTransDestroy (txn);
1810 : 8 : xaccTransCommitEdit (txn);
1811 : :
1812 : : /* Disconnect the lot from the invoice; re-attach to the invoice owner */
1813 : 8 : gncInvoiceDetachFromLot (lot);
1814 : 8 : gncOwnerAttachToLot (&invoice->owner, lot);
1815 : :
1816 : : /* Check if this invoice was linked to other lots (payments/inverse signed
1817 : : * invoices).
1818 : : * If this is the case, recreate the link transaction between all the remaining lots.
1819 : : *
1820 : : * Note that before GnuCash 2.6 payments were not stored in separate lots, but
1821 : : * always ended up in invoice lots when matched to an invoice. Over-payments
1822 : : * were copied to a new lot, to which later an invoice was added again and so on.
1823 : : * These over-payments were handled with automatic payment forward transactions.
1824 : : * You could consider these transactions to be links between lots as well, but
1825 : : * to avoid some unexpected behavior, these will not be altered here.
1826 : : */
1827 : :
1828 : : // Note: make a copy of the lot list here, when splits are deleted from the lot,
1829 : : // the original list may be destroyed by the lot code.
1830 : 8 : lot_split_list = g_list_copy (gnc_lot_get_split_list (lot));
1831 : 8 : if (lot_split_list)
1832 : 0 : PINFO ("Recreating link transactions for remaining lots");
1833 : 8 : for (lot_split_iter = lot_split_list; lot_split_iter; lot_split_iter = lot_split_iter->next)
1834 : : {
1835 : 0 : Split *split = lot_split_iter->data;
1836 : : GList *other_split_list, *list_iter;
1837 : 0 : Transaction *other_txn = xaccSplitGetParent (split);
1838 : 0 : GList *lot_list = NULL;
1839 : :
1840 : : /* Only work with transactions that link invoices and payments.
1841 : : * Note: this check also catches the possible case of NULL splits. */
1842 : 0 : if (xaccTransGetTxnType (other_txn) != TXN_TYPE_LINK)
1843 : 0 : continue;
1844 : :
1845 : : /* Save a list of lots this linking transaction linked to */
1846 : 0 : other_split_list = xaccTransGetSplitList (other_txn);
1847 : 0 : for (list_iter = other_split_list; list_iter; list_iter = list_iter->next)
1848 : : {
1849 : 0 : Split *other_split = list_iter->data;
1850 : 0 : GNCLot *other_lot = xaccSplitGetLot (other_split);
1851 : :
1852 : : /* Omit the lot we are about to delete */
1853 : 0 : if (other_lot == lot)
1854 : 0 : continue;
1855 : :
1856 : 0 : lot_list = g_list_prepend (lot_list, other_lot);
1857 : : }
1858 : : /* Maintain original split order */
1859 : 0 : lot_list = g_list_reverse (lot_list);
1860 : :
1861 : : /* Now remove this link transaction. */
1862 : 0 : xaccTransClearReadOnly (other_txn);
1863 : 0 : xaccTransBeginEdit (other_txn);
1864 : 0 : xaccTransDestroy (other_txn);
1865 : 0 : xaccTransCommitEdit (other_txn);
1866 : :
1867 : : /* Re-balance the saved lots as well as is possible */
1868 : 0 : gncOwnerAutoApplyPaymentsWithLots (&invoice->owner, lot_list);
1869 : :
1870 : : /* If any of the saved lots has no more splits, then destroy it.
1871 : : * Otherwise if any has an invoice associated with it,
1872 : : * send it a modified event to reset its paid status */
1873 : 0 : for (list_iter = lot_list; list_iter; list_iter = list_iter->next)
1874 : : {
1875 : 0 : GNCLot *other_lot = list_iter->data;
1876 : 0 : GncInvoice *other_invoice = gncInvoiceGetInvoiceFromLot (other_lot);
1877 : :
1878 : 0 : if (!gnc_lot_count_splits (other_lot))
1879 : 0 : gnc_lot_destroy (other_lot);
1880 : 0 : else if (other_invoice)
1881 : 0 : qof_event_gen (QOF_INSTANCE(other_invoice), QOF_EVENT_MODIFY, NULL);
1882 : : }
1883 : 0 : g_list_free (lot_list);
1884 : : }
1885 : 8 : g_list_free (lot_split_list);
1886 : :
1887 : : /* If the lot has no splits, then destroy it */
1888 : 8 : if (!gnc_lot_count_splits (lot))
1889 : 8 : gnc_lot_destroy (lot);
1890 : :
1891 : : /* Clear out the invoice posted information */
1892 : 8 : gncInvoiceBeginEdit (invoice);
1893 : :
1894 : 8 : invoice->posted_acc = NULL;
1895 : 8 : invoice->posted_txn = NULL;
1896 : 8 : invoice->posted_lot = NULL;
1897 : 8 : invoice->date_posted = INT64_MAX;
1898 : :
1899 : : /* if we've been asked to reset the tax tables, then do so */
1900 : 8 : if (reset_tax_tables)
1901 : : {
1902 : 8 : gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
1903 : : GList *iter;
1904 : :
1905 : 15 : for (iter = gncInvoiceGetEntries (invoice); iter; iter = iter->next)
1906 : : {
1907 : 7 : GncEntry *entry = iter->data;
1908 : :
1909 : 7 : gncEntryBeginEdit (entry);
1910 : 7 : if (is_cust_doc)
1911 : 7 : gncEntrySetInvTaxTable (entry,
1912 : 7 : gncTaxTableGetParent (gncEntryGetInvTaxTable( entry)));
1913 : : else
1914 : 0 : gncEntrySetBillTaxTable (entry,
1915 : 0 : gncTaxTableGetParent (gncEntryGetBillTaxTable (entry)));
1916 : 7 : gncEntryCommitEdit (entry);
1917 : : }
1918 : : }
1919 : :
1920 : 8 : mark_invoice (invoice);
1921 : 8 : gncInvoiceCommitEdit (invoice);
1922 : :
1923 : 8 : LEAVE ("TRUE");
1924 : :
1925 : 8 : return TRUE;
1926 : : }
1927 : :
1928 : : struct lotmatch
1929 : : {
1930 : : const GncOwner *owner;
1931 : : gboolean positive_balance;
1932 : : };
1933 : :
1934 : : static gboolean
1935 : 0 : gnc_lot_match_owner_balancing (GNCLot *lot, gpointer user_data)
1936 : : {
1937 : 0 : struct lotmatch *lm = user_data;
1938 : : GncOwner owner_def;
1939 : : const GncOwner *owner;
1940 : 0 : gnc_numeric balance = gnc_lot_get_balance (lot);
1941 : :
1942 : : /* Could (part of) this lot serve to balance the lot
1943 : : * for which this query was run ?*/
1944 : 0 : if (lm->positive_balance == gnc_numeric_positive_p (balance))
1945 : 0 : return FALSE;
1946 : :
1947 : : /* Is it ours? Either the lot owner or the lot invoice owner should match */
1948 : 0 : if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
1949 : : {
1950 : 0 : const GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
1951 : 0 : if (!invoice)
1952 : 0 : return FALSE;
1953 : 0 : owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
1954 : : }
1955 : : else
1956 : 0 : owner = gncOwnerGetEndOwner (&owner_def);
1957 : :
1958 : 0 : return gncOwnerEqual (owner, lm->owner);
1959 : : }
1960 : :
1961 : 1 : void gncInvoiceAutoApplyPayments (GncInvoice *invoice)
1962 : : {
1963 : : GNCLot *inv_lot;
1964 : : Account *acct;
1965 : : const GncOwner *owner;
1966 : : GList *lot_list;
1967 : : struct lotmatch lm;
1968 : :
1969 : : /* General note: "paying" in this context means balancing
1970 : : * a lot, by linking opposite signed lots together. So below the term
1971 : : * "payment" can both mean a true payment or it can mean a document of
1972 : : * the opposite sign (invoice vs credit note). It just
1973 : : * depends on what type of document was given as parameter
1974 : : * to this function. */
1975 : :
1976 : : /* Payments can only be applied to posted invoices */
1977 : 1 : g_return_if_fail (invoice);
1978 : 1 : g_return_if_fail (invoice->posted_lot);
1979 : :
1980 : 1 : inv_lot = invoice->posted_lot;
1981 : 1 : acct = invoice->posted_acc;
1982 : 1 : owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
1983 : :
1984 : : /* Find all lots whose balance (or part of their balance) could be
1985 : : * used to close this lot.
1986 : : * To be eligible, the lots have to have an opposite signed balance
1987 : : * and be for the same owner.
1988 : : * For example, for an invoice lot, payment lots and credit note lots
1989 : : * could be used. */
1990 : 1 : lm.positive_balance = gnc_numeric_positive_p (gnc_lot_get_balance (inv_lot));
1991 : 1 : lm.owner = owner;
1992 : 1 : lot_list = xaccAccountFindOpenLots (acct, gnc_lot_match_owner_balancing,
1993 : : &lm, NULL);
1994 : :
1995 : 1 : lot_list = g_list_prepend (lot_list, inv_lot);
1996 : 1 : gncOwnerAutoApplyPaymentsWithLots (owner, lot_list);
1997 : 1 : g_list_free (lot_list);
1998 : : }
1999 : :
2000 : : /*
2001 : : * Create a payment of "amount" for the invoice owner and attempt
2002 : : * to balance it with the given invoice.
2003 : : */
2004 : : void
2005 : 12 : gncInvoiceApplyPayment (const GncInvoice *invoice, Transaction *txn,
2006 : : Account *xfer_acc, gnc_numeric amount,
2007 : : gnc_numeric exch, time64 date,
2008 : : const char *memo, const char *num)
2009 : : {
2010 : : GNCLot *payment_lot;
2011 : 12 : GList *selected_lots = NULL;
2012 : : const GncOwner *owner;
2013 : :
2014 : : /* Verify our arguments */
2015 : 12 : if (!invoice || !gncInvoiceIsPosted (invoice) || !xfer_acc) return;
2016 : :
2017 : 12 : owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
2018 : 12 : g_return_if_fail (owner->owner.undefined);
2019 : :
2020 : : /* Create a lot for this payment */
2021 : 12 : payment_lot = gncOwnerCreatePaymentLotSecs (owner, &txn,
2022 : 12 : invoice->posted_acc,
2023 : : xfer_acc, amount, exch,
2024 : : date, memo, num);
2025 : :
2026 : : /* Select the invoice as only payment candidate */
2027 : 12 : selected_lots = g_list_prepend (selected_lots, invoice->posted_lot);
2028 : :
2029 : : /* And link the invoice lot and the payment lot together as well as possible. */
2030 : 12 : if (payment_lot)
2031 : 12 : selected_lots = g_list_prepend (selected_lots, payment_lot);
2032 : 12 : gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
2033 : : }
2034 : :
2035 : 115 : gboolean gncInvoiceIsPosted (const GncInvoice *invoice)
2036 : : {
2037 : 115 : if (!invoice) return FALSE;
2038 : 115 : return GNC_IS_TRANSACTION(gncInvoiceGetPostedTxn (invoice));
2039 : : }
2040 : :
2041 : 15 : gboolean gncInvoiceIsPaid (const GncInvoice *invoice)
2042 : : {
2043 : 15 : if (!invoice) return FALSE;
2044 : 15 : if (!invoice->posted_lot) return FALSE;
2045 : 15 : return gnc_lot_is_closed (invoice->posted_lot);
2046 : : }
2047 : :
2048 : : /* ================================================================== */
2049 : :
2050 : 681 : void gncInvoiceBeginEdit (GncInvoice *invoice)
2051 : : {
2052 : 681 : qof_begin_edit (&invoice->inst);
2053 : 681 : }
2054 : :
2055 : 0 : static void gncInvoiceOnError (QofInstance *inst, QofBackendError errcode)
2056 : : {
2057 : 0 : PERR("Invoice QofBackend Failure: %d", errcode);
2058 : 0 : gnc_engine_signal_commit_error (errcode);
2059 : 0 : }
2060 : :
2061 : 350 : static void gncInvoiceOnDone (QofInstance *invoice) { }
2062 : :
2063 : 26 : static void invoice_free (QofInstance *inst)
2064 : : {
2065 : 26 : GncInvoice *invoice = (GncInvoice *) inst;
2066 : 26 : gncInvoiceFree (invoice);
2067 : 26 : }
2068 : :
2069 : 681 : void gncInvoiceCommitEdit (GncInvoice *invoice)
2070 : : {
2071 : 681 : if (!qof_commit_edit (QOF_INSTANCE(invoice))) return;
2072 : 376 : qof_commit_edit_part2 (&invoice->inst, gncInvoiceOnError,
2073 : : gncInvoiceOnDone, invoice_free);
2074 : : }
2075 : :
2076 : 0 : int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b)
2077 : : {
2078 : : int compare;
2079 : :
2080 : 0 : if (a == b) return 0;
2081 : 0 : if (!a) return -1;
2082 : 0 : if (!b) return 1;
2083 : :
2084 : 0 : compare = g_strcmp0 (a->id, b->id);
2085 : 0 : if (compare) return compare;
2086 : 0 : if (a->date_opened != b->date_opened) return a->date_opened - b->date_opened;
2087 : 0 : if (a->date_posted != b->date_posted) return a->date_posted - b->date_posted;
2088 : 0 : return qof_instance_guid_compare(a, b);
2089 : : }
2090 : :
2091 : 0 : gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b)
2092 : : {
2093 : 0 : if (a == NULL && b == NULL) return TRUE;
2094 : 0 : if (a == NULL || b == NULL) return FALSE;
2095 : :
2096 : 0 : g_return_val_if_fail (GNC_IS_INVOICE(a), FALSE);
2097 : 0 : g_return_val_if_fail (GNC_IS_INVOICE(b), FALSE);
2098 : :
2099 : 0 : if (g_strcmp0 (a->id, b->id) != 0)
2100 : : {
2101 : 0 : PWARN("IDs differ: %s vs %s", a->id, b->id);
2102 : 0 : return FALSE;
2103 : : }
2104 : :
2105 : 0 : if (g_strcmp0 (a->notes, b->notes) != 0)
2106 : : {
2107 : 0 : PWARN("Notes differ: %s vs %s", a->notes, b->notes);
2108 : 0 : return FALSE;
2109 : : }
2110 : :
2111 : 0 : if (g_strcmp0 (a->billing_id, b->billing_id) != 0)
2112 : : {
2113 : 0 : PWARN("Billing IDs differ: %s vs %s", a->billing_id, b->billing_id);
2114 : 0 : return FALSE;
2115 : : }
2116 : :
2117 : 0 : if (g_strcmp0 (a->printname, b->printname) != 0)
2118 : : {
2119 : 0 : PWARN("Printnames differ: %s vs %s", a->printname, b->printname);
2120 : 0 : return FALSE;
2121 : : }
2122 : :
2123 : 0 : if (a->active != b->active)
2124 : : {
2125 : 0 : PWARN("Active flags differ");
2126 : 0 : return FALSE;
2127 : : }
2128 : :
2129 : 0 : if (!gncBillTermEqual (a->terms, b->terms))
2130 : : {
2131 : 0 : PWARN("Billterms differ");
2132 : 0 : return FALSE;
2133 : : }
2134 : :
2135 : 0 : if (!gncJobEqual (a->job, b->job))
2136 : : {
2137 : 0 : PWARN("Jobs differ");
2138 : 0 : return FALSE;
2139 : : }
2140 : :
2141 : 0 : if (!gnc_commodity_equal (a->currency, b->currency))
2142 : : {
2143 : 0 : PWARN("Currencies differ");
2144 : 0 : return FALSE;
2145 : : }
2146 : :
2147 : 0 : if (!xaccAccountEqual (a->posted_acc, b->posted_acc, TRUE))
2148 : : {
2149 : 0 : PWARN("Posted accounts differ");
2150 : 0 : return FALSE;
2151 : : }
2152 : :
2153 : 0 : if (!xaccTransEqual (a->posted_txn, b->posted_txn, TRUE, TRUE, TRUE, FALSE))
2154 : : {
2155 : 0 : PWARN("Posted tx differ");
2156 : 0 : return FALSE;
2157 : : }
2158 : :
2159 : : #if 0
2160 : : if (!gncLotEqual (a->posted_lot, b->posted_lot))
2161 : : {
2162 : : PWARN("Posted lots differ");
2163 : : return FALSE;
2164 : : }
2165 : : #endif
2166 : :
2167 : : /* FIXME: Need real checks */
2168 : : #if 0
2169 : : GList *entries;
2170 : : GList *prices;
2171 : : GncOwner owner;
2172 : : GncOwner billto;
2173 : : time64 date_opened;
2174 : : time64 date_posted;
2175 : :
2176 : : gnc_numeric to_charge_amount;
2177 : : #endif
2178 : :
2179 : 0 : return TRUE;
2180 : : }
2181 : :
2182 : : /* ============================================================= */
2183 : : /* Package-Private functions */
2184 : :
2185 : 0 : static const char * _gncInvoicePrintable (gpointer obj)
2186 : : {
2187 : 0 : GncInvoice *invoice = obj;
2188 : :
2189 : 0 : g_return_val_if_fail (invoice, NULL);
2190 : :
2191 : 0 : if (qof_instance_get_dirty_flag (invoice) || invoice->printname == NULL)
2192 : : {
2193 : 0 : if (invoice->printname) g_free (invoice->printname);
2194 : :
2195 : 0 : invoice->printname =
2196 : 0 : g_strdup_printf ("%s%s", invoice->id,
2197 : 0 : gncInvoiceIsPosted (invoice) ? _(" (posted)") : "");
2198 : : }
2199 : :
2200 : 0 : return invoice->printname;
2201 : : }
2202 : :
2203 : : static void
2204 : 13 : destroy_invoice_on_book_close (QofInstance *ent, gpointer data)
2205 : : {
2206 : 13 : GncInvoice* invoice = GNC_INVOICE(ent);
2207 : :
2208 : 13 : gncInvoiceBeginEdit (invoice);
2209 : 13 : gncInvoiceDestroy (invoice);
2210 : 13 : }
2211 : :
2212 : : static void
2213 : 156 : gnc_invoice_book_end (QofBook* book)
2214 : : {
2215 : : QofCollection *col;
2216 : :
2217 : 156 : col = qof_book_get_collection (book, GNC_ID_INVOICE);
2218 : 156 : qof_collection_foreach (col, destroy_invoice_on_book_close, NULL);
2219 : 156 : }
2220 : :
2221 : : static QofObject gncInvoiceDesc =
2222 : : {
2223 : : DI(.interface_version = ) QOF_OBJECT_VERSION,
2224 : : DI(.e_type = ) _GNC_MOD_NAME,
2225 : : DI(.type_label = ) "Invoice",
2226 : : DI(.create = ) (gpointer)gncInvoiceCreate,
2227 : : DI(.book_begin = ) NULL,
2228 : : DI(.book_end = ) gnc_invoice_book_end,
2229 : : DI(.is_dirty = ) qof_collection_is_dirty,
2230 : : DI(.mark_clean = ) qof_collection_mark_clean,
2231 : : DI(.foreach = ) qof_collection_foreach,
2232 : : DI(.printable = ) _gncInvoicePrintable,
2233 : : DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
2234 : : };
2235 : :
2236 : : static void
2237 : 84 : reg_lot (void)
2238 : : {
2239 : : static QofParam params[] =
2240 : : {
2241 : : {
2242 : : INVOICE_FROM_LOT, _GNC_MOD_NAME,
2243 : : (QofAccessFunc)gncInvoiceGetInvoiceFromLot, NULL
2244 : : },
2245 : : { NULL },
2246 : : };
2247 : :
2248 : 84 : qof_class_register (GNC_ID_LOT, NULL, params);
2249 : 84 : }
2250 : :
2251 : : static void
2252 : 84 : reg_txn (void)
2253 : : {
2254 : : static QofParam params[] =
2255 : : {
2256 : : {
2257 : : INVOICE_FROM_TXN, _GNC_MOD_NAME,
2258 : : (QofAccessFunc)gncInvoiceGetInvoiceFromTxn, NULL
2259 : : },
2260 : : { NULL },
2261 : : };
2262 : :
2263 : 84 : qof_class_register (GNC_ID_TRANS, NULL, params);
2264 : 84 : }
2265 : :
2266 : 84 : gboolean gncInvoiceRegister (void)
2267 : : {
2268 : : static QofParam params[] =
2269 : : {
2270 : : { INVOICE_ID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID },
2271 : : { INVOICE_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetOwner, NULL },
2272 : : { INVOICE_OPENED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened },
2273 : : { INVOICE_DUE, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateDue, NULL },
2274 : : { INVOICE_POSTED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted },
2275 : : { INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL },
2276 : : { INVOICE_IS_PAID, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid, NULL },
2277 : : { INVOICE_BILLINGID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
2278 : : { INVOICE_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetNotes, (QofSetterFunc)gncInvoiceSetNotes },
2279 : : { INVOICE_DOCLINK, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetDocLink, (QofSetterFunc)gncInvoiceSetDocLink },
2280 : : { INVOICE_ACC, GNC_ID_ACCOUNT, (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc },
2281 : : { INVOICE_POST_TXN, GNC_ID_TRANS, (QofAccessFunc)gncInvoiceGetPostedTxn, (QofSetterFunc)gncInvoiceSetPostedTxn },
2282 : : { INVOICE_POST_LOT, GNC_ID_LOT, (QofAccessFunc)gncInvoiceGetPostedLot, NULL/*(QofSetterFunc)gncInvoiceSetPostedLot*/ },
2283 : : { INVOICE_TYPE, QOF_TYPE_INT32, (QofAccessFunc)gncInvoiceGetType, NULL },
2284 : : { INVOICE_TYPE_STRING, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetTypeString, NULL },
2285 : : { INVOICE_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncInvoiceGetTerms, (QofSetterFunc)gncInvoiceSetTerms },
2286 : : { INVOICE_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetBillTo, NULL },
2287 : : { INVOICE_ENTRIES, QOF_TYPE_COLLECT, (QofAccessFunc)qofInvoiceGetEntries, (QofSetterFunc)qofInvoiceSetEntries },
2288 : : { INVOICE_JOB, GNC_ID_JOB, (QofAccessFunc)qofInvoiceGetJob, (QofSetterFunc)qofInvoiceSetJob },
2289 : : { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, (QofSetterFunc)gncInvoiceSetActive },
2290 : : { INVOICE_IS_CN, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetIsCreditNote, (QofSetterFunc)gncInvoiceSetIsCreditNote },
2291 : : { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
2292 : : { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
2293 : : { NULL },
2294 : : };
2295 : :
2296 : 84 : qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncInvoiceCompare, params);
2297 : 84 : reg_lot ();
2298 : 84 : reg_txn ();
2299 : :
2300 : : /* Make the compiler happy... */
2301 : : if (0)
2302 : : {
2303 : : qofInvoiceSetEntries (NULL, NULL);
2304 : : qofInvoiceGetEntries (NULL);
2305 : : qofInvoiceSetOwner (NULL, NULL);
2306 : : qofInvoiceGetOwner (NULL);
2307 : : qofInvoiceSetBillTo (NULL, NULL);
2308 : : qofInvoiceGetBillTo (NULL);
2309 : : }
2310 : 84 : return qof_object_register (&gncInvoiceDesc);
2311 : : }
2312 : :
2313 : 0 : gchar *gncInvoiceNextID (QofBook *book, const GncOwner *owner)
2314 : : {
2315 : : gchar *nextID;
2316 : 0 : switch (gncOwnerGetType (gncOwnerGetEndOwner (owner)))
2317 : : {
2318 : 0 : case GNC_OWNER_CUSTOMER:
2319 : 0 : nextID = qof_book_increment_and_format_counter (book, "gncInvoice");
2320 : 0 : break;
2321 : 0 : case GNC_OWNER_VENDOR:
2322 : 0 : nextID = qof_book_increment_and_format_counter (book, "gncBill");
2323 : 0 : break;
2324 : 0 : case GNC_OWNER_EMPLOYEE:
2325 : 0 : nextID = qof_book_increment_and_format_counter (book, "gncExpVoucher");
2326 : 0 : break;
2327 : 0 : default:
2328 : 0 : nextID = qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
2329 : 0 : break;
2330 : : }
2331 : 0 : return nextID;
2332 : : }
|