GnuCash 2.4.99
gncBillTerm.c
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 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines