X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fhomebank;a=blobdiff_plain;f=src%2Fhb-archive.c;h=cbb732883fd598b6d53f70e06dd497dc69a80a7a;hp=0ede27030cce95f71fb29d1ef1f7c0f67da9a627;hb=HEAD;hpb=27f6e3b112df235c8e9afc9911b1f6bce208a001 diff --git a/src/hb-archive.c b/src/hb-archive.c index 0ede270..cbb7328 100644 --- a/src/hb-archive.c +++ b/src/hb-archive.c @@ -1,5 +1,5 @@ /* HomeBank -- Free, easy, personal accounting for everyone. - * Copyright (C) 1995-2014 Maxime DOYEN + * Copyright (C) 1995-2019 Maxime DOYEN * * This file is part of HomeBank. * @@ -19,6 +19,7 @@ #include "homebank.h" #include "hb-archive.h" +#include "hb-split.h" /****************************************************************************/ /* Debug macros */ @@ -35,14 +36,60 @@ extern struct HomeBank *GLOBALS; -/* = = = = = = = = = = = = = = = = = = = = */ -/* Archive */ +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ + + +static void +da_archive_clean(Archive *item) +{ + if(item != NULL) + { + if(item->memo != NULL) + { + g_free(item->memo); + item->memo = NULL; + } + + + + + + //5.3 added as it was a leak + if(item->tags != NULL) + { + g_free(item->tags); + item->tags = NULL; + } + if(item->splits != NULL) + { + da_split_destroy(item->splits); + item->splits = NULL; + item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared + } + } +} + + +void da_archive_free(Archive *item) +{ + if(item != NULL) + { + da_archive_clean(item); + g_free(item); + } +} + Archive *da_archive_malloc(void) { - return g_malloc0(sizeof(Archive)); +Archive *item; + + item = g_malloc0(sizeof(Archive)); + item->key = 1; + return item; } + Archive *da_archive_clone(Archive *src_item) { Archive *new_item = g_memdup(src_item, sizeof(Archive)); @@ -50,22 +97,22 @@ Archive *new_item = g_memdup(src_item, sizeof(Archive)); if(new_item) { //duplicate the string - new_item->wording = g_strdup(src_item->wording); - } - return new_item; -} + new_item->memo = g_strdup(src_item->memo); -void da_archive_free(Archive *item) -{ - if(item != NULL) - { - if(item->wording != NULL) - g_free(item->wording); + //duplicate tags + //no g_free here to avoid free the src tags (memdup copie dthe ptr) + new_item->tags = tags_clone(src_item->tags); - g_free(item); + //duplicate splits + //no g_free here to avoid free the src tags (memdup copie dthe ptr) + new_item->splits = da_splits_clone(src_item->splits); + if( da_splits_length (new_item->splits) > 0 ) + new_item->flags |= OF_SPLIT; //Flag that Splits are active } + return new_item; } + void da_archive_destroy(GList *list) { GList *tmplist = g_list_first(list); @@ -79,9 +126,13 @@ GList *tmplist = g_list_first(list); g_list_free(list); } + +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ + + static gint da_archive_glist_compare_func(Archive *a, Archive *b) { - return hb_string_utf8_compare(a->wording, b->wording); + return hb_string_utf8_compare(a->memo, b->memo); } @@ -90,16 +141,77 @@ GList *da_archive_sort(GList *list) return g_list_sort(list, (GCompareFunc)da_archive_glist_compare_func); } + guint da_archive_length(void) { return g_list_length(GLOBALS->arc_list); } + +/* append a fav with an existing key (from xml file only) */ +gboolean +da_archive_append(Archive *item) +{ + GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item); + return TRUE; +} + + +gboolean +da_archive_append_new(Archive *item) +{ + item->key = da_archive_get_max_key() + 1; + GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item); + return TRUE; +} + + +guint32 +da_archive_get_max_key(void) +{ +GList *tmplist = g_list_first(GLOBALS->arc_list); +guint32 max_key = 0; + + while (tmplist != NULL) + { + Archive *item = tmplist->data; + + max_key = MAX(item->key, max_key); + tmplist = g_list_next(tmplist); + } + + return max_key; +} + + +Archive * +da_archive_get(guint32 key) +{ +GList *tmplist; +Archive *retval = NULL; + + tmplist = g_list_first(GLOBALS->arc_list); + while (tmplist != NULL) + { + Archive *item = tmplist->data; + + if(item->key == key) + { + retval = item; + break; + } + tmplist = g_list_next(tmplist); + } + return retval; +} + + void da_archive_consistency(Archive *item) { Account *acc; Category *cat; Payee *pay; +guint nbsplit; // check category exists cat = da_cat_get(item->kcat); @@ -107,27 +219,42 @@ Payee *pay; { g_warning("arc consistency: fixed invalid cat %d", item->kcat); item->kcat = 0; + GLOBALS->changes_count++; } + //#1340142 check split category + if( item->splits != NULL ) + { + nbsplit = da_splits_consistency(item->splits); + //# 1416624 empty category when split + if(nbsplit > 0 && item->kcat > 0) + { + g_warning("txn consistency: fixed invalid cat on split txn"); + item->kcat = 0; + GLOBALS->changes_count++; + } + } + // check payee exists pay = da_pay_get(item->kpay); if(pay == NULL) { g_warning("arc consistency: fixed invalid pay %d", item->kpay); item->kpay = 0; + GLOBALS->changes_count++; } // reset dst acc for non xfer transaction if( item->paymode != PAYMODE_INTXFER ) item->kxferacc = 0; - // remove automation if dst_acc not exists + // delete automation if dst_acc not exists if(item->paymode == PAYMODE_INTXFER) { acc = da_acc_get(item->kxferacc); if(acc == NULL) { - item->flags &= ~(OF_AUTO); //remove flag + item->flags &= ~(OF_AUTO); //delete flag } } @@ -135,12 +262,43 @@ Payee *pay; /* = = = = = = = = = = = = = = = = = = = = */ -static guint32 _sched_date_get_next_post(Archive *arc, guint32 nextdate) +Archive *da_archive_init_from_transaction(Archive *arc, Transaction *txn) { -GDate *tmpdate; -guint32 nextpostdate = nextdate; + DB( g_print("\n[scheduled] init from txn\n") ); + + //fill it + arc->amount = txn->amount; + arc->kacc = txn->kacc; + arc->kxferacc = txn->kxferacc; + arc->paymode = txn->paymode; + arc->flags = txn->flags & (OF_INCOME); + arc->status = txn->status; + arc->kpay = txn->kpay; + arc->kcat = txn->kcat; + if(txn->memo != NULL) + arc->memo = g_strdup(txn->memo); + else + arc->memo = g_strdup(_("(new archive)")); + + arc->tags = tags_clone(txn->tags); + arc->splits = da_splits_clone(txn->splits); + if( da_splits_length (arc->splits) > 0 ) + arc->flags |= OF_SPLIT; //Flag that Splits are active - tmpdate = g_date_new_julian(nextpostdate); + return arc; +} + + +static guint32 _sched_date_get_next_post(GDate *tmpdate, Archive *arc, guint32 nextdate) +{ +guint32 nextpostdate = nextdate; + + //DB( g_print("\n[scheduled] date_get_next_post\n") ); + + g_date_set_julian(tmpdate, nextpostdate); + + //DB( g_print("in : %2d-%2d-%4d\n", g_date_get_day(tmpdate), g_date_get_month (tmpdate), g_date_get_year(tmpdate) ) ); + switch(arc->unit) { case AUTO_UNIT_DAY: @@ -157,21 +315,12 @@ guint32 nextpostdate = nextdate; break; } + //DB( g_print("out: %2d-%2d-%4d\n", g_date_get_day(tmpdate), g_date_get_month (tmpdate), g_date_get_year(tmpdate) ) ); + + /* get the final post date and free */ nextpostdate = g_date_get_julian(tmpdate); - g_date_free(tmpdate); - /* check limit, update and maybe break */ - if(arc->flags & OF_LIMIT) - { - arc->limit--; - if(arc->limit <= 0) - { - arc->flags ^= (OF_LIMIT | OF_AUTO); // invert flags - nextpostdate = 0; - } - } - return nextpostdate; } @@ -180,7 +329,7 @@ gboolean scheduled_is_postable(Archive *arc) { gdouble value; - value = arrondi(arc->amount, 2); + value = hb_amount_round(arc->amount, 2); if( (arc->flags & OF_AUTO) && (arc->kacc > 0) && (value != 0.0) ) return TRUE; @@ -195,6 +344,9 @@ GDateWeekday wday; guint32 finalpostdate; gint shift; + DB( g_print("\n[scheduled] get_postdate\n") ); + + finalpostdate = postdate; tmpdate = g_date_new_julian(finalpostdate); @@ -203,7 +355,7 @@ gint shift; { wday = g_date_get_weekday(tmpdate); - DB( g_print(" %s wday=%d\n", arc->wording, wday) ); + DB( g_print(" %s wday=%d\n", arc->memo, wday) ); if( wday >= G_DATE_SATURDAY ) { @@ -232,31 +384,126 @@ gint shift; } +guint32 scheduled_get_latepost_count(Archive *arc, guint32 jrefdate) +{ +GDate *post_date; +guint32 curdate; +guint32 nblate = 0; + //DB( g_print("\n[scheduled] get_latepost_count\n") ); + /* + curdate = jrefdate - arc->nextdate; + switch(arc->unit) + { + case AUTO_UNIT_DAY: + nbpost = (curdate / arc->every); + g_print("debug d: %d => %f\n", curdate, nbpost); + break; -guint32 scheduled_get_latepost_count(Archive *arc, guint32 jrefdate) -{ -guint32 nbpost = 0; -guint32 curdate = arc->nextdate; + case AUTO_UNIT_WEEK: + nbpost = (curdate / ( 7 * arc->every)); + g_print("debug w: %d => %f\n", curdate, nbpost); + break; + + case AUTO_UNIT_MONTH: + //approximate is sufficient + nbpost = (curdate / (( 365.2425 / 12) * arc->every)); + g_print("debug m: %d => %f\n", curdate, nbpost); + break; + + case AUTO_UNIT_YEAR: + //approximate is sufficient + nbpost = (curdate / ( 365.2425 * arc->every)); + g_print("debug y: %d => %f\n", curdate, nbpost); + break; + } + + nblate = floor(nbpost); + + if(arc->flags & OF_LIMIT) + nblate = MIN(nblate, arc->limit); + + nblate = MIN(nblate, 11); + */ + + // pre 5.1 way + post_date = g_date_new(); + curdate = arc->nextdate; while(curdate <= jrefdate) { - curdate = _sched_date_get_next_post(arc, curdate); - nbpost++; - // break at 11 max (to display +10) - if(nbpost >= 11) + curdate = _sched_date_get_next_post(post_date, arc, curdate); + nblate++; + // break if over limit or at 11 max (to display +10) + if( nblate >= 11 || ( (arc->flags & OF_LIMIT) && (nblate >= arc->limit) ) ) break; } - return nbpost; + //DB( g_print(" nblate=%d\n", nblate) ); + + g_date_free(post_date); + + return nblate; } /* return 0 is max number of post is reached */ guint32 scheduled_date_advance(Archive *arc) { - arc->nextdate = _sched_date_get_next_post(arc, arc->nextdate); +GDate *post_date; +gushort lastday; + + DB( g_print("\n[scheduled] date_advance\n") ); + + DB( g_print(" arc: '%s'\n", arc->memo ) ); + + post_date = g_date_new(); + g_date_set_julian(post_date, arc->nextdate); + // saved the current day number + lastday = g_date_get_day(post_date); + + arc->nextdate = _sched_date_get_next_post(post_date, arc, arc->nextdate); + + DB( g_print(" raw next post date: %2d-%2d-%4d\n", g_date_get_day(post_date), g_date_get_month (post_date), g_date_get_year(post_date) ) ); + + //for day > 28 we might have a gap to compensate later + if( (arc->unit==AUTO_UNIT_MONTH) || (arc->unit==AUTO_UNIT_YEAR) ) + { + if( lastday >= 28 ) + { + DB( g_print(" lastday:%d, daygap:%d\n", lastday, arc->daygap) ); + if( arc->daygap > 0 ) + { + g_date_add_days (post_date, arc->daygap); + arc->nextdate = g_date_get_julian (post_date); + lastday += arc->daygap; + DB( g_print(" adjusted post date: %2d-%2d-%4d\n", g_date_get_day(post_date), g_date_get_month (post_date), g_date_get_year(post_date) ) ); + } + + arc->daygap = CLAMP(lastday - g_date_get_day(post_date), 0, 3); + + DB( g_print(" daygap is %d\n", arc->daygap) ); + } + else + arc->daygap = 0; + } + + + //#1556289 + /* check limit, update and maybe break */ + if(arc->flags & OF_LIMIT) + { + arc->limit--; + if(arc->limit <= 0) + { + arc->flags ^= (OF_LIMIT | OF_AUTO); // invert flags + arc->nextdate = 0; + } + } + + g_date_free(post_date); + return arc->nextdate; } @@ -322,7 +569,7 @@ Transaction *txn; { Archive *arc = list->data; - DB( g_print("\n eval %d for '%s'\n", scheduled_is_postable(arc), arc->wording) ); + DB( g_print("\n eval %d for '%s'\n", scheduled_is_postable(arc), arc->memo) ); if(scheduled_is_postable(arc) == TRUE) { @@ -335,13 +582,13 @@ Transaction *txn; while(mydate < maxpostdate) { - DB( hb_print_date(mydate, arc->wording) ); + DB( hb_print_date(mydate, arc->memo) ); da_transaction_init_from_template(txn, arc); txn->date = scheduled_get_postdate(arc, mydate); /* todo: ? fill in cheque number */ - transaction_add(txn, NULL, 0); + transaction_add(NULL, txn); GLOBALS->changes_count++; count++;