GnuCash  5.6-150-g038405b370+
gncBillTerm.c
1 /********************************************************************\
2  * gncBillTerm.c -- the Gnucash Billing Terms 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) 2002 Derek Atkins
25  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
26  * Author: Derek Atkins <warlord@MIT.EDU>
27  */
28 
29 #include <config.h>
30 
31 #include <glib.h>
32 #include <qofinstance-p.h>
33 
34 #include "gnc-engine.h"
35 #include "gncBillTermP.h"
36 
38 {
39  QofInstance inst;
40 
41  /* 'visible' data fields directly manipulated by user */
42  const char * name;
43  const char * desc;
44  GncBillTermType type;
45  gint due_days;
46  gint disc_days;
47  gnc_numeric discount;
48  gint cutoff;
49 
50  /* Internal management fields */
51  /* See libgnucash/engine/TaxTableBillTermImmutability.txt for an explanation of the following */
52  /* Code that handles this is *identical* to that in gncTaxTable */
53  gint64 refcount;
54  GncBillTerm * parent; /* if non-null, we are an immutable child */
55  GncBillTerm * child; /* if non-null, we have not changed */
56  gboolean invisible;
57  GList * children; /* list of children for disconnection */
58 };
59 
61 {
62  QofInstanceClass parent_class;
63 };
64 
65 struct _book_info
66 {
67  GList * terms; /* visible terms */
68 };
69 
70 static QofLogModule log_module = GNC_MOD_BUSINESS;
71 
72 #define _GNC_MOD_NAME GNC_ID_BILLTERM
73 
74 #define SET_STR(obj, member, str) { \
75  if (!g_strcmp0 (member, str)) return; \
76  gncBillTermBeginEdit (obj); \
77  CACHE_REPLACE(member, str); \
78  }
79 
80 AS_STRING_DEC(GncBillTermType, ENUM_TERMS_TYPE)
81 FROM_STRING_DEC(GncBillTermType, ENUM_TERMS_TYPE)
82 
83 /* ============================================================== */
84 /* Misc inline utilities */
85 
86 static inline void
87 mark_term (GncBillTerm *term)
88 {
89  qof_instance_set_dirty(&term->inst);
90  qof_event_gen (&term->inst, QOF_EVENT_MODIFY, NULL);
91 }
92 
93 static inline void maybe_resort_list (GncBillTerm *term)
94 {
95  struct _book_info *bi;
96 
97  if (term->parent || term->invisible) return;
98  bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME);
99  bi->terms = g_list_sort (bi->terms, (GCompareFunc)gncBillTermCompare);
100 }
101 
102 static inline void addObj (GncBillTerm *term)
103 {
104  struct _book_info *bi;
105  bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME);
106  bi->terms = g_list_insert_sorted (bi->terms, term,
107  (GCompareFunc)gncBillTermCompare);
108 }
109 
110 static inline void remObj (GncBillTerm *term)
111 {
112  struct _book_info *bi;
113  bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME);
114  bi->terms = g_list_remove (bi->terms, term);
115 }
116 
117 static inline void
118 gncBillTermAddChild (GncBillTerm *table, GncBillTerm *child)
119 {
120  g_return_if_fail(qof_instance_get_destroying(table) == FALSE);
121  table->children = g_list_prepend(table->children, child);
122 }
123 
124 static inline void
125 gncBillTermRemoveChild (GncBillTerm *table, GncBillTerm *child)
126 {
127  if (qof_instance_get_destroying(table)) return;
128  table->children = g_list_remove(table->children, child);
129 }
130 
131 /* ============================================================== */
132 
133 enum
134 {
135  PROP_0,
136  PROP_NAME
137 };
138 
139 /* GObject Initialization */
140 G_DEFINE_TYPE(GncBillTerm, gnc_billterm, QOF_TYPE_INSTANCE)
141 
142 static void
143 gnc_billterm_init(GncBillTerm* bt)
144 {
145 }
146 
147 static void
148 gnc_billterm_dispose(GObject *btp)
149 {
150  G_OBJECT_CLASS(gnc_billterm_parent_class)->dispose(btp);
151 }
152 
153 static void
154 gnc_billterm_finalize(GObject* btp)
155 {
156  G_OBJECT_CLASS(gnc_billterm_parent_class)->finalize(btp);
157 }
158 
159 static void
160 gnc_billterm_get_property (GObject *object,
161  guint prop_id,
162  GValue *value,
163  GParamSpec *pspec)
164 {
165  GncBillTerm *bt;
166 
167  g_return_if_fail(GNC_IS_BILLTERM(object));
168 
169  bt = GNC_BILLTERM(object);
170  switch (prop_id)
171  {
172  case PROP_NAME:
173  g_value_set_string(value, bt->name);
174  break;
175  default:
176  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
177  break;
178  }
179 }
180 
181 static void
182 gnc_billterm_set_property (GObject *object,
183  guint prop_id,
184  const GValue *value,
185  GParamSpec *pspec)
186 {
187  GncBillTerm *bt;
188 
189  g_return_if_fail(GNC_IS_BILLTERM(object));
190 
191  bt = GNC_BILLTERM(object);
192  g_assert (qof_instance_get_editlevel(bt));
193 
194  switch (prop_id)
195  {
196  case PROP_NAME:
197  gncBillTermSetName(bt, g_value_get_string(value));
198  break;
199  default:
200  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
201  break;
202  }
203 }
204 
211 static GList*
212 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
213 {
214  /* Bill term doesn't refer to anything except other billterms */
215  return NULL;
216 }
217 
218 static void
219 gnc_billterm_class_init (GncBillTermClass *klass)
220 {
221  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
222  QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
223 
224  gobject_class->dispose = gnc_billterm_dispose;
225  gobject_class->finalize = gnc_billterm_finalize;
226  gobject_class->set_property = gnc_billterm_set_property;
227  gobject_class->get_property = gnc_billterm_get_property;
228 
229  qof_class->get_display_name = NULL;
230  qof_class->refers_to_object = NULL;
231  qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
232 
233  g_object_class_install_property
234  (gobject_class,
235  PROP_NAME,
236  g_param_spec_string ("name",
237  "BillTerm Name",
238  "The bill term name is an arbitrary string "
239  "assigned by the user. It is intended to "
240  "a short, 10 to 30 character long string "
241  "that is displayed by the GUI as the "
242  "billterm mnemonic.",
243  NULL,
244  G_PARAM_READWRITE));
245 }
246 
247 /* Create/Destroy Functions */
248 GncBillTerm * gncBillTermCreate (QofBook *book)
249 {
250  GncBillTerm *term;
251  if (!book) return NULL;
252 
253  term = g_object_new (GNC_TYPE_BILLTERM, NULL);
254  qof_instance_init_data(&term->inst, _GNC_MOD_NAME, book);
255  term->name = CACHE_INSERT ("");
256  term->desc = CACHE_INSERT ("");
257  term->discount = gnc_numeric_zero ();
258  addObj (term);
259  qof_event_gen (&term->inst, QOF_EVENT_CREATE, NULL);
260  return term;
261 }
262 
263 void gncBillTermDestroy (GncBillTerm *term)
264 {
265  gchar guidstr[GUID_ENCODING_LENGTH+1];
266  if (!term) return;
267  guid_to_string_buff(qof_instance_get_guid(&term->inst),guidstr);
268  DEBUG("destroying bill term %s (%p)", guidstr, term);
269  qof_instance_set_destroying(term, TRUE);
270  qof_instance_set_dirty (&term->inst);
271  gncBillTermCommitEdit (term);
272 }
273 
274 static void gncBillTermFree (GncBillTerm *term)
275 {
276  GncBillTerm *child;
277  GList *list;
278 
279  if (!term) return;
280 
281  qof_event_gen (&term->inst, QOF_EVENT_DESTROY, NULL);
282  CACHE_REMOVE (term->name);
283  CACHE_REMOVE (term->desc);
284  remObj (term);
285 
286  if (!qof_instance_get_destroying(term))
287  PERR("free a billterm without do_free set!");
288 
289  /* disconnect from parent */
290  if (term->parent)
291  gncBillTermRemoveChild(term->parent, term);
292 
293  /* disconnect from the children */
294  for (list = term->children; list; list = list->next)
295  {
296  child = list->data;
297  gncBillTermSetParent(child, NULL);
298  }
299  g_list_free(term->children);
300 
301  /* qof_instance_release(&term->inst); */
302  g_object_unref (term);
303 }
304 
305 /* ============================================================== */
306 /* Set Functions */
307 
308 void gncBillTermSetName (GncBillTerm *term, const char *name)
309 {
310  if (!term || !name) return;
311  SET_STR (term, term->name, name);
312  mark_term (term);
313  maybe_resort_list (term);
314  gncBillTermCommitEdit (term);
315 }
316 
317 void gncBillTermSetDescription (GncBillTerm *term, const char *desc)
318 {
319  if (!term || !desc) return;
320  SET_STR (term, term->desc, desc);
321  mark_term (term);
322  maybe_resort_list (term);
323  gncBillTermCommitEdit (term);
324 }
325 
326 void gncBillTermSetType (GncBillTerm *term, GncBillTermType type)
327 {
328  if (!term) return;
329  if (term->type == type) return;
330  gncBillTermBeginEdit (term);
331  term->type = type;
332  mark_term (term);
333  gncBillTermCommitEdit (term);
334 }
335 
337 FROM_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE)
338 
339 static
340 void qofBillTermSetType (GncBillTerm *term, const char *type_label)
341 {
342  GncBillTermType type;
343 
344  type = GncBillTermTypefromString(type_label);
345  gncBillTermSetType(term, type);
346 }
347 
348 void gncBillTermSetDueDays (GncBillTerm *term, gint days)
349 {
350  if (!term) return;
351  if (term->due_days == days) return;
352  gncBillTermBeginEdit (term);
353  term->due_days = days;
354  mark_term (term);
355  gncBillTermCommitEdit (term);
356 }
357 
358 void gncBillTermSetDiscountDays (GncBillTerm *term, gint days)
359 {
360  if (!term) return;
361  if (term->disc_days == days) return;
362  gncBillTermBeginEdit (term);
363  term->disc_days = days;
364  mark_term (term);
365  gncBillTermCommitEdit (term);
366 }
367 
368 void gncBillTermSetDiscount (GncBillTerm *term, gnc_numeric discount)
369 {
370  if (!term) return;
371  if (gnc_numeric_eq (term->discount, discount)) return;
372  gncBillTermBeginEdit (term);
373  term->discount = discount;
374  mark_term (term);
375  gncBillTermCommitEdit (term);
376 }
377 
378 void gncBillTermSetCutoff (GncBillTerm *term, gint cutoff)
379 {
380  if (!term) return;
381  if (term->cutoff == cutoff) return;
382  gncBillTermBeginEdit (term);
383  term->cutoff = cutoff;
384  mark_term (term);
385  gncBillTermCommitEdit (term);
386 }
387 
388 /* XXX this doesn't seem right. If the parent/child relationship
389  * is a doubly-linked list, then there shouldn't be separate set-parent,
390  * set-child routines, else misuse of the routines will goof up
391  * relationships. These ops should be atomic, I think.
392  */
393 void gncBillTermSetParent (GncBillTerm *term, GncBillTerm *parent)
394 {
395  if (!term) return;
396  gncBillTermBeginEdit (term);
397  if (term->parent)
398  gncBillTermRemoveChild(term->parent, term);
399  term->parent = parent;
400  if (parent)
401  gncBillTermAddChild(parent, term);
402  term->refcount = 0;
403  if ( parent != NULL )
404  {
405  gncBillTermMakeInvisible (term);
406  }
407  mark_term (term);
408  gncBillTermCommitEdit (term);
409 }
410 
411 void gncBillTermSetChild (GncBillTerm *term, GncBillTerm *child)
412 {
413  if (!term) return;
414  gncBillTermBeginEdit (term);
415  term->child = child;
416  mark_term (term);
417  gncBillTermCommitEdit (term);
418 }
419 
420 void gncBillTermIncRef (GncBillTerm *term)
421 {
422  if (!term) return;
423  if (term->parent || term->invisible) return; /* children dont need refcounts */
424  gncBillTermBeginEdit (term);
425  term->refcount++;
426  mark_term (term);
427  gncBillTermCommitEdit (term);
428 }
429 
430 void gncBillTermDecRef (GncBillTerm *term)
431 {
432  if (!term) return;
433  if (term->parent || term->invisible) return; /* children dont need refcounts */
434  g_return_if_fail (term->refcount >= 1);
435  gncBillTermBeginEdit (term);
436  term->refcount--;
437  mark_term (term);
438  gncBillTermCommitEdit (term);
439 }
440 
441 void gncBillTermSetRefcount (GncBillTerm *term, gint64 refcount)
442 {
443  if (!term) return;
444  gncBillTermBeginEdit (term);
445  term->refcount = refcount;
446  mark_term (term);
447  gncBillTermCommitEdit (term);
448 }
449 
450 void gncBillTermMakeInvisible (GncBillTerm *term)
451 {
452  if (!term) return;
453  gncBillTermBeginEdit (term);
454  term->invisible = TRUE;
455  remObj (term);
456  mark_term (term);
457  gncBillTermCommitEdit (term);
458 }
459 
460 void gncBillTermChanged (GncBillTerm *term)
461 {
462  if (!term) return;
463  term->child = NULL;
464 }
465 
466 void gncBillTermBeginEdit (GncBillTerm *term)
467 {
468  qof_begin_edit(&term->inst);
469 }
470 
471 static void gncBillTermOnError (QofInstance *inst, QofBackendError errcode)
472 {
473  PERR("BillTerm QofBackend Failure: %d", errcode);
474  gnc_engine_signal_commit_error( errcode );
475 }
476 
477 static void bill_free (QofInstance *inst)
478 {
479  GncBillTerm *term = (GncBillTerm *) inst;
480  gncBillTermFree(term);
481 }
482 
483 static void on_done (QofInstance *inst) {}
484 
485 void gncBillTermCommitEdit (GncBillTerm *term)
486 {
487  if (!qof_commit_edit (QOF_INSTANCE(term))) return;
488  qof_commit_edit_part2 (&term->inst, gncBillTermOnError,
489  on_done, bill_free);
490 }
491 
492 /* Get Functions */
493 
494 GncBillTerm *gncBillTermLookupByName (QofBook *book, const char *name)
495 {
496  GList *list = gncBillTermGetTerms (book);
497 
498  for ( ; list; list = list->next)
499  {
500  GncBillTerm *term = list->data;
501  if (!g_strcmp0 (term->name, name))
502  return list->data;
503  }
504  return NULL;
505 }
506 
507 GList * gncBillTermGetTerms (QofBook *book)
508 {
509  struct _book_info *bi;
510  if (!book) return NULL;
511 
512  bi = qof_book_get_data (book, _GNC_MOD_NAME);
513  return bi->terms;
514 }
515 
516 const char *gncBillTermGetName (const GncBillTerm *term)
517 {
518  if (!term) return NULL;
519  return term->name;
520 }
521 
522 const char *gncBillTermGetDescription (const GncBillTerm *term)
523 {
524  if (!term) return NULL;
525  return term->desc;
526 }
527 
528 GncBillTermType gncBillTermGetType (const GncBillTerm *term)
529 {
530  if (!term) return 0;
531  return term->type;
532 }
533 
535 AS_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE)
536 
537 static
538 const char* qofBillTermGetType (const GncBillTerm *term)
539 {
540  if (!term)
541  {
542  return NULL;
543  }
544  return GncBillTermTypeasString(term->type);
545 }
546 
547 gint gncBillTermGetDueDays (const GncBillTerm *term)
548 {
549  if (!term) return 0;
550  return term->due_days;
551 }
552 
553 gint gncBillTermGetDiscountDays (const GncBillTerm *term)
554 {
555  if (!term) return 0;
556  return term->disc_days;
557 }
558 
559 gnc_numeric gncBillTermGetDiscount (const GncBillTerm *term)
560 {
561  if (!term) return gnc_numeric_zero ();
562  return term->discount;
563 }
564 
565 gint gncBillTermGetCutoff (const GncBillTerm *term)
566 {
567  if (!term) return 0;
568  return term->cutoff;
569 }
570 
571 static GncBillTerm *gncBillTermCopy (const GncBillTerm *term)
572 {
573  GncBillTerm *t;
574 
575  if (!term) return NULL;
576  t = gncBillTermCreate (qof_instance_get_book(term));
577 
578  gncBillTermBeginEdit(t);
579 
580  gncBillTermSetName (t, term->name);
581  gncBillTermSetDescription (t, term->desc);
582 
583  t->type = term->type;
584  t->due_days = term->due_days;
585  t->disc_days = term->disc_days;
586  t->discount = term->discount;
587  t->cutoff = term->cutoff;
588 
589  mark_term (t);
590  gncBillTermCommitEdit(t);
591 
592  return t;
593 }
594 
595 GncBillTerm *gncBillTermReturnChild (GncBillTerm *term, gboolean make_new)
596 {
597  GncBillTerm *child = NULL;
598 
599  if (!term) return NULL;
600  if (term->child) return term->child;
601  if (term->parent || term->invisible) return term;
602  if (make_new)
603  {
604  child = gncBillTermCopy (term);
605  gncBillTermSetChild (term, child);
606  gncBillTermSetParent (child, term);
607  }
608  return child;
609 }
610 
611 GncBillTerm *gncBillTermGetParent (const GncBillTerm *term)
612 {
613  if (!term) return NULL;
614  return term->parent;
615 }
616 
617 gint64 gncBillTermGetRefcount (const GncBillTerm *term)
618 {
619  if (!term) return 0;
620  return term->refcount;
621 }
622 
623 gboolean gncBillTermGetInvisible (const GncBillTerm *term)
624 {
625  if (!term) return FALSE;
626  return term->invisible;
627 }
628 
629 int gncBillTermCompare (const GncBillTerm *a, const GncBillTerm *b)
630 {
631  int ret;
632 
633  if (!a && !b) return 0;
634  if (!a) return -1;
635  if (!b) return 1;
636 
637  ret = g_strcmp0 (a->name, b->name);
638  if (ret) return ret;
639 
640  return g_strcmp0 (a->desc, b->desc);
641 }
642 
643 gboolean gncBillTermEqual(const GncBillTerm *a, const GncBillTerm *b)
644 {
645  if (a == NULL && b == NULL) return TRUE;
646  if (a == NULL || b == NULL) return FALSE;
647 
648  g_return_val_if_fail(GNC_IS_BILLTERM(a), FALSE);
649  g_return_val_if_fail(GNC_IS_BILLTERM(b), FALSE);
650 
651  if (g_strcmp0(a->name, b->name) != 0)
652  {
653  PWARN("Names differ: %s vs %s", a->name, b->name);
654  return FALSE;
655  }
656 
657  if (g_strcmp0(a->desc, b->desc) != 0)
658  {
659  PWARN("Descriptions differ: %s vs %s", a->desc, b->desc);
660  return FALSE;
661  }
662 
663  if (a->type != b->type)
664  {
665  PWARN("Types differ");
666  return FALSE;
667  }
668 
669  if (a->due_days != b->due_days)
670  {
671  PWARN("Due days differ: %d vs %d", a->due_days, b->due_days);
672  return FALSE;
673  }
674 
675  if (a->disc_days != b->disc_days)
676  {
677  PWARN("Discount days differ: %d vs %d", a->disc_days, b->disc_days);
678  return FALSE;
679  }
680 
681  if (!gnc_numeric_equal(a->discount, b->discount))
682  {
683  PWARN("Discounts differ");
684  return FALSE;
685  }
686 
687  if (a->cutoff != b->cutoff)
688  {
689  PWARN("Cutoffs differ: %d vs %d", a->cutoff, b->cutoff);
690  return FALSE;
691  }
692 
693  if (a->invisible != b->invisible)
694  {
695  PWARN("Invisible flags differ");
696  return FALSE;
697  }
698 
699 // gint64 refcount;
700 // GncBillTerm * parent; /* if non-null, we are an immutable child */
701 // GncBillTerm * child; /* if non-null, we have not changed */
702 // GList * children; /* list of children for disconnection */
703 
704  return TRUE;
705 }
706 
707 gboolean gncBillTermIsFamily (const GncBillTerm *a, const GncBillTerm *b)
708 {
709  if (!gncBillTermCompare (a, b))
710  return TRUE;
711  else
712  return FALSE;
713 }
714 
715 gboolean gncBillTermIsDirty (const GncBillTerm *term)
716 {
717  if (!term) return FALSE;
718  return qof_instance_get_dirty_flag(term);
719 }
720 
721 /********************************************************/
722 /* functions to compute dates from Bill Terms */
723 
724 #define SECS_PER_DAY 86400
725 
726 /* Based on the post date and a proximo type, compute the month and
727  * year this is due. The actual day is filled in below.
728  *
729  * A proximo billing term has multiple parameters:
730  * * due day: day of the month the invoice/bill will be due
731  * * cutoff: day of the month used to decide if the due date will be
732  * in the next month or in the month thereafter. This can be
733  * a negative number in which case the cutoff date is relative
734  * to the end of the month and counting backwards.
735  * Eg: cutoff = -3 would mean 25 in February or 28 in June
736  *
737  * How does it work:
738  * Assume cutoff = 19 and due day = 20
739  *
740  * * Example 1 post date = 14-06-2010 (European date format)
741  * 14 is less than the cutoff of 19, so the due date will be in the next
742  * month. Since the due day is set to 20, the due date will be
743  * 20-07-2010
744  *
745  * * Example 2 post date = 22-06-2010 (European date format)
746  * 22 is more than the cutoff of 19, so the due date will be in the month
747  * after next month. Since the due day is set to 20, the due date will be
748  * 20-02-2010
749  *
750  */
751 static void
752 compute_monthyear (const GncBillTerm *term, time64 post_date,
753  int *month, int *year)
754 {
755  int iday, imonth, iyear;
756  struct tm tm;
757  int cutoff = term->cutoff;
758 
759  g_return_if_fail (term->type == GNC_TERM_TYPE_PROXIMO);
760  gnc_localtime_r (&post_date, &tm);
761  iday = tm.tm_mday;
762  imonth = tm.tm_mon + 1;
763  iyear = tm.tm_year + 1900;
764 
765  if (cutoff <= 0)
766  cutoff += gnc_date_get_last_mday (imonth - 1, iyear);
767 
768  if (iday <= cutoff)
769  {
770  /* We apply this to next month */
771  imonth++;
772  }
773  else
774  {
775  /* We apply to the following month */
776  imonth += 2;
777  }
778 
779  if (imonth > 12)
780  {
781  iyear++;
782  imonth -= 12;
783  }
784 
785  if (month) *month = imonth;
786  if (year) *year = iyear;
787 }
788 
789 /* There are two types of billing terms:
790  *
791  * Type DAYS defines a due date to be a fixed number of days passed the post
792  * date. This is a straightforward calculation.
793  *
794  * The other type PROXIMO defines the due date as a fixed day of the month
795  * (like always the 15th of the month). The proximo algorithm determines which
796  * month based on the cutoff day and the post date. See above for a more
797  * detailed explanation of proximo.
798  */
799 
800 static time64
801 compute_time (const GncBillTerm *term, time64 post_date, int days)
802 {
803  time64 res = gnc_time64_get_day_neutral (post_date);
804  int day, month, year;
805 
806  switch (term->type)
807  {
808  case GNC_TERM_TYPE_DAYS:
809  res += (SECS_PER_DAY * days);
810  res = gnc_time64_get_day_neutral (res);
811  break;
812  case GNC_TERM_TYPE_PROXIMO:
813  compute_monthyear (term, post_date, &month, &year);
814  day = gnc_date_get_last_mday (month - 1, year);
815  if (days < day)
816  day = days;
817  res = gnc_dmy2time64_neutral (day, month, year);
818  break;
819  }
820  return res;
821 }
822 
823 time64
824 gncBillTermComputeDueDate (const GncBillTerm *term, time64 post_date)
825 {
826  if (!term) return post_date;
827  return compute_time (term, post_date, term->due_days);
828 }
829 
830 /* Package-Private functions */
831 
832 static void _gncBillTermCreate (QofBook *book)
833 {
834  struct _book_info *bi;
835 
836  if (!book) return;
837 
838  bi = g_new0 (struct _book_info, 1);
839  qof_book_set_data (book, _GNC_MOD_NAME, bi);
840 }
841 
842 
843 static void
844 destroy_billterm_on_book_close (QofInstance *ent, gpointer data)
845 {
846  GncBillTerm *term = GNC_BILLTERM(ent);
847 
848  gncBillTermBeginEdit (term);
849  gncBillTermDestroy (term);
850 }
851 
852 static void _gncBillTermDestroy (QofBook *book)
853 {
854  struct _book_info *bi;
855  QofCollection *col;
856 
857  if (!book) return;
858 
859  bi = qof_book_get_data (book, _GNC_MOD_NAME);
860 
861  col = qof_book_get_collection (book, GNC_ID_BILLTERM);
862  qof_collection_foreach (col, destroy_billterm_on_book_close, NULL);
863 
864  g_list_free (bi->terms);
865  g_free (bi);
866 }
867 
868 static QofObject gncBillTermDesc =
869 {
870  DI(.interface_version = ) QOF_OBJECT_VERSION,
871  DI(.e_type = ) _GNC_MOD_NAME,
872  DI(.type_label = ) "Billing Term",
873  DI(.create = ) (gpointer)gncBillTermCreate,
874  DI(.book_begin = ) _gncBillTermCreate,
875  DI(.book_end = ) _gncBillTermDestroy,
876  DI(.is_dirty = ) qof_collection_is_dirty,
877  DI(.mark_clean = ) qof_collection_mark_clean,
878  DI(.foreach = ) qof_collection_foreach,
879  DI(.printable = ) NULL,
880  DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
881 };
882 
883 gboolean gncBillTermRegister (void)
884 {
885  static QofParam params[] =
886  {
887  { GNC_BILLTERM_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncBillTermGetName, (QofSetterFunc)gncBillTermSetName },
888  { GNC_BILLTERM_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncBillTermGetDescription, (QofSetterFunc)gncBillTermSetDescription },
889  { GNC_BILLTERM_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofBillTermGetType, (QofSetterFunc)qofBillTermSetType },
890  { GNC_BILLTERM_DUEDAYS, QOF_TYPE_INT32, (QofAccessFunc)gncBillTermGetDueDays, (QofSetterFunc)gncBillTermSetDueDays },
891  { GNC_BILLTERM_DISCDAYS, QOF_TYPE_INT32, (QofAccessFunc)gncBillTermGetDiscountDays, (QofSetterFunc)gncBillTermSetDiscountDays },
892  { GNC_BILLTERM_DISCOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncBillTermGetDiscount, (QofSetterFunc)gncBillTermSetDiscount },
893  { GNC_BILLTERM_CUTOFF, QOF_TYPE_INT32, (QofAccessFunc)gncBillTermGetCutoff, (QofSetterFunc)gncBillTermSetCutoff },
894  { GNC_BILLTERM_REFCOUNT, QOF_TYPE_INT64, (QofAccessFunc)gncBillTermGetRefcount, NULL },
895  { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
896  { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
897  { NULL },
898  };
899 
900  qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncBillTermCompare, params);
901 
902  return qof_object_register (&gncBillTermDesc);
903 }
int gncBillTermCompare(const GncBillTerm *a, const GncBillTerm *b)
Compare BillTerms on their name for sorting.
Definition: gncBillTerm.c:629
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
time64 gnc_dmy2time64_neutral(gint day, gint month, gint year)
Converts a day, month, and year to a time64 representing 11:00:00 UTC 11:00:00 UTC falls on the same ...
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of &#39;dirty&#39; flag on collection.
Definition: qofid.cpp:255
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition: qofclass.cpp:86
#define ENUM_TERMS_TYPE(_)
How to interpret the amount.
Definition: gncBillTerm.h:116
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:173
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition: qofclass.h:223
int gnc_date_get_last_mday(int month, int year)
Get the numerical last date of the month.
Definition: gnc-date.cpp:411
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition: qofobject.h:63
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition: qofquery.h:108
struct tm * gnc_localtime_r(const time64 *secs, struct tm *time)
fill out a time struct from a 64-bit time value adjusted for the current time zone.
Definition: gnc-date.cpp:114
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:185
gboolean gncBillTermEqual(const GncBillTerm *a, const GncBillTerm *b)
Check if all internal fields of a and b match.
Definition: gncBillTerm.c:643
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
gboolean gncBillTermIsFamily(const GncBillTerm *a, const GncBillTerm *b)
Check only if the bill terms are "family".
Definition: gncBillTerm.c:707
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
void qof_book_set_data(QofBook *book, const gchar *key, gpointer data)
The qof_book_set_data() allows arbitrary pointers to structs to be stored in QofBook.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:178
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:261
gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and ...
All type declarations for the whole Gnucash engine.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
Definition: qofobject.cpp:299
FROM_STRING_DEC(CheckItemType, ENUM_CHECK_ITEM_TYPE)
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition: qofevent.cpp:231
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
Retrieves arbitrary pointers to structs stored by qof_book_set_data.
time64 gnc_time64_get_day_neutral(time64 time_val)
The gnc_time64_get_day_neutral() routine will take the given time in seconds and adjust it to 10:59:0...
Definition: gnc-date.cpp:1306