1 /********************************************************************\
2 * gncEntry.c -- the Core Business Entry Interface *
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 Derek Atkins
25 * Author: Derek Atkins <warlord@MIT.EDU>
26 */
27
28 #include <config.h>
29
30 #include <glib.h>
31 #include <qofinstance-p.h>
32
33 #include "gnc-commodity.h"
34
35 #include "gncEntry.h"
36 #include "gncEntryP.h"
37 #include "gnc-features.h"
38 #include "gncInvoice.h"
39 #include "gncOrder.h"
40
41 struct _gncEntry
42 {
43 QofInstance inst;
44
45 time64 date;
46 time64 date_entered;
47 const char * desc;
48 const char * action;
49 const char * notes;
50 gnc_numeric quantity;
51
52 /* customer invoice data */
53 Account * i_account;
54 gnc_numeric i_price;
55 gboolean i_taxable;
56 gboolean i_taxincluded;
57 GncTaxTable * i_tax_table;
58 gnc_numeric i_discount;
59 GncAmountType i_disc_type;
60 GncDiscountHow i_disc_how;
61
62 /* vendor bill data */
63 Account * b_account;
64 gnc_numeric b_price;
65 gboolean b_taxable;
66 gboolean b_taxincluded;
67 GncTaxTable * b_tax_table;
68 gboolean billable;
69 GncOwner billto;
70
71 /* employee bill data */
72 GncEntryPaymentType b_payment;
73
74 /* my parent(s) */
75 GncOrder * order;
76 GncInvoice * invoice;
77 GncInvoice * bill;
78
79 /* CACHED VALUES */
80 gboolean values_dirty;
81
82 /* customer invoice */
83 gnc_numeric i_value;
84 gnc_numeric i_value_rounded;
85 GList * i_tax_values;
86 gnc_numeric i_tax_value;
87 gnc_numeric i_tax_value_rounded;
88 gnc_numeric i_disc_value;
89 gnc_numeric i_disc_value_rounded;
90 time64 i_taxtable_modtime;
91
92 /* vendor bill */
93 gnc_numeric b_value;
94 gnc_numeric b_value_rounded;
95 GList * b_tax_values;
96 gnc_numeric b_tax_value;
97 gnc_numeric b_tax_value_rounded;
98 time64 b_taxtable_modtime;
99 };
100
101 struct _gncEntryClass
102 {
103 QofInstanceClass parent_class;
104 };
105
106 static QofLogModule log_module = GNC_MOD_BUSINESS;
107
108
109 /* You must edit the functions in this block in tandem.
110 * KEEP THIS FUNCTION IN SYNC with the one below! */
111 const char *
gncEntryDiscountHowToString(GncDiscountHow how)112 gncEntryDiscountHowToString (GncDiscountHow how)
113 {
114 switch (how)
115 {
116 case (GNC_DISC_PRETAX):
117 return "PRETAX";
118 case (GNC_DISC_SAMETIME):
119 return "SAMETIME";
120 case (GNC_DISC_POSTTAX):
121 return "POSTTAX";
122 default:
123 g_warning ("asked to translate unknown discount-how %d.\n", how);
124 break;
125 }
126 return NULL;
127 }
128
129 /* You must edit the functions in this block in tandem.
130 * KEEP THIS FUNCTION IN SYNC with the one above! */
gncEntryDiscountStringToHow(const char * str,GncDiscountHow * how)131 gboolean gncEntryDiscountStringToHow (const char *str, GncDiscountHow *how)
132 {
133 if (g_strcmp0 ("PRETAX", str) == 0)
134 {
135 *how = GNC_DISC_PRETAX;
136 return TRUE;
137 }
138 if (g_strcmp0 ("SAMETIME", str) == 0)
139 {
140 *how = GNC_DISC_SAMETIME;
141 return TRUE;
142 }
143 if (g_strcmp0 ("POSTTAX", str) == 0)
144 {
145 *how = GNC_DISC_POSTTAX;
146 return TRUE;
147 }
148 g_warning ("asked to translate unknown discount-how string %s.\n",
149 str ? str : "(null)");
150
151 return FALSE;
152 }
153
154 /* You must edit the functions in this block in tandem.
155 * KEEP THIS FUNCTION IN SYNC with the one below! */
gncEntryPaymentTypeToString(GncEntryPaymentType type)156 const char * gncEntryPaymentTypeToString (GncEntryPaymentType type)
157 {
158 switch (type)
159 {
160 case (GNC_PAYMENT_CASH):
161 return "CASH";
162 case (GNC_PAYMENT_CARD):
163 return "CARD";
164 default:
165 g_warning ("asked to translate unknown payment type %d.\n", type);
166 break;
167 }
168 return NULL ;
169 }
170
171 /* You must edit the functions in this block in tandem.
172 * KEEP THIS FUNCTION IN SYNC with the one above! */
gncEntryPaymentStringToType(const char * str,GncEntryPaymentType * type)173 gboolean gncEntryPaymentStringToType (const char *str, GncEntryPaymentType *type)
174 {
175 if (g_strcmp0 ("CASH", str) == 0)
176 {
177 *type = GNC_PAYMENT_CASH;
178 return TRUE;
179 }
180 if (g_strcmp0 ("CARD", str) == 0)
181 {
182 *type = GNC_PAYMENT_CARD;
183 return TRUE;
184 }
185 g_warning ("asked to translate unknown discount-how string %s.\n",
186 str ? str : "(null)");
187
188 return FALSE;
189 }
190
191 #define _GNC_MOD_NAME GNC_ID_ENTRY
192
193 #define SET_STR(obj, member, str) { \
194 if (!g_strcmp0 (member, str)) return; \
195 gncEntryBeginEdit (obj); \
196 CACHE_REPLACE (member, str); \
197 }
198
199 static inline void mark_entry (GncEntry *entry);
mark_entry(GncEntry * entry)200 void mark_entry (GncEntry *entry)
201 {
202 qof_instance_set_dirty(&entry->inst);
203 qof_event_gen (&entry->inst, QOF_EVENT_MODIFY, NULL);
204 }
205
206 /* ================================================================ */
207
208 enum
209 {
210 PROP_0,
211 // PROP_DATE, /* Table */
212 // PROP_DATE_ENTERED, /* Table */
213 PROP_DESCRIPTION, /* Table */
214 // PROP_ACTION, /* Table */
215 // PROP_NOTES, /* Table */
216 // PROP_QUANTITY, /* Table (numeric) */
217 // PROP_I_ACCT, /* Table */
218 // PROP_I_PRICE, /* Table (numeric) */
219 // PROP_I_DISCOUNT, /* Table (numeric) */
220 // PROP_INVOICE, /* Table */
221 // PROP_I_DISC_TYPE, /* Table */
222 // PROP_I_DISC_HOW, /* Table */
223 // PROP_I_TAXABLE, /* Table */
224 // PROP_I_TAX_INCL, /* Table */
225 // PROP_I_TAXTABLE, /* Table */
226 // PROP_B_ACCT, /* Table */
227 // PROP_B_PRICE, /* Table (numeric) */
228 // PROP_BILL, /* Table */
229 // PROP_B_TAXTABLE_1, /* Table */
230 // PROP_B_TAX_INCL, /* Table */
231 // PROP_B_TAXTABLE, /* Table */
232 // PROP_B_PAYTYPE, /* Table */
233 // PROP_BILLABLE, /* Table */
234 // PROP_BILLTO_TYPE, /* Table */
235 // PROP_BILLTO, /* Table */
236 // PROP_ORDER, /* Table */
237 };
238
239 /* GObject Initialization */
240 G_DEFINE_TYPE(GncEntry, gnc_entry, QOF_TYPE_INSTANCE);
241
242 static void
gnc_entry_init(GncEntry * entry)243 gnc_entry_init(GncEntry* entry)
244 {
245 }
246
247 static void
gnc_entry_dispose(GObject * entryp)248 gnc_entry_dispose(GObject *entryp)
249 {
250 G_OBJECT_CLASS(gnc_entry_parent_class)->dispose(entryp);
251 }
252
253 static void
gnc_entry_finalize(GObject * entryp)254 gnc_entry_finalize(GObject* entryp)
255 {
256 G_OBJECT_CLASS(gnc_entry_parent_class)->finalize(entryp);
257 }
258
259 static void
gnc_entry_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)260 gnc_entry_get_property (GObject *object,
261 guint prop_id,
262 GValue *value,
263 GParamSpec *pspec)
264 {
265 GncEntry *entry;
266
267 g_return_if_fail(GNC_IS_ENTRY(object));
268
269 entry = GNC_ENTRY(object);
270 switch (prop_id)
271 {
272 case PROP_DESCRIPTION:
273 g_value_set_string(value, entry->desc);
274 break;
275 default:
276 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
277 break;
278 }
279 }
280
281 static void
gnc_entry_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)282 gnc_entry_set_property (GObject *object,
283 guint prop_id,
284 const GValue *value,
285 GParamSpec *pspec)
286 {
287 GncEntry *entry;
288
289 g_return_if_fail(GNC_IS_ENTRY(object));
290
291 entry = GNC_ENTRY(object);
292 g_assert (qof_instance_get_editlevel(entry));
293
294 switch (prop_id)
295 {
296 case PROP_DESCRIPTION:
297 gncEntrySetDescription(entry, g_value_get_string(value));
298 break;
299 default:
300 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
301 break;
302 }
303 }
304
305 /** Return displayable name */
306 static gchar*
impl_get_display_name(const QofInstance * inst)307 impl_get_display_name(const QofInstance* inst)
308 {
309 GncEntry* entry;
310 gchar* display_name;
311 gchar* s;
312
313 g_return_val_if_fail(inst != NULL, FALSE);
314 g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
315
316 entry = GNC_ENTRY(inst);
317 if (entry->order != NULL)
318 {
319 display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->order));
320 s = g_strdup_printf("Entry in %s", display_name);
321 g_free(display_name);
322 return s;
323 }
324 if (entry->invoice != NULL)
325 {
326 display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->invoice));
327 s = g_strdup_printf("Entry in %s", display_name);
328 g_free(display_name);
329 return s;
330 }
331 if (entry->bill != NULL)
332 {
333 display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->bill));
334 s = g_strdup_printf("Entry in %s", display_name);
335 g_free(display_name);
336 return s;
337 }
338
339 return g_strdup_printf("Entry %p", inst);
340 }
341
342 /** Does this object refer to a specific object */
343 static gboolean
impl_refers_to_object(const QofInstance * inst,const QofInstance * ref)344 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
345 {
346 GncEntry* entry;
347
348 g_return_val_if_fail(inst != NULL, FALSE);
349 g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
350
351 entry = GNC_ENTRY(inst);
352
353 if (GNC_IS_ACCOUNT(ref))
354 {
355 Account* acc = GNC_ACCOUNT(ref);
356 return (entry->i_account == acc || entry->b_account == acc);
357 }
358 else if (GNC_IS_TAXTABLE(ref))
359 {
360 GncTaxTable* tt = GNC_TAXTABLE(ref);
361 return (entry->i_tax_table == tt || entry->b_tax_table == tt);
362 }
363
364 return FALSE;
365 }
366
367 /** Returns a list of my type of object which refers to an object. For example, when called as
368 qof_instance_get_typed_referring_object_list(taxtable, account);
369 it will return the list of taxtables which refer to a specific account. The result should be the
370 same regardless of which taxtable object is used. The list must be freed by the caller but the
371 objects on the list must not.
372 */
373 static GList*
impl_get_typed_referring_object_list(const QofInstance * inst,const QofInstance * ref)374 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
375 {
376 if (!GNC_IS_ACCOUNT(ref) && !GNC_IS_TAXTABLE(ref))
377 {
378 return NULL;
379 }
380
381 return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
382 }
383
384 static void
gnc_entry_class_init(GncEntryClass * klass)385 gnc_entry_class_init (GncEntryClass *klass)
386 {
387 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
388 QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
389
390 gobject_class->dispose = gnc_entry_dispose;
391 gobject_class->finalize = gnc_entry_finalize;
392 gobject_class->set_property = gnc_entry_set_property;
393 gobject_class->get_property = gnc_entry_get_property;
394
395 qof_class->get_display_name = impl_get_display_name;
396 qof_class->refers_to_object = impl_refers_to_object;
397 qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
398
399 g_object_class_install_property
400 (gobject_class,
401 PROP_DESCRIPTION,
402 g_param_spec_string ("description",
403 "Entry Description",
404 "The description is an arbitrary string "
405 "assigned by the user. It provides identification "
406 "for this entry.",
407 NULL,
408 G_PARAM_READWRITE));
409 }
410
411 /* Create/Destroy Functions */
gncEntryCreate(QofBook * book)412 GncEntry *gncEntryCreate (QofBook *book)
413 {
414 GncEntry *entry;
415 gnc_numeric zero = gnc_numeric_zero ();
416
417 if (!book) return NULL;
418
419 entry = g_object_new (GNC_TYPE_ENTRY, NULL);
420 qof_instance_init_data (&entry->inst, _GNC_MOD_NAME, book);
421
422 entry->desc = CACHE_INSERT ("");
423 entry->action = CACHE_INSERT ("");
424 entry->notes = CACHE_INSERT ("");
425 entry->quantity = zero;
426
427 entry->i_price = zero;
428 entry->i_taxable = TRUE;
429 entry->i_discount = zero;
430 entry->i_disc_type = GNC_AMT_TYPE_PERCENT;
431 entry->i_disc_how = GNC_DISC_PRETAX;
432
433 entry->b_price = zero;
434 entry->b_taxable = TRUE;
435 entry->billto.type = GNC_OWNER_CUSTOMER;
436 entry->b_payment = GNC_PAYMENT_CASH;
437
438 entry->values_dirty = TRUE;
439
440 qof_event_gen (&entry->inst, QOF_EVENT_CREATE, NULL);
441
442 return entry;
443 }
444
gncEntryDestroy(GncEntry * entry)445 void gncEntryDestroy (GncEntry *entry)
446 {
447 if (!entry) return;
448 qof_instance_set_destroying(entry, TRUE);
449 gncEntryCommitEdit(entry);
450 }
451
gncEntryFree(GncEntry * entry)452 static void gncEntryFree (GncEntry *entry)
453 {
454 if (!entry) return;
455
456 qof_event_gen (&entry->inst, QOF_EVENT_DESTROY, NULL);
457
458 CACHE_REMOVE (entry->desc);
459 CACHE_REMOVE (entry->action);
460 CACHE_REMOVE (entry->notes);
461 if (entry->i_tax_values)
462 gncAccountValueDestroy (entry->i_tax_values);
463 if (entry->b_tax_values)
464 gncAccountValueDestroy (entry->b_tax_values);
465 if (entry->i_tax_table)
466 gncTaxTableDecRef (entry->i_tax_table);
467 if (entry->b_tax_table)
468 gncTaxTableDecRef (entry->b_tax_table);
469
470 /* qof_instance_release (&entry->inst); */
471 g_object_unref (entry);
472 }
473
474 /* ================================================================ */
475 /* Set Functions */
476
gncEntrySetDate(GncEntry * entry,time64 date)477 void gncEntrySetDate (GncEntry *entry, time64 date)
478 {
479 gboolean first_date = FALSE;
480 if (!entry) return;
481 if (entry->date == date) return;
482 if (!entry->date)
483 first_date = TRUE;
484 gncEntryBeginEdit (entry);
485 entry->date = date;
486 mark_entry (entry);
487 gncEntryCommitEdit (entry);
488
489 /* Don't re-sort the first time we set the date on this entry */
490 if (!first_date)
491 {
492 if (entry->invoice)
493 gncInvoiceSortEntries(entry->invoice);
494 if (entry->bill)
495 gncInvoiceSortEntries(entry->bill);
496 }
497 }
498
gncEntrySetDateGDate(GncEntry * entry,const GDate * date)499 void gncEntrySetDateGDate (GncEntry *entry, const GDate* date)
500 {
501 if (!entry || !date || !g_date_valid(date))
502 return;
503
504 /* Watch out: Here we are deviating from the initial convention that a
505 GDate always converts to the start time of the day. Instead, the GDate is
506 converted to "noon" on the respective date. This is not nice, but this
507 convention was used for the time64 of GncEntry all the time, so we better
508 stick to it.*/
509 gncEntrySetDate(entry, time64CanonicalDayTime(gdate_to_time64(*date)));
510 }
511
gncEntrySetDateEntered(GncEntry * entry,time64 date)512 void gncEntrySetDateEntered (GncEntry *entry, time64 date)
513 {
514 if (!entry) return;
515 if (entry->date_entered == date) return;
516 gncEntryBeginEdit (entry);
517 entry->date_entered = date;
518 mark_entry (entry);
519 gncEntryCommitEdit (entry);
520 }
521
gncEntrySetDescription(GncEntry * entry,const char * desc)522 void gncEntrySetDescription (GncEntry *entry, const char *desc)
523 {
524 if (!entry || !desc) return;
525 SET_STR (entry, entry->desc, desc);
526 mark_entry (entry);
527 gncEntryCommitEdit (entry);
528 }
529
gncEntrySetAction(GncEntry * entry,const char * action)530 void gncEntrySetAction (GncEntry *entry, const char *action)
531 {
532 if (!entry || !action) return;
533 SET_STR (entry, entry->action, action);
534 mark_entry (entry);
535 gncEntryCommitEdit (entry);
536 }
537
gncEntrySetNotes(GncEntry * entry,const char * notes)538 void gncEntrySetNotes (GncEntry *entry, const char *notes)
539 {
540 if (!entry || !notes) return;
541 SET_STR (entry, entry->notes, notes);
542 mark_entry (entry);
543 gncEntryCommitEdit (entry);
544 }
545
gncEntrySetQuantity(GncEntry * entry,gnc_numeric quantity)546 void gncEntrySetQuantity (GncEntry *entry, gnc_numeric quantity)
547 {
548 if (!entry) return;
549 if (gnc_numeric_eq (entry->quantity, quantity)) return;
550 gncEntryBeginEdit (entry);
551 entry->quantity = quantity;
552 entry->values_dirty = TRUE;
553 mark_entry (entry);
554 gncEntryCommitEdit (entry);
555 }
556
gncEntrySetDocQuantity(GncEntry * entry,gnc_numeric quantity,gboolean is_cn)557 void gncEntrySetDocQuantity (GncEntry *entry, gnc_numeric quantity, gboolean is_cn)
558 {
559 if (!entry) return;
560 if (gnc_numeric_eq (entry->quantity, (is_cn ? gnc_numeric_neg (quantity) : quantity))) return;
561 gncEntryBeginEdit (entry);
562 entry->quantity = (is_cn ? gnc_numeric_neg (quantity) : quantity);
563 entry->values_dirty = TRUE;
564 mark_entry (entry);
565 gncEntryCommitEdit (entry);
566 }
567
568 /* Customer Invoices */
569
gncEntrySetInvAccount(GncEntry * entry,Account * acc)570 void gncEntrySetInvAccount (GncEntry *entry, Account *acc)
571 {
572 if (!entry) return;
573 if (entry->i_account == acc) return;
574 gncEntryBeginEdit (entry);
575 entry->i_account = acc;
576 mark_entry (entry);
577 gncEntryCommitEdit (entry);
578 }
579
gncEntrySetInvPrice(GncEntry * entry,gnc_numeric price)580 void gncEntrySetInvPrice (GncEntry *entry, gnc_numeric price)
581 {
582 if (!entry) return;
583 if (gnc_numeric_eq (entry->i_price, price)) return;
584 gncEntryBeginEdit (entry);
585 entry->i_price = price;
586 entry->values_dirty = TRUE;
587 mark_entry (entry);
588 gncEntryCommitEdit (entry);
589 }
590
gncEntrySetInvTaxable(GncEntry * entry,gboolean taxable)591 void gncEntrySetInvTaxable (GncEntry *entry, gboolean taxable)
592 {
593 if (!entry) return;
594 if (entry->i_taxable == taxable) return;
595 gncEntryBeginEdit (entry);
596 entry->i_taxable = taxable;
597 entry->values_dirty = TRUE;
598 mark_entry (entry);
599 gncEntryCommitEdit (entry);
600 }
601
gncEntrySetInvTaxIncluded(GncEntry * entry,gboolean taxincluded)602 void gncEntrySetInvTaxIncluded (GncEntry *entry, gboolean taxincluded)
603 {
604 if (!entry) return;
605 if (entry->i_taxincluded == taxincluded) return;
606 gncEntryBeginEdit (entry);
607 entry->i_taxincluded = taxincluded;
608 entry->values_dirty = TRUE;
609 mark_entry (entry);
610 gncEntryCommitEdit (entry);
611 }
612
gncEntrySetInvTaxTable(GncEntry * entry,GncTaxTable * table)613 void gncEntrySetInvTaxTable (GncEntry *entry, GncTaxTable *table)
614 {
615 if (!entry) return;
616 if (entry->i_tax_table == table) return;
617 gncEntryBeginEdit (entry);
618 if (entry->i_tax_table)
619 gncTaxTableDecRef (entry->i_tax_table);
620 if (table)
621 gncTaxTableIncRef (table);
622 entry->i_tax_table = table;
623 entry->values_dirty = TRUE;
624 mark_entry (entry);
625 gncEntryCommitEdit (entry);
626 }
627
gncEntrySetInvDiscount(GncEntry * entry,gnc_numeric discount)628 void gncEntrySetInvDiscount (GncEntry *entry, gnc_numeric discount)
629 {
630 if (!entry) return;
631 if (gnc_numeric_eq (entry->i_discount, discount)) return;
632 gncEntryBeginEdit (entry);
633 entry->i_discount = discount;
634 entry->values_dirty = TRUE;
635 mark_entry (entry);
636 gncEntryCommitEdit (entry);
637 }
638
gncEntrySetInvDiscountType(GncEntry * entry,GncAmountType type)639 void gncEntrySetInvDiscountType (GncEntry *entry, GncAmountType type)
640 {
641 if (!entry) return;
642 if (entry->i_disc_type == type) return;
643
644 gncEntryBeginEdit (entry);
645 entry->i_disc_type = type;
646 entry->values_dirty = TRUE;
647 mark_entry (entry);
648 gncEntryCommitEdit (entry);
649 }
650
gncEntrySetInvDiscountHow(GncEntry * entry,GncDiscountHow how)651 void gncEntrySetInvDiscountHow (GncEntry *entry, GncDiscountHow how)
652 {
653 if (!entry) return;
654 if (entry->i_disc_how == how) return;
655
656 gncEntryBeginEdit (entry);
657 entry->i_disc_how = how;
658 entry->values_dirty = TRUE;
659 mark_entry (entry);
660 gncEntryCommitEdit (entry);
661 }
662
qofEntrySetInvDiscType(GncEntry * entry,const char * type_string)663 void qofEntrySetInvDiscType (GncEntry *entry, const char *type_string)
664 {
665 GncAmountType type;
666
667 if (!entry) return;
668 gncAmountStringToType(type_string, &type);
669 if (entry->i_disc_type == type) return;
670 gncEntryBeginEdit (entry);
671 entry->i_disc_type = type;
672 entry->values_dirty = TRUE;
673 mark_entry (entry);
674 gncEntryCommitEdit (entry);
675
676 }
677
qofEntrySetInvDiscHow(GncEntry * entry,const char * type)678 void qofEntrySetInvDiscHow (GncEntry *entry, const char *type)
679 {
680 GncDiscountHow how = GNC_DISC_PRETAX;
681
682 if (!entry) return;
683 gncEntryBeginEdit (entry);
684 gncEntryDiscountStringToHow(type, &how);
685 if (entry->i_disc_how == how) return;
686 entry->i_disc_how = how;
687 entry->values_dirty = TRUE;
688 mark_entry (entry);
689 gncEntryCommitEdit (entry);
690 }
691
692 /* Vendor Bills */
693
gncEntrySetBillAccount(GncEntry * entry,Account * acc)694 void gncEntrySetBillAccount (GncEntry *entry, Account *acc)
695 {
696 if (!entry) return;
697 if (entry->b_account == acc) return;
698 gncEntryBeginEdit (entry);
699 entry->b_account = acc;
700 mark_entry (entry);
701 gncEntryCommitEdit (entry);
702 }
703
gncEntrySetBillPrice(GncEntry * entry,gnc_numeric price)704 void gncEntrySetBillPrice (GncEntry *entry, gnc_numeric price)
705 {
706 if (!entry) return;
707 if (gnc_numeric_eq (entry->b_price, price)) return;
708 gncEntryBeginEdit (entry);
709 entry->b_price = price;
710 entry->values_dirty = TRUE;
711 mark_entry (entry);
712 gncEntryCommitEdit (entry);
713 }
714
gncEntrySetBillTaxable(GncEntry * entry,gboolean taxable)715 void gncEntrySetBillTaxable (GncEntry *entry, gboolean taxable)
716 {
717 if (!entry) return;
718 if (entry->b_taxable == taxable) return;
719 gncEntryBeginEdit (entry);
720 entry->b_taxable = taxable;
721 entry->values_dirty = TRUE;
722 mark_entry (entry);
723 gncEntryCommitEdit (entry);
724 }
725
gncEntrySetBillTaxIncluded(GncEntry * entry,gboolean taxincluded)726 void gncEntrySetBillTaxIncluded (GncEntry *entry, gboolean taxincluded)
727 {
728 if (!entry) return;
729 if (entry->b_taxincluded == taxincluded) return;
730 gncEntryBeginEdit (entry);
731 entry->b_taxincluded = taxincluded;
732 entry->values_dirty = TRUE;
733 mark_entry (entry);
734 gncEntryCommitEdit (entry);
735 }
736
gncEntrySetBillTaxTable(GncEntry * entry,GncTaxTable * table)737 void gncEntrySetBillTaxTable (GncEntry *entry, GncTaxTable *table)
738 {
739 if (!entry) return;
740 if (entry->b_tax_table == table) return;
741 gncEntryBeginEdit (entry);
742 if (entry->b_tax_table)
743 gncTaxTableDecRef (entry->b_tax_table);
744 if (table)
745 gncTaxTableIncRef (table);
746 entry->b_tax_table = table;
747 entry->values_dirty = TRUE;
748 mark_entry (entry);
749 gncEntryCommitEdit (entry);
750 }
751
gncEntrySetBillable(GncEntry * entry,gboolean billable)752 void gncEntrySetBillable (GncEntry *entry, gboolean billable)
753 {
754 if (!entry) return;
755 if (entry->billable == billable) return;
756
757 gncEntryBeginEdit (entry);
758 entry->billable = billable;
759 mark_entry (entry);
760 gncEntryCommitEdit (entry);
761 }
762
gncEntrySetBillTo(GncEntry * entry,GncOwner * billto)763 void gncEntrySetBillTo (GncEntry *entry, GncOwner *billto)
764 {
765 if (!entry || !billto) return;
766 if (gncOwnerEqual (&entry->billto, billto)) return;
767
768 gncEntryBeginEdit (entry);
769 gncOwnerCopy (billto, &entry->billto);
770 mark_entry (entry);
771 gncEntryCommitEdit (entry);
772 }
773
gncEntrySetBillPayment(GncEntry * entry,GncEntryPaymentType type)774 void gncEntrySetBillPayment (GncEntry *entry, GncEntryPaymentType type)
775 {
776 if (!entry) return;
777 if (entry->b_payment == type) return;
778 gncEntryBeginEdit (entry);
779 entry->b_payment = type;
780 mark_entry (entry);
781 gncEntryCommitEdit (entry);
782 }
783
784 /* Called from gncOrder when we're added to the Order */
gncEntrySetOrder(GncEntry * entry,GncOrder * order)785 void gncEntrySetOrder (GncEntry *entry, GncOrder *order)
786 {
787 if (!entry) return;
788 if (entry->order == order) return;
789 gncEntryBeginEdit (entry);
790 entry->order = order;
791 mark_entry (entry);
792 gncEntryCommitEdit (entry);
793
794 }
795
796 /* called from gncInvoice when we're added to the Invoice */
gncEntrySetInvoice(GncEntry * entry,GncInvoice * invoice)797 void gncEntrySetInvoice (GncEntry *entry, GncInvoice *invoice)
798 {
799 if (!entry) return;
800 if (entry->invoice == invoice) return;
801 gncEntryBeginEdit (entry);
802 entry->invoice = invoice;
803 mark_entry (entry);
804 gncEntryCommitEdit (entry);
805 }
806
807 /* called from gncInvoice when we're added to the Invoice/Bill */
gncEntrySetBill(GncEntry * entry,GncInvoice * bill)808 void gncEntrySetBill (GncEntry *entry, GncInvoice *bill)
809 {
810 if (!entry) return;
811 if (entry->bill == bill) return;
812 gncEntryBeginEdit (entry);
813 entry->bill = bill;
814 mark_entry (entry);
815 gncEntryCommitEdit (entry);
816 }
817
gncEntryCopy(const GncEntry * src,GncEntry * dest,gboolean add_entry)818 void gncEntryCopy (const GncEntry *src, GncEntry *dest, gboolean add_entry)
819 {
820 if (!src || !dest) return;
821
822 gncEntryBeginEdit (dest);
823 dest->date = src->date;
824 dest->date_entered = src->date_entered; /* ??? */
825 gncEntrySetDescription (dest, src->desc);
826 gncEntrySetAction (dest, src->action);
827 gncEntrySetNotes (dest, src->notes);
828 dest->quantity = src->quantity;
829
830 dest->i_account = src->i_account;
831 dest->i_price = src->i_price;
832 dest->i_taxable = src->i_taxable;
833 dest->i_taxincluded = src->i_taxincluded;
834 dest->i_discount = src->i_discount;
835 dest->i_disc_type = src->i_disc_type;
836 dest->i_disc_how = src->i_disc_how;
837
838 /* vendor bill data */
839 dest->b_account = src->b_account;
840 dest->b_price = src->b_price;
841 dest->b_taxable = src->b_taxable;
842 dest->b_taxincluded = src->b_taxincluded;
843 dest->billable = src->billable;
844 dest->billto = src->billto;
845
846 if (src->i_tax_table)
847 gncEntrySetInvTaxTable (dest, src->i_tax_table);
848
849 if (src->b_tax_table)
850 gncEntrySetBillTaxTable (dest, src->b_tax_table);
851
852 if (add_entry)
853 {
854 if (src->order)
855 gncOrderAddEntry (src->order, dest);
856
857 if (src->invoice)
858 gncInvoiceAddEntry (src->invoice, dest);
859
860 if (src->bill)
861 gncBillAddEntry (src->bill, dest);
862 }
863
864 dest->values_dirty = TRUE;
865 mark_entry (dest);
866 gncEntryCommitEdit (dest);
867 }
868
869 /* ================================================================ */
870 /* Get Functions */
871
gncEntryGetDate(const GncEntry * entry)872 time64 gncEntryGetDate (const GncEntry *entry)
873 {
874 return entry ? entry->date : 0;
875 }
876
gncEntryGetDateGDate(const GncEntry * entry)877 GDate gncEntryGetDateGDate(const GncEntry *entry)
878 {
879 return time64_to_gdate(gncEntryGetDate(entry));
880 }
881
gncEntryGetDateEntered(const GncEntry * entry)882 time64 gncEntryGetDateEntered (const GncEntry *entry)
883 {
884 return entry ? entry->date_entered : 0;
885 }
886
gncEntryGetDescription(const GncEntry * entry)887 const char * gncEntryGetDescription (const GncEntry *entry)
888 {
889 if (!entry) return NULL;
890 return entry->desc;
891 }
892
gncEntryGetAction(const GncEntry * entry)893 const char * gncEntryGetAction (const GncEntry *entry)
894 {
895 if (!entry) return NULL;
896 return entry->action;
897 }
898
gncEntryGetNotes(const GncEntry * entry)899 const char * gncEntryGetNotes (const GncEntry *entry)
900 {
901 if (!entry) return NULL;
902 return entry->notes;
903 }
904
gncEntryGetQuantity(const GncEntry * entry)905 gnc_numeric gncEntryGetQuantity (const GncEntry *entry)
906 {
907 if (!entry) return gnc_numeric_zero();
908 return entry->quantity;
909 }
910
gncEntryGetDocQuantity(const GncEntry * entry,gboolean is_cn)911 gnc_numeric gncEntryGetDocQuantity (const GncEntry *entry, gboolean is_cn)
912 {
913 gnc_numeric value = gncEntryGetQuantity (entry);
914 return (is_cn ? gnc_numeric_neg (value) : value);
915 }
916
917 /* Customer Invoice */
918
gncEntryGetInvAccount(const GncEntry * entry)919 Account * gncEntryGetInvAccount (const GncEntry *entry)
920 {
921 if (!entry) return NULL;
922 return entry->i_account;
923 }
924
gncEntryGetInvPrice(const GncEntry * entry)925 gnc_numeric gncEntryGetInvPrice (const GncEntry *entry)
926 {
927 if (!entry) return gnc_numeric_zero();
928 return entry->i_price;
929 }
930
gncEntryGetInvDiscount(const GncEntry * entry)931 gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry)
932 {
933 if (!entry) return gnc_numeric_zero();
934 return entry->i_discount;
935 }
936
gncEntryGetInvDiscountType(const GncEntry * entry)937 GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry)
938 {
939 if (!entry) return 0;
940 return entry->i_disc_type;
941 }
942
gncEntryGetInvDiscountHow(const GncEntry * entry)943 GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry)
944 {
945 if (!entry) return 0;
946 return entry->i_disc_how;
947 }
948
qofEntryGetInvDiscType(const GncEntry * entry)949 char* qofEntryGetInvDiscType (const GncEntry *entry)
950 {
951 char *type_string;
952
953 if (!entry) return 0;
954 type_string = g_strdup(gncAmountTypeToString(entry->i_disc_type));
955 return type_string;
956 }
957
qofEntryGetInvDiscHow(const GncEntry * entry)958 char* qofEntryGetInvDiscHow (const GncEntry *entry)
959 {
960 char *type_string;
961
962 if (!entry) return 0;
963 type_string = g_strdup(gncEntryDiscountHowToString(entry->i_disc_how));
964 return type_string;
965 }
966
gncEntryGetInvTaxable(const GncEntry * entry)967 gboolean gncEntryGetInvTaxable (const GncEntry *entry)
968 {
969 if (!entry) return FALSE;
970 return entry->i_taxable;
971 }
972
gncEntryGetInvTaxIncluded(const GncEntry * entry)973 gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry)
974 {
975 if (!entry) return FALSE;
976 return entry->i_taxincluded;
977 }
978
gncEntryGetInvTaxTable(const GncEntry * entry)979 GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry)
980 {
981 if (!entry) return NULL;
982 return entry->i_tax_table;
983 }
984
985 /* vendor bills */
986
gncEntryGetBillAccount(const GncEntry * entry)987 Account * gncEntryGetBillAccount (const GncEntry *entry)
988 {
989 if (!entry) return NULL;
990 return entry->b_account;
991 }
992
gncEntryGetBillPrice(const GncEntry * entry)993 gnc_numeric gncEntryGetBillPrice (const GncEntry *entry)
994 {
995 if (!entry) return gnc_numeric_zero();
996 return entry->b_price;
997 }
998
gncEntryGetBillTaxable(const GncEntry * entry)999 gboolean gncEntryGetBillTaxable (const GncEntry *entry)
1000 {
1001 if (!entry) return FALSE;
1002 return entry->b_taxable;
1003 }
1004
gncEntryGetBillTaxIncluded(const GncEntry * entry)1005 gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry)
1006 {
1007 if (!entry) return FALSE;
1008 return entry->b_taxincluded;
1009 }
1010
gncEntryGetBillTaxTable(const GncEntry * entry)1011 GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry)
1012 {
1013 if (!entry) return NULL;
1014 return entry->b_tax_table;
1015 }
1016
gncEntryGetBillable(const GncEntry * entry)1017 gboolean gncEntryGetBillable (const GncEntry *entry)
1018 {
1019 if (!entry) return FALSE;
1020 return entry->billable;
1021 }
1022
gncEntryGetBillTo(GncEntry * entry)1023 GncOwner * gncEntryGetBillTo (GncEntry *entry)
1024 {
1025 if (!entry) return NULL;
1026 return &entry->billto;
1027 }
1028
gncEntryGetBillPayment(const GncEntry * entry)1029 GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry)
1030 {
1031 if (!entry) return 0;
1032 return entry->b_payment;
1033 }
1034
gncEntryGetInvoice(const GncEntry * entry)1035 GncInvoice * gncEntryGetInvoice (const GncEntry *entry)
1036 {
1037 if (!entry) return NULL;
1038 return entry->invoice;
1039 }
1040
gncEntryGetBill(const GncEntry * entry)1041 GncInvoice * gncEntryGetBill (const GncEntry *entry)
1042 {
1043 if (!entry) return NULL;
1044 return entry->bill;
1045 }
1046
gncEntryGetOrder(const GncEntry * entry)1047 GncOrder * gncEntryGetOrder (const GncEntry *entry)
1048 {
1049 if (!entry) return NULL;
1050 return entry->order;
1051 }
1052
1053 /* ================================================================ */
1054 /*
1055 * This is the logic of computing the total for an Entry, so you know
1056 * what values to put into various Splits or to display in the ledger.
1057 * In other words, we combine the quantity, unit-price, discount and
1058 * taxes together, depending on various flags.
1059 *
1060 * There are four potential ways to combine these numbers:
1061 * Discount: Pre-Tax Post-Tax
1062 * Tax : Included Not-Included
1063 *
1064 * The process is relatively simple:
1065 *
1066 * 1) compute the aggregate price (price*qty)
1067 * 2) if taxincluded, then back-compute the aggregate pre-tax price
1068 * 3) apply discount and taxes in the appropriate order
1069 * 4) return the requested results.
1070 *
1071 * Step 2 can be done with aggregate taxes; no need to compute them all
1072 * unless the caller asked for the tax_value.
1073 *
1074 * Note that the returned "value" is such that
1075 * value + tax == "total to pay"
1076 * which means in the case of tax-included that the returned
1077 * "value" may be less than the aggregate price, even without a
1078 * discount. If you want to display the tax-included value, you need
1079 * to add the value and taxes together. In other words, the value is
1080 * the amount the merchant gets; the taxes are the amount the gov't
1081 * gets, and the customer pays the sum or value + taxes.
1082 *
1083 * The discount return value is just for entertainment -- you may want
1084 * to let a consumer know how much they saved.
1085 *
1086 * Note this function will not do any rounding unless forced to prevent overflow.
1087 * It's the caller's responsibility to round to the proper commodity
1088 * denominator if needed.
1089 */
gncEntryComputeValueInt(gnc_numeric qty,gnc_numeric price,const GncTaxTable * tax_table,gboolean tax_included,gnc_numeric discount,GncAmountType discount_type,GncDiscountHow discount_how,gnc_numeric * value,gnc_numeric * discount_value,GList ** tax_value,gnc_numeric * net_price)1090 static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
1091 const GncTaxTable *tax_table, gboolean tax_included,
1092 gnc_numeric discount, GncAmountType discount_type,
1093 GncDiscountHow discount_how,
1094 gnc_numeric *value, gnc_numeric *discount_value,
1095 GList **tax_value, gnc_numeric *net_price)
1096 {
1097 gnc_numeric aggregate;
1098 gnc_numeric pretax;
1099 gnc_numeric result;
1100 gnc_numeric tax;
1101 gnc_numeric percent = gnc_numeric_create (100, 1);
1102 gnc_numeric tpercent = gnc_numeric_zero ();
1103 gnc_numeric tvalue = gnc_numeric_zero ();
1104 gnc_numeric i_net_price = price;
1105
1106 GList * entries = gncTaxTableGetEntries (tax_table);
1107 GList * node;
1108
1109 /* Step 1: compute the aggregate price */
1110
1111 aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1112
1113 /* Step 2: compute the pre-tax aggregate */
1114
1115 /* First, compute the aggregate tpercent and tvalue numbers */
1116 for (node = entries; node; node = node->next)
1117 {
1118 GncTaxTableEntry *entry = node->data;
1119 gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
1120
1121 switch (gncTaxTableEntryGetType (entry))
1122 {
1123 case GNC_AMT_TYPE_VALUE:
1124 tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO,
1125 GNC_HOW_DENOM_LCD);
1126 break;
1127 case GNC_AMT_TYPE_PERCENT:
1128 tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO,
1129 GNC_HOW_DENOM_LCD);
1130 break;
1131 default:
1132 g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry));
1133 break;
1134 }
1135 }
1136 /* now we need to convert from 5% -> .05 */
1137 tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO,
1138 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
1139
1140 /* Next, actually compute the pre-tax aggregate value based on the
1141 * taxincluded flag.
1142 */
1143 if (tax_table && tax_included)
1144 {
1145 /* Back-compute the pre-tax aggregate value.
1146 * We know that aggregate = pretax + pretax*tpercent + tvalue, so
1147 * pretax = (aggregate-tvalue)/(1+tpercent)
1148 */
1149 pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO,
1150 GNC_HOW_DENOM_LCD);
1151 pretax = gnc_numeric_div (pretax,
1152 gnc_numeric_add (tpercent,
1153 gnc_numeric_create (1, 1),
1154 GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
1155 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1156 if (!gnc_numeric_zero_p(qty))
1157 {
1158 i_net_price = gnc_numeric_div (pretax, qty, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1159 }
1160 }
1161 else
1162 {
1163 pretax = aggregate;
1164 }
1165
1166 /* Step 3: apply discount and taxes in the appropriate order */
1167
1168 /*
1169 * There are two ways to apply discounts and taxes. In one way, you
1170 * always compute the discount off the pretax number, and compute
1171 * the taxes off of either the pretax value or "pretax-discount"
1172 * value. In the other way, you always compute the tax on "pretax",
1173 * and compute the discount on either "pretax" or "pretax+taxes".
1174 *
1175 * I don't know which is the "correct" way.
1176 */
1177
1178 /*
1179 * Type: discount tax
1180 * PRETAX pretax pretax-discount
1181 * SAMETIME pretax pretax
1182 * POSTTAX pretax+tax pretax
1183 */
1184
1185 switch (discount_how)
1186 {
1187 case GNC_DISC_PRETAX:
1188 case GNC_DISC_SAMETIME:
1189 /* compute the discount from pretax */
1190
1191 if (discount_type == GNC_AMT_TYPE_PERCENT)
1192 {
1193 discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
1194 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
1195 discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO,
1196 GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1197 }
1198
1199 result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1200
1201 /* Figure out when to apply the tax, pretax or pretax-discount */
1202 if (discount_how == GNC_DISC_PRETAX)
1203 pretax = result;
1204 break;
1205
1206 case GNC_DISC_POSTTAX:
1207 /* compute discount on pretax+taxes */
1208
1209 if (discount_type == GNC_AMT_TYPE_PERCENT)
1210 {
1211 gnc_numeric after_tax;
1212
1213 tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1214 after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1215 after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO,
1216 GNC_HOW_DENOM_LCD);
1217 discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
1218 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
1219 discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO,
1220 GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1221 }
1222
1223 result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
1224 break;
1225
1226 default:
1227 g_warning ("unknown DiscountHow value: %d", discount_how);
1228 break;
1229 }
1230
1231 /* Step 4: return the requested results. */
1232
1233 /* result == amount merchant gets
1234 * discount == amount of discount
1235 * need to compute taxes (based on 'pretax') if the caller wants it.
1236 */
1237
1238 if (discount_value != NULL)
1239 *discount_value = discount;
1240
1241 if (value != NULL)
1242 *value = result;
1243
1244 /* Now... Compute the list of tax values (if the caller wants it) */
1245
1246 if (tax_value != NULL)
1247 {
1248 GList * taxes = NULL;
1249
1250 for (node = entries; node; node = node->next)
1251 {
1252 GncTaxTableEntry *entry = node->data;
1253 Account *acc = gncTaxTableEntryGetAccount (entry);
1254 gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
1255
1256 g_return_if_fail (acc);
1257
1258 switch (gncTaxTableEntryGetType (entry))
1259 {
1260 case GNC_AMT_TYPE_VALUE:
1261 taxes = gncAccountValueAdd (taxes, acc, amount);
1262 break;
1263 case GNC_AMT_TYPE_PERCENT:
1264 amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO,
1265 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
1266 tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
1267 taxes = gncAccountValueAdd (taxes, acc, tax);
1268 break;
1269 default:
1270 break;
1271 }
1272 }
1273 *tax_value = taxes;
1274 }
1275
1276 if (net_price != NULL)
1277 *net_price = i_net_price;
1278
1279 return;
1280 }
1281
gncEntryComputeValue(gnc_numeric qty,gnc_numeric price,const GncTaxTable * tax_table,gboolean tax_included,gnc_numeric discount,GncAmountType discount_type,GncDiscountHow discount_how,G_GNUC_UNUSED int SCU,gnc_numeric * value,gnc_numeric * discount_value,GList ** tax_value)1282 void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price,
1283 const GncTaxTable *tax_table, gboolean tax_included,
1284 gnc_numeric discount, GncAmountType discount_type,
1285 GncDiscountHow discount_how, G_GNUC_UNUSED int SCU,
1286 gnc_numeric *value, gnc_numeric *discount_value,
1287 GList **tax_value)
1288 {
1289 gncEntryComputeValueInt (qty, price, tax_table, tax_included, discount, discount_type,
1290 discount_how, value, discount_value, tax_value, NULL);
1291 }
1292
1293 static int
get_entry_commodity_denom(const GncEntry * entry)1294 get_entry_commodity_denom (const GncEntry *entry)
1295 {
1296 gnc_commodity *c;
1297 if (!entry)
1298 return 0;
1299 if (entry->invoice)
1300 {
1301 c = gncInvoiceGetCurrency (entry->invoice);
1302 if (c)
1303 return (gnc_commodity_get_fraction (c));
1304 }
1305 if (entry->bill)
1306 {
1307 c = gncInvoiceGetCurrency (entry->bill);
1308 if (c)
1309 return (gnc_commodity_get_fraction (c));
1310 }
1311 return 100000;
1312 }
1313
1314 static void
gncEntryRecomputeValues(GncEntry * entry)1315 gncEntryRecomputeValues (GncEntry *entry)
1316 {
1317 int denom;
1318 GList *tv_iter;
1319
1320 /* See if either tax table changed since we last computed values */
1321 if (entry->i_tax_table)
1322 {
1323 time64 modtime = gncTaxTableLastModifiedSecs (entry->i_tax_table);
1324 if (entry->i_taxtable_modtime != modtime)
1325 {
1326 entry->values_dirty = TRUE;
1327 entry->i_taxtable_modtime = modtime;
1328 }
1329 }
1330 if (entry->b_tax_table)
1331 {
1332 time64 modtime = gncTaxTableLastModifiedSecs (entry->b_tax_table);
1333 if (entry->b_taxtable_modtime != modtime)
1334 {
1335 entry->values_dirty = TRUE;
1336 entry->b_taxtable_modtime = modtime;
1337 }
1338 }
1339
1340 if (!entry->values_dirty)
1341 return;
1342
1343 /* Clear the last-computed tax values */
1344 if (entry->i_tax_values)
1345 {
1346 gncAccountValueDestroy (entry->i_tax_values);
1347 entry->i_tax_values = NULL;
1348 }
1349 if (entry->b_tax_values)
1350 {
1351 gncAccountValueDestroy (entry->b_tax_values);
1352 entry->b_tax_values = NULL;
1353 }
1354
1355 /* Determine the commodity denominator */
1356 denom = get_entry_commodity_denom (entry);
1357
1358 /* Compute the invoice values */
1359 gncEntryComputeValue (entry->quantity, entry->i_price,
1360 (entry->i_taxable ? entry->i_tax_table : NULL),
1361 entry->i_taxincluded,
1362 entry->i_discount, entry->i_disc_type,
1363 entry->i_disc_how,
1364 denom,
1365 &(entry->i_value), &(entry->i_disc_value),
1366 &(entry->i_tax_values));
1367
1368 /* Compute the bill values */
1369 gncEntryComputeValue (entry->quantity, entry->b_price,
1370 (entry->b_taxable ? entry->b_tax_table : NULL),
1371 entry->b_taxincluded,
1372 gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
1373 denom,
1374 &(entry->b_value), NULL, &(entry->b_tax_values));
1375
1376 entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom,
1377 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1378 entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom,
1379 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1380 entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values);
1381 entry->i_tax_value_rounded = gnc_numeric_zero();
1382 for (tv_iter = entry->i_tax_values; tv_iter; tv_iter=tv_iter->next)
1383 {
1384 GncAccountValue *acc_val = tv_iter->data;
1385 entry->i_tax_value_rounded = gnc_numeric_add (entry->i_tax_value_rounded, acc_val->value,
1386 denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1387 }
1388
1389 entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom,
1390 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1391 entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values);
1392 entry->b_tax_value_rounded = gnc_numeric_zero();
1393 for (tv_iter = entry->b_tax_values; tv_iter; tv_iter=tv_iter->next)
1394 {
1395 GncAccountValue *acc_val = tv_iter->data;
1396 entry->b_tax_value_rounded = gnc_numeric_add (entry->b_tax_value_rounded, acc_val->value,
1397 denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1398 }
1399 entry->values_dirty = FALSE;
1400 }
1401
1402 /* The "Int" functions below are for internal use only.
1403 * Outside this file, use the "Doc" or "Bal" variants found below instead. */
gncEntryGetIntValue(GncEntry * entry,gboolean round,gboolean is_cust_doc)1404 static gnc_numeric gncEntryGetIntValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
1405 {
1406 if (!entry) return gnc_numeric_zero();
1407 gncEntryRecomputeValues (entry);
1408 if (round)
1409 return (is_cust_doc ? entry->i_value_rounded : entry->b_value_rounded);
1410 else
1411 return (is_cust_doc ? entry->i_value : entry->b_value);
1412 }
1413
gncEntryGetIntTaxValue(GncEntry * entry,gboolean round,gboolean is_cust_doc)1414 static gnc_numeric gncEntryGetIntTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
1415 {
1416 if (!entry) return gnc_numeric_zero();
1417 gncEntryRecomputeValues (entry);
1418 if (round)
1419 return (is_cust_doc ? entry->i_tax_value_rounded : entry->b_tax_value_rounded);
1420 else
1421 return (is_cust_doc ? entry->i_tax_value : entry->b_tax_value);
1422 }
1423
1424 /* Careful: the returned list is managed by the entry, and will only be valid for a short time */
gncEntryGetIntTaxValues(GncEntry * entry,gboolean is_cust_doc)1425 static AccountValueList * gncEntryGetIntTaxValues (GncEntry *entry, gboolean is_cust_doc)
1426 {
1427 if (!entry) return NULL;
1428 gncEntryRecomputeValues (entry);
1429 return (is_cust_doc ? entry->i_tax_values : entry->b_tax_values);
1430 }
1431
gncEntryGetIntDiscountValue(GncEntry * entry,gboolean round,gboolean is_cust_doc)1432 static gnc_numeric gncEntryGetIntDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
1433 {
1434 if (!entry) return gnc_numeric_zero();
1435 gncEntryRecomputeValues (entry);
1436 if (round)
1437 return (is_cust_doc ? entry->i_disc_value_rounded : gnc_numeric_zero());
1438 else
1439 return (is_cust_doc ? entry->i_disc_value : gnc_numeric_zero());
1440 }
1441
1442 /* Note contrary to the GetDoc*Value and GetBal*Value functions below
1443 * this function will always round the net price to the entry's
1444 * currency denominator (being the invoice/bill denom or 100000 if not
1445 * included in a bill or invoice) */
gncEntryGetPrice(const GncEntry * entry,gboolean cust_doc,gboolean net)1446 gnc_numeric gncEntryGetPrice (const GncEntry *entry, gboolean cust_doc, gboolean net)
1447 {
1448 gnc_numeric result;
1449 int denom;
1450
1451 if (!entry) return gnc_numeric_zero();
1452 if (!net) return (cust_doc ? entry->i_price : entry->b_price);
1453
1454
1455 /* Compute the net price */
1456 if (cust_doc)
1457 gncEntryComputeValueInt (entry->quantity, entry->i_price,
1458 (entry->i_taxable ? entry->i_tax_table : NULL),
1459 entry->i_taxincluded,
1460 entry->i_discount, entry->i_disc_type,
1461 entry->i_disc_how,
1462 NULL, NULL, NULL, &result);
1463 else
1464 gncEntryComputeValueInt (entry->quantity, entry->b_price,
1465 (entry->b_taxable ? entry->b_tax_table : NULL),
1466 entry->b_taxincluded,
1467 gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
1468 NULL, NULL, NULL, &result);
1469
1470 /* Determine the commodity denominator */
1471 denom = get_entry_commodity_denom (entry);
1472
1473 result = gnc_numeric_convert (result, denom,
1474 GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
1475
1476 return result;
1477 }
1478
gncEntryGetDocValue(GncEntry * entry,gboolean round,gboolean is_cust_doc,gboolean is_cn)1479 gnc_numeric gncEntryGetDocValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
1480 {
1481 gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
1482 return (is_cn ? gnc_numeric_neg (value) : value);
1483 }
1484
gncEntryGetDocTaxValue(GncEntry * entry,gboolean round,gboolean is_cust_doc,gboolean is_cn)1485 gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
1486 {
1487 gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
1488 return (is_cn ? gnc_numeric_neg (value) : value);
1489 }
1490
1491 /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
gncEntryGetDocTaxValues(GncEntry * entry,gboolean is_cust_doc,gboolean is_cn)1492 AccountValueList * gncEntryGetDocTaxValues (GncEntry *entry, gboolean is_cust_doc, gboolean is_cn)
1493 {
1494 AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
1495 AccountValueList *values = NULL, *node;
1496
1497 /* Make a copy of the list with negated values if necessary. */
1498 for (node = int_values; node; node = node->next)
1499 {
1500 GncAccountValue *acct_val = node->data;
1501 values = gncAccountValueAdd (values, acct_val->account,
1502 (is_cn ? gnc_numeric_neg (acct_val->value)
1503 : acct_val->value));
1504 }
1505
1506 return values;
1507 }
1508
gncEntryGetDocDiscountValue(GncEntry * entry,gboolean round,gboolean is_cust_doc,gboolean is_cn)1509 gnc_numeric gncEntryGetDocDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
1510 {
1511 gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
1512 return (is_cn ? gnc_numeric_neg (value) : value);
1513 }
1514
gncEntryGetBalValue(GncEntry * entry,gboolean round,gboolean is_cust_doc)1515 gnc_numeric gncEntryGetBalValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
1516 {
1517 gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
1518 return (is_cust_doc ? gnc_numeric_neg (value) : value);
1519 }
1520
gncEntryGetBalTaxValue(GncEntry * entry,gboolean round,gboolean is_cust_doc)1521 gnc_numeric gncEntryGetBalTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
1522 {
1523 gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
1524 return (is_cust_doc ? gnc_numeric_neg (value) : value);
1525 }
1526
1527 /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
gncEntryGetBalTaxValues(GncEntry * entry,gboolean is_cust_doc)1528 AccountValueList * gncEntryGetBalTaxValues (GncEntry *entry, gboolean is_cust_doc)
1529 {
1530 AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
1531 AccountValueList *values = NULL, *node;
1532
1533 /* Make a copy of the list with negated values if necessary. */
1534 for (node = int_values; node; node = node->next)
1535 {
1536 GncAccountValue *acct_val = node->data;
1537 values = gncAccountValueAdd (values, acct_val->account,
1538 (is_cust_doc ? gnc_numeric_neg (acct_val->value)
1539 : acct_val->value));
1540 }
1541
1542 return values;
1543 }
1544
gncEntryGetBalDiscountValue(GncEntry * entry,gboolean round,gboolean is_cust_doc)1545 gnc_numeric gncEntryGetBalDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
1546 {
1547 gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
1548 return (is_cust_doc ? gnc_numeric_neg (value) : value);
1549 }
1550
1551 /* XXX this existence of this routine is just wrong */
gncEntryIsOpen(const GncEntry * entry)1552 gboolean gncEntryIsOpen (const GncEntry *entry)
1553 {
1554 if (!entry) return FALSE;
1555 return (qof_instance_get_editlevel(entry) > 0);
1556 }
1557
1558 /* ================================================================ */
1559
gncEntryBeginEdit(GncEntry * entry)1560 void gncEntryBeginEdit (GncEntry *entry)
1561 {
1562 qof_begin_edit(&entry->inst);
1563 }
1564
gncEntryOnError(QofInstance * entry,QofBackendError errcode)1565 static void gncEntryOnError (QofInstance *entry, QofBackendError errcode)
1566 {
1567 PERR("Entry QofBackend Failure: %d", errcode);
1568 gnc_engine_signal_commit_error( errcode );
1569 }
1570
gncEntryOnDone(QofInstance * inst)1571 static void gncEntryOnDone (QofInstance *inst) {}
1572
entry_free(QofInstance * inst)1573 static void entry_free (QofInstance *inst)
1574 {
1575 GncEntry *entry = (GncEntry *)inst;
1576 gncEntryFree (entry);
1577 }
1578
gncEntryCommitEdit(GncEntry * entry)1579 void gncEntryCommitEdit (GncEntry *entry)
1580 {
1581 /* GnuCash 2.6.3 and earlier didn't handle entry kvp's... */
1582 if (qof_instance_has_kvp(QOF_INSTANCE(entry)))
1583 gnc_features_set_used (qof_instance_get_book (QOF_INSTANCE (entry)),
1584 GNC_FEATURE_KVP_EXTRA_DATA);
1585
1586 if (!qof_commit_edit (QOF_INSTANCE(entry))) return;
1587 qof_commit_edit_part2 (&entry->inst, gncEntryOnError,
1588 gncEntryOnDone, entry_free);
1589 }
1590
gncEntryCompare(const GncEntry * a,const GncEntry * b)1591 int gncEntryCompare (const GncEntry *a, const GncEntry *b)
1592 {
1593 int compare;
1594
1595 if (a == b) return 0;
1596 if (!a && b) return -1;
1597 if (a && !b) return 1;
1598 g_assert (a && b); /* Silence a static analysis warning. */
1599 if (a->date != b->date) return a->date - b->date;
1600 if (a->date_entered != b->date_entered) return a->date_entered - b->date_entered;
1601
1602 compare = g_strcmp0 (a->desc, b->desc);
1603 if (compare) return compare;
1604
1605 compare = g_strcmp0 (a->action, b->action);
1606 if (compare) return compare;
1607
1608 return qof_instance_guid_compare(a, b);
1609 }
1610
1611 #define CHECK_STRING(X, Y, FIELD) \
1612 if (g_strcmp0((X)->FIELD, (Y)->FIELD) != 0) \
1613 { \
1614 PWARN("%s differ: %s vs %s", #FIELD, (X)->FIELD, (Y)->FIELD); \
1615 return FALSE; \
1616 }
1617
1618 #define CHECK_ACCOUNT(X, Y, FIELD) \
1619 if (!xaccAccountEqual((X)->FIELD, (Y)->FIELD, TRUE)) \
1620 { \
1621 PWARN("%s differ", #FIELD); \
1622 return FALSE; \
1623 }
1624
1625 #define CHECK_NUMERIC(X, Y, FIELD) \
1626 if (!gnc_numeric_equal((X)->FIELD, (Y)->FIELD)) \
1627 { \
1628 PWARN("%s differ", #FIELD); \
1629 return FALSE; \
1630 }
1631
1632 #define CHECK_VALUE(X, Y, FIELD) \
1633 if ((X)->FIELD != (Y)->FIELD) \
1634 { \
1635 PWARN("%s differ", #FIELD); \
1636 return FALSE; \
1637 }
1638
1639
1640 /* ============================================================= */
1641 /* Object declaration */
1642
1643 static void
destroy_entry_on_book_close(QofInstance * ent,gpointer data)1644 destroy_entry_on_book_close(QofInstance *ent, gpointer data)
1645 {
1646 GncEntry* entry = GNC_ENTRY(ent);
1647
1648 gncEntryBeginEdit(entry);
1649 gncEntryDestroy(entry);
1650 }
1651
1652 /** Handles book end - frees all entries from the book
1653 *
1654 * @param book Book being closed
1655 */
1656 static void
gnc_entry_book_end(QofBook * book)1657 gnc_entry_book_end(QofBook* book)
1658 {
1659 QofCollection *col;
1660
1661 col = qof_book_get_collection(book, GNC_ID_ENTRY);
1662 qof_collection_foreach(col, destroy_entry_on_book_close, NULL);
1663 }
1664
1665 static QofObject gncEntryDesc =
1666 {
1667 DI(.interface_version = ) QOF_OBJECT_VERSION,
1668 DI(.e_type = ) _GNC_MOD_NAME,
1669 DI(.type_label = ) "Order/Invoice/Bill Entry",
1670 DI(.create = ) (gpointer)gncEntryCreate,
1671 DI(.book_begin = ) NULL,
1672 DI(.book_end = ) gnc_entry_book_end,
1673 DI(.is_dirty = ) qof_collection_is_dirty,
1674 DI(.mark_clean = ) qof_collection_mark_clean,
1675 DI(.foreach = ) qof_collection_foreach,
1676 DI(.printable = ) NULL,
1677 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
1678 };
1679
gncEntryRegister(void)1680 gboolean gncEntryRegister (void)
1681 {
1682 static QofParam params[] =
1683 {
1684 { ENTRY_DATE, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDate, (QofSetterFunc)gncEntrySetDate },
1685 { ENTRY_DATE_ENTERED, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDateEntered, (QofSetterFunc)gncEntrySetDateEntered },
1686 { ENTRY_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetDescription, (QofSetterFunc)gncEntrySetDescription },
1687 { ENTRY_ACTION, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetAction, (QofSetterFunc)gncEntrySetAction },
1688 { ENTRY_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetNotes, (QofSetterFunc)gncEntrySetNotes },
1689 { ENTRY_QTY, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetQuantity, (QofSetterFunc)gncEntrySetQuantity },
1690 { ENTRY_IPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetInvPrice, (QofSetterFunc)gncEntrySetInvPrice },
1691 { ENTRY_BPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetBillPrice, (QofSetterFunc)gncEntrySetBillPrice },
1692 { ENTRY_INVOICE, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetInvoice, NULL },
1693 { ENTRY_IACCT, GNC_ID_ACCOUNT, (QofAccessFunc)gncEntryGetInvAccount, (QofSetterFunc)gncEntrySetInvAccount },
1694 { ENTRY_BACCT, GNC_ID_ACCOUNT, (QofAccessFunc)gncEntryGetBillAccount, (QofSetterFunc)gncEntrySetBillAccount },
1695 { ENTRY_BILL, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetBill, NULL },
1696 {
1697 ENTRY_INV_DISC_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscType,
1698 (QofSetterFunc)qofEntrySetInvDiscType
1699 },
1700 {
1701 ENTRY_INV_DISC_HOW, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscHow,
1702 (QofSetterFunc)qofEntrySetInvDiscHow
1703 },
1704 {
1705 ENTRY_INV_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxable,
1706 (QofSetterFunc)gncEntrySetInvTaxable
1707 },
1708 {
1709 ENTRY_INV_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxIncluded,
1710 (QofSetterFunc)gncEntrySetInvTaxIncluded
1711 },
1712 {
1713 ENTRY_BILL_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxable,
1714 (QofSetterFunc)gncEntrySetBillTaxable
1715 },
1716 {
1717 ENTRY_BILL_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxIncluded,
1718 (QofSetterFunc)gncEntrySetBillTaxIncluded
1719 },
1720 { ENTRY_BILLABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillable, (QofSetterFunc)gncEntrySetBillable },
1721 { ENTRY_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncEntryGetBillTo, (QofSetterFunc)gncEntrySetBillTo },
1722 { ENTRY_ORDER, GNC_ID_ORDER, (QofAccessFunc)gncEntryGetOrder, NULL },
1723 { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
1724 { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
1725 { NULL },
1726 };
1727
1728 qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncEntryCompare, params);
1729
1730 return qof_object_register (&gncEntryDesc);
1731 }
1732