Branch data Line data Source code
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 : :
37 : : struct _gncBillTerm
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 : :
60 : : struct _gncBillTermClass
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 : 48 : mark_term (GncBillTerm *term)
88 : : {
89 : 48 : qof_instance_set_dirty(&term->inst);
90 : 48 : qof_event_gen (&term->inst, QOF_EVENT_MODIFY, NULL);
91 : 48 : }
92 : :
93 : 16 : static inline void maybe_resort_list (GncBillTerm *term)
94 : : {
95 : : struct _book_info *bi;
96 : :
97 : 16 : if (term->parent || term->invisible) return;
98 : 16 : bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME);
99 : 16 : bi->terms = g_list_sort (bi->terms, (GCompareFunc)gncBillTermCompare);
100 : : }
101 : :
102 : 8 : static inline void addObj (GncBillTerm *term)
103 : : {
104 : : struct _book_info *bi;
105 : 8 : bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME);
106 : 8 : bi->terms = g_list_insert_sorted (bi->terms, term,
107 : : (GCompareFunc)gncBillTermCompare);
108 : 8 : }
109 : :
110 : 4 : static inline void remObj (GncBillTerm *term)
111 : : {
112 : : struct _book_info *bi;
113 : 4 : bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME);
114 : 4 : bi->terms = g_list_remove (bi->terms, term);
115 : 4 : }
116 : :
117 : : static inline void
118 : 4 : gncBillTermAddChild (GncBillTerm *table, GncBillTerm *child)
119 : : {
120 : 4 : g_return_if_fail(qof_instance_get_destroying(table) == FALSE);
121 : 4 : table->children = g_list_prepend(table->children, child);
122 : : }
123 : :
124 : : static inline void
125 : 0 : gncBillTermRemoveChild (GncBillTerm *table, GncBillTerm *child)
126 : : {
127 : 0 : if (qof_instance_get_destroying(table)) return;
128 : 0 : 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 : 24 : G_DEFINE_TYPE(GncBillTerm, gnc_billterm, QOF_TYPE_INSTANCE)
141 : :
142 : : static void
143 : 8 : gnc_billterm_init(GncBillTerm* bt)
144 : : {
145 : 8 : }
146 : :
147 : : static void
148 : 0 : gnc_billterm_dispose(GObject *btp)
149 : : {
150 : 0 : G_OBJECT_CLASS(gnc_billterm_parent_class)->dispose(btp);
151 : 0 : }
152 : :
153 : : static void
154 : 0 : gnc_billterm_finalize(GObject* btp)
155 : : {
156 : 0 : G_OBJECT_CLASS(gnc_billterm_parent_class)->finalize(btp);
157 : 0 : }
158 : :
159 : : static void
160 : 0 : gnc_billterm_get_property (GObject *object,
161 : : guint prop_id,
162 : : GValue *value,
163 : : GParamSpec *pspec)
164 : : {
165 : : GncBillTerm *bt;
166 : :
167 : 0 : g_return_if_fail(GNC_IS_BILLTERM(object));
168 : :
169 : 0 : bt = GNC_BILLTERM(object);
170 : 0 : switch (prop_id)
171 : : {
172 : 0 : case PROP_NAME:
173 : 0 : g_value_set_string(value, bt->name);
174 : 0 : break;
175 : 0 : default:
176 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
177 : 0 : break;
178 : : }
179 : : }
180 : :
181 : : static void
182 : 0 : gnc_billterm_set_property (GObject *object,
183 : : guint prop_id,
184 : : const GValue *value,
185 : : GParamSpec *pspec)
186 : : {
187 : : GncBillTerm *bt;
188 : :
189 : 0 : g_return_if_fail(GNC_IS_BILLTERM(object));
190 : :
191 : 0 : bt = GNC_BILLTERM(object);
192 : 0 : g_assert (qof_instance_get_editlevel(bt));
193 : :
194 : 0 : switch (prop_id)
195 : : {
196 : 0 : case PROP_NAME:
197 : 0 : gncBillTermSetName(bt, g_value_get_string(value));
198 : 0 : break;
199 : 0 : default:
200 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
201 : 0 : break;
202 : : }
203 : : }
204 : :
205 : : /** Returns a list of my type of object which refers to an object. For example, when called as
206 : : qof_instance_get_typed_referring_object_list(taxtable, account);
207 : : it will return the list of taxtables which refer to a specific account. The result should be the
208 : : same regardless of which taxtable object is used. The list must be freed by the caller but the
209 : : objects on the list must not.
210 : : */
211 : : static GList*
212 : 0 : 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 : 0 : return NULL;
216 : : }
217 : :
218 : : static void
219 : 2 : gnc_billterm_class_init (GncBillTermClass *klass)
220 : : {
221 : 2 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
222 : 2 : QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
223 : :
224 : 2 : gobject_class->dispose = gnc_billterm_dispose;
225 : 2 : gobject_class->finalize = gnc_billterm_finalize;
226 : 2 : gobject_class->set_property = gnc_billterm_set_property;
227 : 2 : gobject_class->get_property = gnc_billterm_get_property;
228 : :
229 : 2 : qof_class->get_display_name = NULL;
230 : 2 : qof_class->refers_to_object = NULL;
231 : 2 : qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
232 : :
233 : 2 : 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 : 2 : }
246 : :
247 : : /* Create/Destroy Functions */
248 : 8 : GncBillTerm * gncBillTermCreate (QofBook *book)
249 : : {
250 : : GncBillTerm *term;
251 : 8 : if (!book) return NULL;
252 : :
253 : 8 : term = g_object_new (GNC_TYPE_BILLTERM, NULL);
254 : 8 : qof_instance_init_data(&term->inst, _GNC_MOD_NAME, book);
255 : 8 : term->name = CACHE_INSERT ("");
256 : 8 : term->desc = CACHE_INSERT ("");
257 : 8 : term->discount = gnc_numeric_zero ();
258 : 8 : addObj (term);
259 : 8 : qof_event_gen (&term->inst, QOF_EVENT_CREATE, NULL);
260 : 8 : return term;
261 : : }
262 : :
263 : 0 : void gncBillTermDestroy (GncBillTerm *term)
264 : : {
265 : : gchar guidstr[GUID_ENCODING_LENGTH+1];
266 : 0 : if (!term) return;
267 : 0 : guid_to_string_buff(qof_instance_get_guid(&term->inst),guidstr);
268 : 0 : DEBUG("destroying bill term %s (%p)", guidstr, term);
269 : 0 : qof_instance_set_destroying(term, TRUE);
270 : 0 : qof_instance_set_dirty (&term->inst);
271 : 0 : gncBillTermCommitEdit (term);
272 : : }
273 : :
274 : 0 : static void gncBillTermFree (GncBillTerm *term)
275 : : {
276 : : GncBillTerm *child;
277 : : GList *list;
278 : :
279 : 0 : if (!term) return;
280 : :
281 : 0 : qof_event_gen (&term->inst, QOF_EVENT_DESTROY, NULL);
282 : 0 : CACHE_REMOVE (term->name);
283 : 0 : CACHE_REMOVE (term->desc);
284 : 0 : remObj (term);
285 : :
286 : 0 : if (!qof_instance_get_destroying(term))
287 : 0 : PERR("free a billterm without do_free set!");
288 : :
289 : : /* disconnect from parent */
290 : 0 : if (term->parent)
291 : 0 : gncBillTermRemoveChild(term->parent, term);
292 : :
293 : : /* disconnect from the children */
294 : 0 : for (list = term->children; list; list = list->next)
295 : : {
296 : 0 : child = list->data;
297 : 0 : gncBillTermSetParent(child, NULL);
298 : : }
299 : 0 : g_list_free(term->children);
300 : :
301 : : /* qof_instance_release(&term->inst); */
302 : 0 : g_object_unref (term);
303 : : }
304 : :
305 : : /* ============================================================== */
306 : : /* Set Functions */
307 : :
308 : 8 : void gncBillTermSetName (GncBillTerm *term, const char *name)
309 : : {
310 : 8 : if (!term || !name) return;
311 : 8 : SET_STR (term, term->name, name);
312 : 8 : mark_term (term);
313 : 8 : maybe_resort_list (term);
314 : 8 : gncBillTermCommitEdit (term);
315 : : }
316 : :
317 : 8 : void gncBillTermSetDescription (GncBillTerm *term, const char *desc)
318 : : {
319 : 8 : if (!term || !desc) return;
320 : 8 : SET_STR (term, term->desc, desc);
321 : 8 : mark_term (term);
322 : 8 : maybe_resort_list (term);
323 : 8 : gncBillTermCommitEdit (term);
324 : : }
325 : :
326 : 4 : void gncBillTermSetType (GncBillTerm *term, GncBillTermType type)
327 : : {
328 : 4 : if (!term) return;
329 : 4 : if (term->type == type) return;
330 : 4 : gncBillTermBeginEdit (term);
331 : 4 : term->type = type;
332 : 4 : mark_term (term);
333 : 4 : gncBillTermCommitEdit (term);
334 : : }
335 : :
336 : : /** \brief Convert bill term types from text. */
337 : 0 : FROM_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE)
338 : :
339 : : static
340 : 0 : void qofBillTermSetType (GncBillTerm *term, const char *type_label)
341 : : {
342 : : GncBillTermType type;
343 : :
344 : 0 : type = GncBillTermTypefromString(type_label);
345 : 0 : gncBillTermSetType(term, type);
346 : 0 : }
347 : :
348 : 4 : void gncBillTermSetDueDays (GncBillTerm *term, gint days)
349 : : {
350 : 4 : if (!term) return;
351 : 4 : if (term->due_days == days) return;
352 : 4 : gncBillTermBeginEdit (term);
353 : 4 : term->due_days = days;
354 : 4 : mark_term (term);
355 : 4 : gncBillTermCommitEdit (term);
356 : : }
357 : :
358 : 0 : void gncBillTermSetDiscountDays (GncBillTerm *term, gint days)
359 : : {
360 : 0 : if (!term) return;
361 : 0 : if (term->disc_days == days) return;
362 : 0 : gncBillTermBeginEdit (term);
363 : 0 : term->disc_days = days;
364 : 0 : mark_term (term);
365 : 0 : gncBillTermCommitEdit (term);
366 : : }
367 : :
368 : 0 : void gncBillTermSetDiscount (GncBillTerm *term, gnc_numeric discount)
369 : : {
370 : 0 : if (!term) return;
371 : 0 : if (gnc_numeric_eq (term->discount, discount)) return;
372 : 0 : gncBillTermBeginEdit (term);
373 : 0 : term->discount = discount;
374 : 0 : mark_term (term);
375 : 0 : gncBillTermCommitEdit (term);
376 : : }
377 : :
378 : 0 : void gncBillTermSetCutoff (GncBillTerm *term, gint cutoff)
379 : : {
380 : 0 : if (!term) return;
381 : 0 : if (term->cutoff == cutoff) return;
382 : 0 : gncBillTermBeginEdit (term);
383 : 0 : term->cutoff = cutoff;
384 : 0 : mark_term (term);
385 : 0 : 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 : 4 : void gncBillTermSetParent (GncBillTerm *term, GncBillTerm *parent)
394 : : {
395 : 4 : if (!term) return;
396 : 4 : gncBillTermBeginEdit (term);
397 : 4 : if (term->parent)
398 : 0 : gncBillTermRemoveChild(term->parent, term);
399 : 4 : term->parent = parent;
400 : 4 : if (parent)
401 : 4 : gncBillTermAddChild(parent, term);
402 : 4 : term->refcount = 0;
403 : 4 : if ( parent != NULL )
404 : : {
405 : 4 : gncBillTermMakeInvisible (term);
406 : : }
407 : 4 : mark_term (term);
408 : 4 : gncBillTermCommitEdit (term);
409 : : }
410 : :
411 : 4 : void gncBillTermSetChild (GncBillTerm *term, GncBillTerm *child)
412 : : {
413 : 4 : if (!term) return;
414 : 4 : gncBillTermBeginEdit (term);
415 : 4 : term->child = child;
416 : 4 : mark_term (term);
417 : 4 : gncBillTermCommitEdit (term);
418 : : }
419 : :
420 : 39 : void gncBillTermIncRef (GncBillTerm *term)
421 : : {
422 : 39 : if (!term) return;
423 : 8 : if (term->parent || term->invisible) return; /* children dont need refcounts */
424 : 4 : gncBillTermBeginEdit (term);
425 : 4 : term->refcount++;
426 : 4 : mark_term (term);
427 : 4 : gncBillTermCommitEdit (term);
428 : : }
429 : :
430 : 4 : void gncBillTermDecRef (GncBillTerm *term)
431 : : {
432 : 4 : if (!term) return;
433 : 4 : if (term->parent || term->invisible) return; /* children dont need refcounts */
434 : 4 : g_return_if_fail (term->refcount >= 1);
435 : 4 : gncBillTermBeginEdit (term);
436 : 4 : term->refcount--;
437 : 4 : mark_term (term);
438 : 4 : gncBillTermCommitEdit (term);
439 : : }
440 : :
441 : 0 : void gncBillTermSetRefcount (GncBillTerm *term, gint64 refcount)
442 : : {
443 : 0 : if (!term) return;
444 : 0 : gncBillTermBeginEdit (term);
445 : 0 : term->refcount = refcount;
446 : 0 : mark_term (term);
447 : 0 : gncBillTermCommitEdit (term);
448 : : }
449 : :
450 : 4 : void gncBillTermMakeInvisible (GncBillTerm *term)
451 : : {
452 : 4 : if (!term) return;
453 : 4 : gncBillTermBeginEdit (term);
454 : 4 : term->invisible = TRUE;
455 : 4 : remObj (term);
456 : 4 : mark_term (term);
457 : 4 : gncBillTermCommitEdit (term);
458 : : }
459 : :
460 : 0 : void gncBillTermChanged (GncBillTerm *term)
461 : : {
462 : 0 : if (!term) return;
463 : 0 : term->child = NULL;
464 : : }
465 : :
466 : 48 : void gncBillTermBeginEdit (GncBillTerm *term)
467 : : {
468 : 48 : qof_begin_edit(&term->inst);
469 : 48 : }
470 : :
471 : 0 : static void gncBillTermOnError (QofInstance *inst, QofBackendError errcode)
472 : : {
473 : 0 : PERR("BillTerm QofBackend Failure: %d", errcode);
474 : 0 : gnc_engine_signal_commit_error( errcode );
475 : 0 : }
476 : :
477 : 0 : static void bill_free (QofInstance *inst)
478 : : {
479 : 0 : GncBillTerm *term = (GncBillTerm *) inst;
480 : 0 : gncBillTermFree(term);
481 : 0 : }
482 : :
483 : 36 : static void on_done (QofInstance *inst) {}
484 : :
485 : 48 : void gncBillTermCommitEdit (GncBillTerm *term)
486 : : {
487 : 48 : if (!qof_commit_edit (QOF_INSTANCE(term))) return;
488 : 36 : qof_commit_edit_part2 (&term->inst, gncBillTermOnError,
489 : : on_done, bill_free);
490 : : }
491 : :
492 : : /* Get Functions */
493 : :
494 : 0 : GncBillTerm *gncBillTermLookupByName (QofBook *book, const char *name)
495 : : {
496 : 0 : GList *list = gncBillTermGetTerms (book);
497 : :
498 : 0 : for ( ; list; list = list->next)
499 : : {
500 : 0 : GncBillTerm *term = list->data;
501 : 0 : if (!g_strcmp0 (term->name, name))
502 : 0 : return list->data;
503 : : }
504 : 0 : return NULL;
505 : : }
506 : :
507 : 0 : GList * gncBillTermGetTerms (QofBook *book)
508 : : {
509 : : struct _book_info *bi;
510 : 0 : if (!book) return NULL;
511 : :
512 : 0 : bi = qof_book_get_data (book, _GNC_MOD_NAME);
513 : 0 : return bi->terms;
514 : : }
515 : :
516 : 0 : const char *gncBillTermGetName (const GncBillTerm *term)
517 : : {
518 : 0 : if (!term) return NULL;
519 : 0 : return term->name;
520 : : }
521 : :
522 : 24 : const char *gncBillTermGetDescription (const GncBillTerm *term)
523 : : {
524 : 24 : if (!term) return NULL;
525 : 3 : return term->desc;
526 : : }
527 : :
528 : 0 : GncBillTermType gncBillTermGetType (const GncBillTerm *term)
529 : : {
530 : 0 : if (!term) return 0;
531 : 0 : return term->type;
532 : : }
533 : :
534 : : /** \brief Convert bill term types to text. */
535 : 0 : AS_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE)
536 : :
537 : : static
538 : 0 : const char* qofBillTermGetType (const GncBillTerm *term)
539 : : {
540 : 0 : if (!term)
541 : : {
542 : 0 : return NULL;
543 : : }
544 : 0 : return GncBillTermTypeasString(term->type);
545 : : }
546 : :
547 : 0 : gint gncBillTermGetDueDays (const GncBillTerm *term)
548 : : {
549 : 0 : if (!term) return 0;
550 : 0 : return term->due_days;
551 : : }
552 : :
553 : 0 : gint gncBillTermGetDiscountDays (const GncBillTerm *term)
554 : : {
555 : 0 : if (!term) return 0;
556 : 0 : return term->disc_days;
557 : : }
558 : :
559 : 0 : gnc_numeric gncBillTermGetDiscount (const GncBillTerm *term)
560 : : {
561 : 0 : if (!term) return gnc_numeric_zero ();
562 : 0 : return term->discount;
563 : : }
564 : :
565 : 0 : gint gncBillTermGetCutoff (const GncBillTerm *term)
566 : : {
567 : 0 : if (!term) return 0;
568 : 0 : return term->cutoff;
569 : : }
570 : :
571 : 4 : static GncBillTerm *gncBillTermCopy (const GncBillTerm *term)
572 : : {
573 : : GncBillTerm *t;
574 : :
575 : 4 : if (!term) return NULL;
576 : 4 : t = gncBillTermCreate (qof_instance_get_book(term));
577 : :
578 : 4 : gncBillTermBeginEdit(t);
579 : :
580 : 4 : gncBillTermSetName (t, term->name);
581 : 4 : gncBillTermSetDescription (t, term->desc);
582 : :
583 : 4 : t->type = term->type;
584 : 4 : t->due_days = term->due_days;
585 : 4 : t->disc_days = term->disc_days;
586 : 4 : t->discount = term->discount;
587 : 4 : t->cutoff = term->cutoff;
588 : :
589 : 4 : mark_term (t);
590 : 4 : gncBillTermCommitEdit(t);
591 : :
592 : 4 : return t;
593 : : }
594 : :
595 : 4 : GncBillTerm *gncBillTermReturnChild (GncBillTerm *term, gboolean make_new)
596 : : {
597 : 4 : GncBillTerm *child = NULL;
598 : :
599 : 4 : if (!term) return NULL;
600 : 4 : if (term->child) return term->child;
601 : 4 : if (term->parent || term->invisible) return term;
602 : 4 : if (make_new)
603 : : {
604 : 4 : child = gncBillTermCopy (term);
605 : 4 : gncBillTermSetChild (term, child);
606 : 4 : gncBillTermSetParent (child, term);
607 : : }
608 : 4 : return child;
609 : : }
610 : :
611 : 0 : GncBillTerm *gncBillTermGetParent (const GncBillTerm *term)
612 : : {
613 : 0 : if (!term) return NULL;
614 : 0 : return term->parent;
615 : : }
616 : :
617 : 0 : gint64 gncBillTermGetRefcount (const GncBillTerm *term)
618 : : {
619 : 0 : if (!term) return 0;
620 : 0 : return term->refcount;
621 : : }
622 : :
623 : 0 : gboolean gncBillTermGetInvisible (const GncBillTerm *term)
624 : : {
625 : 0 : if (!term) return FALSE;
626 : 0 : return term->invisible;
627 : : }
628 : :
629 : 28 : int gncBillTermCompare (const GncBillTerm *a, const GncBillTerm *b)
630 : : {
631 : : int ret;
632 : :
633 : 28 : if (!a && !b) return 0;
634 : 28 : if (!a) return -1;
635 : 28 : if (!b) return 1;
636 : :
637 : 28 : ret = g_strcmp0 (a->name, b->name);
638 : 28 : if (ret) return ret;
639 : :
640 : 22 : return g_strcmp0 (a->desc, b->desc);
641 : : }
642 : :
643 : 5 : gboolean gncBillTermEqual(const GncBillTerm *a, const GncBillTerm *b)
644 : : {
645 : 5 : if (a == NULL && b == NULL) return TRUE;
646 : 0 : if (a == NULL || b == NULL) return FALSE;
647 : :
648 : 0 : g_return_val_if_fail(GNC_IS_BILLTERM(a), FALSE);
649 : 0 : g_return_val_if_fail(GNC_IS_BILLTERM(b), FALSE);
650 : :
651 : 0 : if (g_strcmp0(a->name, b->name) != 0)
652 : : {
653 : 0 : PWARN("Names differ: %s vs %s", a->name, b->name);
654 : 0 : return FALSE;
655 : : }
656 : :
657 : 0 : if (g_strcmp0(a->desc, b->desc) != 0)
658 : : {
659 : 0 : PWARN("Descriptions differ: %s vs %s", a->desc, b->desc);
660 : 0 : return FALSE;
661 : : }
662 : :
663 : 0 : if (a->type != b->type)
664 : : {
665 : 0 : PWARN("Types differ");
666 : 0 : return FALSE;
667 : : }
668 : :
669 : 0 : if (a->due_days != b->due_days)
670 : : {
671 : 0 : PWARN("Due days differ: %d vs %d", a->due_days, b->due_days);
672 : 0 : return FALSE;
673 : : }
674 : :
675 : 0 : if (a->disc_days != b->disc_days)
676 : : {
677 : 0 : PWARN("Discount days differ: %d vs %d", a->disc_days, b->disc_days);
678 : 0 : return FALSE;
679 : : }
680 : :
681 : 0 : if (!gnc_numeric_equal(a->discount, b->discount))
682 : : {
683 : 0 : PWARN("Discounts differ");
684 : 0 : return FALSE;
685 : : }
686 : :
687 : 0 : if (a->cutoff != b->cutoff)
688 : : {
689 : 0 : PWARN("Cutoffs differ: %d vs %d", a->cutoff, b->cutoff);
690 : 0 : return FALSE;
691 : : }
692 : :
693 : 0 : if (a->invisible != b->invisible)
694 : : {
695 : 0 : PWARN("Invisible flags differ");
696 : 0 : 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 : 0 : return TRUE;
705 : : }
706 : :
707 : 0 : gboolean gncBillTermIsFamily (const GncBillTerm *a, const GncBillTerm *b)
708 : : {
709 : 0 : if (!gncBillTermCompare (a, b))
710 : 0 : return TRUE;
711 : : else
712 : 0 : return FALSE;
713 : : }
714 : :
715 : 0 : gboolean gncBillTermIsDirty (const GncBillTerm *term)
716 : : {
717 : 0 : if (!term) return FALSE;
718 : 0 : 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 : 0 : compute_monthyear (const GncBillTerm *term, time64 post_date,
753 : : int *month, int *year)
754 : : {
755 : : int iday, imonth, iyear;
756 : : struct tm tm;
757 : 0 : int cutoff = term->cutoff;
758 : :
759 : 0 : g_return_if_fail (term->type == GNC_TERM_TYPE_PROXIMO);
760 : 0 : gnc_localtime_r (&post_date, &tm);
761 : 0 : iday = tm.tm_mday;
762 : 0 : imonth = tm.tm_mon + 1;
763 : 0 : iyear = tm.tm_year + 1900;
764 : :
765 : 0 : if (cutoff <= 0)
766 : 0 : cutoff += gnc_date_get_last_mday (imonth - 1, iyear);
767 : :
768 : 0 : if (iday <= cutoff)
769 : : {
770 : : /* We apply this to next month */
771 : 0 : imonth++;
772 : : }
773 : : else
774 : : {
775 : : /* We apply to the following month */
776 : 0 : imonth += 2;
777 : : }
778 : :
779 : 0 : if (imonth > 12)
780 : : {
781 : 0 : iyear++;
782 : 0 : imonth -= 12;
783 : : }
784 : :
785 : 0 : if (month) *month = imonth;
786 : 0 : 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 : 0 : compute_time (const GncBillTerm *term, time64 post_date, int days)
802 : : {
803 : 0 : time64 res = gnc_time64_get_day_neutral (post_date);
804 : : int day, month, year;
805 : :
806 : 0 : switch (term->type)
807 : : {
808 : 0 : case GNC_TERM_TYPE_DAYS:
809 : 0 : res += (SECS_PER_DAY * days);
810 : 0 : res = gnc_time64_get_day_neutral (res);
811 : 0 : break;
812 : 0 : case GNC_TERM_TYPE_PROXIMO:
813 : 0 : compute_monthyear (term, post_date, &month, &year);
814 : 0 : day = gnc_date_get_last_mday (month - 1, year);
815 : 0 : if (days < day)
816 : 0 : day = days;
817 : 0 : res = gnc_dmy2time64_neutral (day, month, year);
818 : 0 : break;
819 : : }
820 : 0 : return res;
821 : : }
822 : :
823 : : time64
824 : 0 : gncBillTermComputeDueDate (const GncBillTerm *term, time64 post_date)
825 : : {
826 : 0 : if (!term) return post_date;
827 : 0 : return compute_time (term, post_date, term->due_days);
828 : : }
829 : :
830 : : /* Package-Private functions */
831 : :
832 : 255 : static void _gncBillTermCreate (QofBook *book)
833 : : {
834 : : struct _book_info *bi;
835 : :
836 : 255 : if (!book) return;
837 : :
838 : 255 : bi = g_new0 (struct _book_info, 1);
839 : 255 : qof_book_set_data (book, _GNC_MOD_NAME, bi);
840 : : }
841 : :
842 : :
843 : : static void
844 : 0 : destroy_billterm_on_book_close (QofInstance *ent, gpointer data)
845 : : {
846 : 0 : GncBillTerm *term = GNC_BILLTERM(ent);
847 : :
848 : 0 : gncBillTermBeginEdit (term);
849 : 0 : gncBillTermDestroy (term);
850 : 0 : }
851 : :
852 : 153 : static void _gncBillTermDestroy (QofBook *book)
853 : : {
854 : : struct _book_info *bi;
855 : : QofCollection *col;
856 : :
857 : 153 : if (!book) return;
858 : :
859 : 153 : bi = qof_book_get_data (book, _GNC_MOD_NAME);
860 : :
861 : 153 : col = qof_book_get_collection (book, GNC_ID_BILLTERM);
862 : 153 : qof_collection_foreach (col, destroy_billterm_on_book_close, NULL);
863 : :
864 : 153 : g_list_free (bi->terms);
865 : 153 : 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 : 81 : 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 : 81 : qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncBillTermCompare, params);
901 : :
902 : 81 : return qof_object_register (&gncBillTermDesc);
903 : : }
|