|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * gncBillTerm.c -- the Gnucash Billing Terms interface * 00003 * * 00004 * This program is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU General Public License as * 00006 * published by the Free Software Foundation; either version 2 of * 00007 * the License, or (at your option) any later version. * 00008 * * 00009 * This program is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00012 * GNU General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU General Public License* 00015 * along with this program; if not, contact: * 00016 * * 00017 * Free Software Foundation Voice: +1-617-542-5942 * 00018 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00019 * Boston, MA 02110-1301, USA gnu@gnu.org * 00020 * * 00021 \********************************************************************/ 00022 00023 /* 00024 * Copyright (C) 2002 Derek Atkins 00025 * Copyright (C) 2003 Linas Vepstas <linas@linas.org> 00026 * Author: Derek Atkins <warlord@MIT.EDU> 00027 */ 00028 00029 #include "config.h" 00030 00031 #include <glib.h> 00032 00033 #include "gnc-engine.h" 00034 #include "gncBillTermP.h" 00035 00036 struct _gncBillTerm 00037 { 00038 QofInstance inst; 00039 00040 /* 'visible' data fields directly manipulated by user */ 00041 char * name; 00042 char * desc; 00043 GncBillTermType type; 00044 gint due_days; 00045 gint disc_days; 00046 gnc_numeric discount; 00047 gint cutoff; 00048 00049 /* Internal management fields */ 00050 /* See src/doc/business.txt for an explanation of the following */ 00051 /* Code that handles this is *identical* to that in gncTaxTable */ 00052 gint64 refcount; 00053 GncBillTerm * parent; /* if non-null, we are an immutable child */ 00054 GncBillTerm * child; /* if non-null, we have not changed */ 00055 gboolean invisible; 00056 GList * children; /* list of children for disconnection */ 00057 }; 00058 00059 struct _gncBillTermClass 00060 { 00061 QofInstanceClass parent_class; 00062 }; 00063 00064 struct _book_info 00065 { 00066 GList * terms; /* visible terms */ 00067 }; 00068 00069 static QofLogModule log_module = GNC_MOD_BUSINESS; 00070 00071 #define _GNC_MOD_NAME GNC_ID_BILLTERM 00072 00073 #define SET_STR(obj, member, str) { \ 00074 char * tmp; \ 00075 \ 00076 if (!safe_strcmp (member, str)) return; \ 00077 gncBillTermBeginEdit (obj); \ 00078 tmp = CACHE_INSERT (str); \ 00079 CACHE_REMOVE (member); \ 00080 member = tmp; \ 00081 } 00082 00083 AS_STRING_DEC(GncBillTermType, ENUM_TERMS_TYPE) 00084 FROM_STRING_DEC(GncBillTermType, ENUM_TERMS_TYPE) 00085 00086 /* ============================================================== */ 00087 /* Misc inline utilities */ 00088 00089 static inline void 00090 mark_term (GncBillTerm *term) 00091 { 00092 qof_instance_set_dirty(&term->inst); 00093 qof_event_gen (&term->inst, QOF_EVENT_MODIFY, NULL); 00094 } 00095 00096 static inline void maybe_resort_list (GncBillTerm *term) 00097 { 00098 struct _book_info *bi; 00099 00100 if (term->parent || term->invisible) return; 00101 bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME); 00102 bi->terms = g_list_sort (bi->terms, (GCompareFunc)gncBillTermCompare); 00103 } 00104 00105 static inline void addObj (GncBillTerm *term) 00106 { 00107 struct _book_info *bi; 00108 bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME); 00109 bi->terms = g_list_insert_sorted (bi->terms, term, 00110 (GCompareFunc)gncBillTermCompare); 00111 } 00112 00113 static inline void remObj (GncBillTerm *term) 00114 { 00115 struct _book_info *bi; 00116 bi = qof_book_get_data (qof_instance_get_book(term), _GNC_MOD_NAME); 00117 bi->terms = g_list_remove (bi->terms, term); 00118 } 00119 00120 static inline void 00121 gncBillTermAddChild (GncBillTerm *table, GncBillTerm *child) 00122 { 00123 g_return_if_fail(qof_instance_get_destroying(table) == FALSE); 00124 table->children = g_list_prepend(table->children, child); 00125 } 00126 00127 static inline void 00128 gncBillTermRemoveChild (GncBillTerm *table, GncBillTerm *child) 00129 { 00130 if (qof_instance_get_destroying(table)) return; 00131 table->children = g_list_remove(table->children, child); 00132 } 00133 00134 /* ============================================================== */ 00135 00136 enum 00137 { 00138 PROP_0, 00139 PROP_NAME 00140 }; 00141 00142 /* GObject Initialization */ 00143 G_DEFINE_TYPE(GncBillTerm, gnc_billterm, QOF_TYPE_INSTANCE); 00144 00145 static void 00146 gnc_billterm_init(GncBillTerm* bt) 00147 { 00148 } 00149 00150 static void 00151 gnc_billterm_dispose(GObject *btp) 00152 { 00153 G_OBJECT_CLASS(gnc_billterm_parent_class)->dispose(btp); 00154 } 00155 00156 static void 00157 gnc_billterm_finalize(GObject* btp) 00158 { 00159 G_OBJECT_CLASS(gnc_billterm_parent_class)->finalize(btp); 00160 } 00161 00162 static void 00163 gnc_billterm_get_property (GObject *object, 00164 guint prop_id, 00165 GValue *value, 00166 GParamSpec *pspec) 00167 { 00168 GncBillTerm *bt; 00169 00170 g_return_if_fail(GNC_IS_BILLTERM(object)); 00171 00172 bt = GNC_BILLTERM(object); 00173 switch (prop_id) 00174 { 00175 case PROP_NAME: 00176 g_value_set_string(value, bt->name); 00177 break; 00178 default: 00179 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00180 break; 00181 } 00182 } 00183 00184 static void 00185 gnc_billterm_set_property (GObject *object, 00186 guint prop_id, 00187 const GValue *value, 00188 GParamSpec *pspec) 00189 { 00190 GncBillTerm *bt; 00191 00192 g_return_if_fail(GNC_IS_BILLTERM(object)); 00193 00194 bt = GNC_BILLTERM(object); 00195 switch (prop_id) 00196 { 00197 case PROP_NAME: 00198 gncBillTermSetName(bt, g_value_get_string(value)); 00199 break; 00200 default: 00201 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00202 break; 00203 } 00204 } 00205 00212 static GList* 00213 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref) 00214 { 00215 /* Bill term doesn't refer to anything except other billterms */ 00216 return NULL; 00217 } 00218 00219 static void 00220 gnc_billterm_class_init (GncBillTermClass *klass) 00221 { 00222 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 00223 QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass); 00224 00225 gobject_class->dispose = gnc_billterm_dispose; 00226 gobject_class->finalize = gnc_billterm_finalize; 00227 gobject_class->set_property = gnc_billterm_set_property; 00228 gobject_class->get_property = gnc_billterm_get_property; 00229 00230 qof_class->get_display_name = NULL; 00231 qof_class->refers_to_object = NULL; 00232 qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list; 00233 00234 g_object_class_install_property 00235 (gobject_class, 00236 PROP_NAME, 00237 g_param_spec_string ("name", 00238 "BillTerm Name", 00239 "The bill term name is an arbitrary string " 00240 "assigned by the user. It is intended to " 00241 "a short, 10 to 30 character long string " 00242 "that is displayed by the GUI as the " 00243 "billterm mnemonic.", 00244 NULL, 00245 G_PARAM_READWRITE)); 00246 } 00247 00248 /* Create/Destroy Functions */ 00249 GncBillTerm * gncBillTermCreate (QofBook *book) 00250 { 00251 GncBillTerm *term; 00252 if (!book) return NULL; 00253 00254 term = g_object_new (GNC_TYPE_BILLTERM, NULL); 00255 qof_instance_init_data(&term->inst, _GNC_MOD_NAME, book); 00256 term->name = CACHE_INSERT (""); 00257 term->desc = CACHE_INSERT (""); 00258 term->discount = gnc_numeric_zero (); 00259 addObj (term); 00260 qof_event_gen (&term->inst, QOF_EVENT_CREATE, NULL); 00261 return term; 00262 } 00263 00264 void gncBillTermDestroy (GncBillTerm *term) 00265 { 00266 if (!term) return; 00267 DEBUG("destroying bill term %s (%p)", 00268 guid_to_string(qof_instance_get_guid(&term->inst)), term); 00269 qof_instance_set_destroying(term, TRUE); 00270 qof_instance_set_dirty (&term->inst); 00271 gncBillTermCommitEdit (term); 00272 } 00273 00274 static void gncBillTermFree (GncBillTerm *term) 00275 { 00276 GncBillTerm *child; 00277 GList *list; 00278 00279 if (!term) return; 00280 00281 qof_event_gen (&term->inst, QOF_EVENT_DESTROY, NULL); 00282 CACHE_REMOVE (term->name); 00283 CACHE_REMOVE (term->desc); 00284 remObj (term); 00285 00286 if (!qof_instance_get_destroying(term)) 00287 PERR("free a billterm without do_free set!"); 00288 00289 /* disconnect from parent */ 00290 if (term->parent) 00291 gncBillTermRemoveChild(term->parent, term); 00292 00293 /* disconnect from the children */ 00294 for (list = term->children; list; list = list->next) 00295 { 00296 child = list->data; 00297 gncBillTermSetParent(child, NULL); 00298 } 00299 g_list_free(term->children); 00300 00301 /* qof_instance_release(&term->inst); */ 00302 g_object_unref (term); 00303 } 00304 00305 /* ============================================================== */ 00306 /* Set Functions */ 00307 00308 void gncBillTermSetName (GncBillTerm *term, const char *name) 00309 { 00310 if (!term || !name) return; 00311 SET_STR (term, term->name, name); 00312 mark_term (term); 00313 maybe_resort_list (term); 00314 gncBillTermCommitEdit (term); 00315 } 00316 00317 void gncBillTermSetDescription (GncBillTerm *term, const char *desc) 00318 { 00319 if (!term || !desc) return; 00320 SET_STR (term, term->desc, desc); 00321 mark_term (term); 00322 maybe_resort_list (term); 00323 gncBillTermCommitEdit (term); 00324 } 00325 00326 void gncBillTermSetType (GncBillTerm *term, GncBillTermType type) 00327 { 00328 if (!term) return; 00329 if (term->type == type) return; 00330 gncBillTermBeginEdit (term); 00331 term->type = type; 00332 mark_term (term); 00333 gncBillTermCommitEdit (term); 00334 } 00335 00337 FROM_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE) 00338 00339 static 00340 void qofBillTermSetType (GncBillTerm *term, const char *type_label) 00341 { 00342 GncBillTermType type; 00343 00344 type = GncBillTermTypefromString(type_label); 00345 gncBillTermSetType(term, type); 00346 } 00347 00348 void gncBillTermSetDueDays (GncBillTerm *term, gint days) 00349 { 00350 if (!term) return; 00351 if (term->due_days == days) return; 00352 gncBillTermBeginEdit (term); 00353 term->due_days = days; 00354 mark_term (term); 00355 gncBillTermCommitEdit (term); 00356 } 00357 00358 void gncBillTermSetDiscountDays (GncBillTerm *term, gint days) 00359 { 00360 if (!term) return; 00361 if (term->disc_days == days) return; 00362 gncBillTermBeginEdit (term); 00363 term->disc_days = days; 00364 mark_term (term); 00365 gncBillTermCommitEdit (term); 00366 } 00367 00368 void gncBillTermSetDiscount (GncBillTerm *term, gnc_numeric discount) 00369 { 00370 if (!term) return; 00371 if (gnc_numeric_eq (term->discount, discount)) return; 00372 gncBillTermBeginEdit (term); 00373 term->discount = discount; 00374 mark_term (term); 00375 gncBillTermCommitEdit (term); 00376 } 00377 00378 void gncBillTermSetCutoff (GncBillTerm *term, gint cutoff) 00379 { 00380 if (!term) return; 00381 if (term->cutoff == cutoff) return; 00382 gncBillTermBeginEdit (term); 00383 term->cutoff = cutoff; 00384 mark_term (term); 00385 gncBillTermCommitEdit (term); 00386 } 00387 00388 /* XXX this doesn't seem right. If the parent/child relationship 00389 * is a doubly-linked list, then there shouldn't be separate set-parent, 00390 * set-child routines, else misuse of the routines will goof up 00391 * relationships. These ops should be atomic, I think. 00392 */ 00393 void gncBillTermSetParent (GncBillTerm *term, GncBillTerm *parent) 00394 { 00395 if (!term) return; 00396 gncBillTermBeginEdit (term); 00397 if (term->parent) 00398 gncBillTermRemoveChild(term->parent, term); 00399 term->parent = parent; 00400 if (parent) 00401 gncBillTermAddChild(parent, term); 00402 term->refcount = 0; 00403 if ( parent != NULL ) 00404 { 00405 gncBillTermMakeInvisible (term); 00406 } 00407 gncBillTermCommitEdit (term); 00408 } 00409 00410 void gncBillTermSetChild (GncBillTerm *term, GncBillTerm *child) 00411 { 00412 if (!term) return; 00413 gncBillTermBeginEdit (term); 00414 term->child = child; 00415 gncBillTermCommitEdit (term); 00416 } 00417 00418 void gncBillTermIncRef (GncBillTerm *term) 00419 { 00420 if (!term) return; 00421 if (term->parent || term->invisible) return; /* children dont need refcounts */ 00422 gncBillTermBeginEdit (term); 00423 term->refcount++; 00424 gncBillTermCommitEdit (term); 00425 } 00426 00427 void gncBillTermDecRef (GncBillTerm *term) 00428 { 00429 if (!term) return; 00430 if (term->parent || term->invisible) return; /* children dont need refcounts */ 00431 gncBillTermBeginEdit (term); 00432 term->refcount--; 00433 g_return_if_fail (term->refcount >= 0); 00434 gncBillTermCommitEdit (term); 00435 } 00436 00437 void gncBillTermSetRefcount (GncBillTerm *term, gint64 refcount) 00438 { 00439 if (!term) return; 00440 term->refcount = refcount; 00441 } 00442 00443 void gncBillTermMakeInvisible (GncBillTerm *term) 00444 { 00445 if (!term) return; 00446 gncBillTermBeginEdit (term); 00447 term->invisible = TRUE; 00448 remObj (term); 00449 gncBillTermCommitEdit (term); 00450 } 00451 00452 void gncBillTermChanged (GncBillTerm *term) 00453 { 00454 if (!term) return; 00455 term->child = NULL; 00456 } 00457 00458 void gncBillTermBeginEdit (GncBillTerm *term) 00459 { 00460 qof_begin_edit(&term->inst); 00461 } 00462 00463 static void gncBillTermOnError (QofInstance *inst, QofBackendError errcode) 00464 { 00465 PERR("BillTerm QofBackend Failure: %d", errcode); 00466 gnc_engine_signal_commit_error( errcode ); 00467 } 00468 00469 static void bill_free (QofInstance *inst) 00470 { 00471 GncBillTerm *term = (GncBillTerm *) inst; 00472 gncBillTermFree(term); 00473 } 00474 00475 static void on_done (QofInstance *inst) {} 00476 00477 void gncBillTermCommitEdit (GncBillTerm *term) 00478 { 00479 if (!qof_commit_edit (QOF_INSTANCE(term))) return; 00480 qof_commit_edit_part2 (&term->inst, gncBillTermOnError, 00481 on_done, bill_free); 00482 } 00483 00484 /* Get Functions */ 00485 00486 GncBillTerm *gncBillTermLookupByName (QofBook *book, const char *name) 00487 { 00488 GList *list = gncBillTermGetTerms (book); 00489 00490 for ( ; list; list = list->next) 00491 { 00492 GncBillTerm *term = list->data; 00493 if (!safe_strcmp (term->name, name)) 00494 return list->data; 00495 } 00496 return NULL; 00497 } 00498 00499 GList * gncBillTermGetTerms (QofBook *book) 00500 { 00501 struct _book_info *bi; 00502 if (!book) return NULL; 00503 00504 bi = qof_book_get_data (book, _GNC_MOD_NAME); 00505 return bi->terms; 00506 } 00507 00508 const char *gncBillTermGetName (const GncBillTerm *term) 00509 { 00510 if (!term) return NULL; 00511 return term->name; 00512 } 00513 00514 const char *gncBillTermGetDescription (const GncBillTerm *term) 00515 { 00516 if (!term) return NULL; 00517 return term->desc; 00518 } 00519 00520 GncBillTermType gncBillTermGetType (const GncBillTerm *term) 00521 { 00522 if (!term) return 0; 00523 return term->type; 00524 } 00525 00527 AS_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE) 00528 00529 static 00530 const char* qofBillTermGetType (const GncBillTerm *term) 00531 { 00532 if (!term) 00533 { 00534 return NULL; 00535 } 00536 return GncBillTermTypeasString(term->type); 00537 } 00538 00539 gint gncBillTermGetDueDays (const GncBillTerm *term) 00540 { 00541 if (!term) return 0; 00542 return term->due_days; 00543 } 00544 00545 gint gncBillTermGetDiscountDays (const GncBillTerm *term) 00546 { 00547 if (!term) return 0; 00548 return term->disc_days; 00549 } 00550 00551 gnc_numeric gncBillTermGetDiscount (const GncBillTerm *term) 00552 { 00553 if (!term) return gnc_numeric_zero (); 00554 return term->discount; 00555 } 00556 00557 gint gncBillTermGetCutoff (const GncBillTerm *term) 00558 { 00559 if (!term) return 0; 00560 return term->cutoff; 00561 } 00562 00563 static GncBillTerm *gncBillTermCopy (const GncBillTerm *term) 00564 { 00565 GncBillTerm *t; 00566 00567 if (!term) return NULL; 00568 t = gncBillTermCreate (qof_instance_get_book(term)); 00569 00570 gncBillTermBeginEdit(t); 00571 00572 gncBillTermSetName (t, term->name); 00573 gncBillTermSetDescription (t, term->desc); 00574 00575 t->type = term->type; 00576 t->due_days = term->due_days; 00577 t->disc_days = term->disc_days; 00578 t->discount = term->discount; 00579 t->cutoff = term->cutoff; 00580 00581 gncBillTermCommitEdit(t); 00582 00583 return t; 00584 } 00585 00586 GncBillTerm *gncBillTermReturnChild (GncBillTerm *term, gboolean make_new) 00587 { 00588 GncBillTerm *child = NULL; 00589 00590 if (!term) return NULL; 00591 if (term->child) return term->child; 00592 if (term->parent || term->invisible) return term; 00593 if (make_new) 00594 { 00595 child = gncBillTermCopy (term); 00596 gncBillTermSetChild (term, child); 00597 gncBillTermSetParent (child, term); 00598 } 00599 return child; 00600 } 00601 00602 GncBillTerm *gncBillTermGetParent (const GncBillTerm *term) 00603 { 00604 if (!term) return NULL; 00605 return term->parent; 00606 } 00607 00608 gint64 gncBillTermGetRefcount (const GncBillTerm *term) 00609 { 00610 if (!term) return 0; 00611 return term->refcount; 00612 } 00613 00614 gboolean gncBillTermGetInvisible (const GncBillTerm *term) 00615 { 00616 if (!term) return FALSE; 00617 return term->invisible; 00618 } 00619 00620 int gncBillTermCompare (const GncBillTerm *a, const GncBillTerm *b) 00621 { 00622 int ret; 00623 00624 if (!a && !b) return 0; 00625 if (!a) return -1; 00626 if (!b) return 1; 00627 00628 ret = safe_strcmp (a->name, b->name); 00629 if (ret) return ret; 00630 00631 return safe_strcmp (a->desc, b->desc); 00632 } 00633 00634 gboolean gncBillTermEqual(const GncBillTerm *a, const GncBillTerm *b) 00635 { 00636 if (a == NULL && b == NULL) return TRUE; 00637 if (a == NULL || b == NULL) return FALSE; 00638 00639 g_return_val_if_fail(GNC_IS_BILLTERM(a), FALSE); 00640 g_return_val_if_fail(GNC_IS_BILLTERM(b), FALSE); 00641 00642 if (safe_strcmp(a->name, b->name) != 0) 00643 { 00644 PWARN("Names differ: %s vs %s", a->name, b->name); 00645 return FALSE; 00646 } 00647 00648 if (safe_strcmp(a->desc, b->desc) != 0) 00649 { 00650 PWARN("Descriptions differ: %s vs %s", a->desc, b->desc); 00651 return FALSE; 00652 } 00653 00654 if (a->type != b->type) 00655 { 00656 PWARN("Types differ"); 00657 return FALSE; 00658 } 00659 00660 if (a->due_days != b->due_days) 00661 { 00662 PWARN("Due days differ: %d vs %d", a->due_days, b->due_days); 00663 return FALSE; 00664 } 00665 00666 if (a->disc_days != b->disc_days) 00667 { 00668 PWARN("Discount days differ: %d vs %d", a->disc_days, b->disc_days); 00669 return FALSE; 00670 } 00671 00672 if (!gnc_numeric_equal(a->discount, b->discount)) 00673 { 00674 PWARN("Discounts differ"); 00675 return FALSE; 00676 } 00677 00678 if (a->cutoff != b->cutoff) 00679 { 00680 PWARN("Cutoffs differ: %d vs %d", a->cutoff, b->cutoff); 00681 return FALSE; 00682 } 00683 00684 if (a->invisible != b->invisible) 00685 { 00686 PWARN("Invisible flags differ"); 00687 return FALSE; 00688 } 00689 00690 // gint64 refcount; 00691 // GncBillTerm * parent; /* if non-null, we are an immutable child */ 00692 // GncBillTerm * child; /* if non-null, we have not changed */ 00693 // GList * children; /* list of children for disconnection */ 00694 00695 return TRUE; 00696 } 00697 00698 gboolean gncBillTermIsFamily (const GncBillTerm *a, const GncBillTerm *b) 00699 { 00700 if (!gncBillTermCompare (a, b)) 00701 return TRUE; 00702 else 00703 return FALSE; 00704 } 00705 00706 gboolean gncBillTermIsDirty (const GncBillTerm *term) 00707 { 00708 if (!term) return FALSE; 00709 return qof_instance_get_dirty_flag(term); 00710 } 00711 00712 /********************************************************/ 00713 /* functions to compute dates from Bill Terms */ 00714 00715 #define SECS_PER_DAY 86400 00716 00717 /* Based on the post date and a proximo type, compute the month and 00718 * year this is due. The actual day is filled in below. 00719 * 00720 * A proximo billing term has multiple parameters: 00721 * * due day: day of the month the invoice/bill will be due 00722 * * cutoff: day of the month used to decide if the due date will be 00723 * in the next month or in the month thereafter. This can be 00724 * a negative number in which case the cutoff date is relative 00725 * to the end of the month and counting backwards. 00726 * Eg: cutoff = -3 would mean 25 in February or 28 in June 00727 * 00728 * How does it work: 00729 * Assume cutoff = 19 and due day = 20 00730 * 00731 * * Example 1 post date = 14-06-2010 (European date format) 00732 * 14 is less than the cutoff of 19, so the due date will be in the next 00733 * month. Since the due day is set to 20, the due date will be 00734 * 20-07-2010 00735 * 00736 * * Example 2 post date = 22-06-2010 (European date format) 00737 * 22 is more than the cutoff of 19, so the due date will be in the month 00738 * after next month. Since the due day is set to 20, the due date will be 00739 * 20-02-2010 00740 * 00741 */ 00742 static void 00743 compute_monthyear (const GncBillTerm *term, Timespec post_date, 00744 int *month, int *year) 00745 { 00746 int iday, imonth, iyear; 00747 int cutoff = term->cutoff; 00748 00749 g_return_if_fail (term->type == GNC_TERM_TYPE_PROXIMO); 00750 00751 gnc_timespec2dmy (post_date, &iday, &imonth, &iyear); 00752 00753 if (cutoff <= 0) 00754 cutoff += gnc_timespec_last_mday (post_date); 00755 00756 if (iday <= cutoff) 00757 { 00758 /* We apply this to next month */ 00759 imonth++; 00760 } 00761 else 00762 { 00763 /* We apply to the following month */ 00764 imonth += 2; 00765 } 00766 00767 if (imonth > 12) 00768 { 00769 iyear++; 00770 imonth -= 12; 00771 } 00772 00773 if (month) *month = imonth; 00774 if (year) *year = iyear; 00775 } 00776 00777 /* There are two types of billing terms: 00778 * 00779 * Type DAYS defines a due date to be a fixed number of days passed the post 00780 * date. This is a straightforward calculation. 00781 * 00782 * The other type PROXIMO defines the due date as a fixed day of the month 00783 * (like always the 15th of the month). The proximo algorithm determines which 00784 * month based on the cutoff day and the post date. See above for a more 00785 * detailed explanation of proximo. 00786 */ 00787 00788 static Timespec 00789 compute_time (const GncBillTerm *term, Timespec post_date, int days) 00790 { 00791 Timespec res = post_date; 00792 int day, month, year; 00793 00794 switch (term->type) 00795 { 00796 case GNC_TERM_TYPE_DAYS: 00797 res.tv_sec += (SECS_PER_DAY * days); 00798 break; 00799 case GNC_TERM_TYPE_PROXIMO: 00800 compute_monthyear (term, post_date, &month, &year); 00801 day = gnc_date_my_last_mday (month, year); 00802 if (days < day) 00803 day = days; 00804 res = gnc_dmy2timespec (day, month, year); 00805 break; 00806 } 00807 return res; 00808 } 00809 00810 Timespec 00811 gncBillTermComputeDueDate (const GncBillTerm *term, Timespec post_date) 00812 { 00813 Timespec res = post_date; 00814 if (!term) return res; 00815 00816 return compute_time (term, post_date, term->due_days); 00817 } 00818 /* Package-Private functions */ 00819 00820 static void _gncBillTermCreate (QofBook *book) 00821 { 00822 struct _book_info *bi; 00823 00824 if (!book) return; 00825 00826 bi = g_new0 (struct _book_info, 1); 00827 qof_book_set_data (book, _GNC_MOD_NAME, bi); 00828 } 00829 00830 static void _gncBillTermDestroy (QofBook *book) 00831 { 00832 struct _book_info *bi; 00833 00834 if (!book) return; 00835 00836 bi = qof_book_get_data (book, _GNC_MOD_NAME); 00837 00838 g_list_free (bi->terms); 00839 g_free (bi); 00840 } 00841 00842 static QofObject gncBillTermDesc = 00843 { 00844 DI(.interface_version = ) QOF_OBJECT_VERSION, 00845 DI(.e_type = ) _GNC_MOD_NAME, 00846 DI(.type_label = ) "Billing Term", 00847 DI(.create = ) (gpointer)gncBillTermCreate, 00848 DI(.book_begin = ) _gncBillTermCreate, 00849 DI(.book_end = ) _gncBillTermDestroy, 00850 DI(.is_dirty = ) qof_collection_is_dirty, 00851 DI(.mark_clean = ) qof_collection_mark_clean, 00852 DI(.foreach = ) qof_collection_foreach, 00853 DI(.printable = ) NULL, 00854 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp, 00855 }; 00856 00857 gboolean gncBillTermRegister (void) 00858 { 00859 static QofParam params[] = 00860 { 00861 { GNC_BILLTERM_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncBillTermGetName, (QofSetterFunc)gncBillTermSetName }, 00862 { GNC_BILLTERM_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncBillTermGetDescription, (QofSetterFunc)gncBillTermSetDescription }, 00863 { GNC_BILLTERM_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofBillTermGetType, (QofSetterFunc)qofBillTermSetType }, 00864 { GNC_BILLTERM_DUEDAYS, QOF_TYPE_INT32, (QofAccessFunc)gncBillTermGetDueDays, (QofSetterFunc)gncBillTermSetDueDays }, 00865 { GNC_BILLTERM_DISCDAYS, QOF_TYPE_INT32, (QofAccessFunc)gncBillTermGetDiscountDays, (QofSetterFunc)gncBillTermSetDiscountDays }, 00866 { GNC_BILLTERM_DISCOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncBillTermGetDiscount, (QofSetterFunc)gncBillTermSetDiscount }, 00867 { GNC_BILLTERM_CUTOFF, QOF_TYPE_INT32, (QofAccessFunc)gncBillTermGetCutoff, (QofSetterFunc)gncBillTermSetCutoff }, 00868 { GNC_BILLTERM_REFCOUNT, QOF_TYPE_INT64, (QofAccessFunc)gncBillTermGetRefcount, NULL }, 00869 { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL }, 00870 { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL }, 00871 { NULL }, 00872 }; 00873 00874 qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncBillTermCompare, params); 00875 00876 return qof_object_register (&gncBillTermDesc); 00877 }
1.7.4