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 : 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 : 1699 : GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot)
1235 : : {
1236 : 1699 : GncGUID *guid = NULL;
1237 : : QofBook *book;
1238 : 1699 : GncInvoice *invoice = NULL;
1239 : :
1240 : 1699 : if (!lot) return NULL;
1241 : :
1242 : 1669 : invoice = gnc_lot_get_cached_invoice (lot);
1243 : 1669 : if (!invoice)
1244 : : {
1245 : 668 : book = gnc_lot_get_book (lot);
1246 : 668 : qof_instance_get (QOF_INSTANCE(lot), "invoice", &guid, NULL);
1247 : 668 : invoice = gncInvoiceLookup (book, guid);
1248 : 668 : guid_free (guid);
1249 : 668 : gnc_lot_set_cached_invoice (lot, invoice);
1250 : : }
1251 : :
1252 : 1669 : 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 : 176 : gnc_invoice_book_end (QofBook* book)
2160 : : {
2161 : : QofCollection *col;
2162 : :
2163 : 176 : col = qof_book_get_collection (book, GNC_ID_INVOICE);
2164 : 176 : qof_collection_foreach (col, destroy_invoice_on_book_close, NULL);
2165 : 176 : }
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_STRING, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID },
2217 : : { INVOICE_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetOwner, NULL },
2218 : : { INVOICE_OPENED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened },
2219 : : { INVOICE_DUE, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateDue, NULL },
2220 : : { INVOICE_POSTED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted },
2221 : : { INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL },
2222 : : { INVOICE_IS_PAID, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid, NULL },
2223 : : { INVOICE_BILLINGID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID },
2224 : : { INVOICE_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetNotes, (QofSetterFunc)gncInvoiceSetNotes },
2225 : : { INVOICE_DOCLINK, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetDocLink, (QofSetterFunc)gncInvoiceSetDocLink },
2226 : : { INVOICE_ACC, GNC_ID_ACCOUNT, (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc },
2227 : : { INVOICE_POST_TXN, GNC_ID_TRANS, (QofAccessFunc)gncInvoiceGetPostedTxn, (QofSetterFunc)gncInvoiceSetPostedTxn },
2228 : : { INVOICE_POST_LOT, GNC_ID_LOT, (QofAccessFunc)gncInvoiceGetPostedLot, NULL/*(QofSetterFunc)gncInvoiceSetPostedLot*/ },
2229 : : { INVOICE_TYPE, QOF_TYPE_INT32, (QofAccessFunc)gncInvoiceGetType, NULL },
2230 : : { INVOICE_TYPE_STRING, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetTypeString, NULL },
2231 : : { INVOICE_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncInvoiceGetTerms, (QofSetterFunc)gncInvoiceSetTerms },
2232 : : { INVOICE_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetBillTo, NULL },
2233 : : { INVOICE_JOB, GNC_ID_JOB, (QofAccessFunc)qofInvoiceGetJob, (QofSetterFunc)qofInvoiceSetJob },
2234 : : { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, (QofSetterFunc)gncInvoiceSetActive },
2235 : : { INVOICE_IS_CN, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetIsCreditNote, (QofSetterFunc)gncInvoiceSetIsCreditNote },
2236 : : { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
2237 : : { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
2238 : : { NULL },
2239 : : };
2240 : :
2241 : 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 : : }
|