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