/* HomeBank -- Free, easy, personal accounting for everyone. * Copyright (C) 1995-2014 Maxime DOYEN * * This file is part of HomeBank. * * HomeBank is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * HomeBank is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "homebank.h" #include "hb-archive.h" /****************************************************************************/ /* Debug macros */ /****************************************************************************/ #define MYDEBUG 0 #if MYDEBUG #define DB(x) (x); #else #define DB(x); #endif /* our global datas */ extern struct HomeBank *GLOBALS; /* = = = = = = = = = = = = = = = = = = = = */ /* Archive */ Archive *da_archive_malloc(void) { return g_malloc0(sizeof(Archive)); } Archive *da_archive_clone(Archive *src_item) { 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; } void da_archive_free(Archive *item) { if(item != NULL) { if(item->wording != NULL) g_free(item->wording); g_free(item); } } void da_archive_destroy(GList *list) { GList *tmplist = g_list_first(list); while (tmplist != NULL) { Archive *item = tmplist->data; da_archive_free(item); tmplist = g_list_next(tmplist); } g_list_free(list); } static gint da_archive_glist_compare_func(Archive *a, Archive *b) { return hb_string_utf8_compare(a->wording, b->wording); } 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); } void da_archive_consistency(Archive *item) { Account *acc; Category *cat; Payee *pay; // check category exists cat = da_cat_get(item->kcat); if(cat == NULL) { g_warning("arc consistency: fixed invalid cat %d", item->kcat); item->kcat = 0; } // 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; } // reset dst acc for non xfer transaction if( item->paymode != PAYMODE_INTXFER ) item->kxferacc = 0; // remove 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 } } } /* = = = = = = = = = = = = = = = = = = = = */ static guint32 _sched_date_get_next_post(Archive *arc, guint32 nextdate) { GDate *tmpdate; guint32 nextpostdate = nextdate; tmpdate = g_date_new_julian(nextpostdate); switch(arc->unit) { case AUTO_UNIT_DAY: g_date_add_days(tmpdate, arc->every); break; case AUTO_UNIT_WEEK: g_date_add_days(tmpdate, 7 * arc->every); break; case AUTO_UNIT_MONTH: g_date_add_months(tmpdate, arc->every); break; case AUTO_UNIT_YEAR: g_date_add_years(tmpdate, arc->every); break; } /* 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; } gboolean scheduled_is_postable(Archive *arc) { gdouble value; value = arrondi(arc->amount, 2); if( (arc->flags & OF_AUTO) && (arc->kacc > 0) && (value != 0.0) ) return TRUE; return FALSE; } guint32 scheduled_get_postdate(Archive *arc, guint32 postdate) { GDate *tmpdate; GDateWeekday wday; guint32 finalpostdate; gint shift; finalpostdate = postdate; tmpdate = g_date_new_julian(finalpostdate); /* manage weekend exception */ if( arc->weekend > 0 ) { wday = g_date_get_weekday(tmpdate); DB( g_print(" %s wday=%d\n", arc->wording, wday) ); if( wday >= G_DATE_SATURDAY ) { switch(arc->weekend) { case 1: /* shift before : sun 7-5=+2 , sat 6-5=+1 */ shift = wday - G_DATE_FRIDAY; DB( g_print("sub=%d\n", shift) ); g_date_subtract_days (tmpdate, shift); break; case 2: /* shift after : sun 8-7=1 , sat 8-6=2 */ shift = 8 - wday; DB( g_print("add=%d\n", shift) ); g_date_add_days (tmpdate, shift); break; } } } /* get the final post date and free */ finalpostdate = g_date_get_julian(tmpdate); g_date_free(tmpdate); return finalpostdate; } guint32 scheduled_get_latepost_count(Archive *arc, guint32 jrefdate) { guint32 nbpost = 0; guint32 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) break; } return nbpost; } /* 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); return arc->nextdate; } /* * return the maximum date a scheduled txn can be posted to */ guint32 scheduled_date_get_post_max(void) { guint nbdays; GDate *today, *maxdate; DB( g_print("\n[scheduled] date_get_post_max\n") ); //add until xx of the next month (excluded) if(GLOBALS->auto_smode == 0) { DB( g_print(" - max is %d of next month\n", GLOBALS->auto_weekday) ); today = g_date_new_julian(GLOBALS->today); //we compute user xx weekday of next month maxdate = g_date_new_julian(GLOBALS->today); g_date_set_day(maxdate, GLOBALS->auto_weekday); if(g_date_get_day (today) >= GLOBALS->auto_weekday) g_date_add_months(maxdate, 1); nbdays = g_date_days_between(today, maxdate); g_date_free(maxdate); g_date_free(today); } else { nbdays = GLOBALS->auto_nbdays; } DB( hb_print_date(GLOBALS->today, "today") ); DB( g_print(" - %d nbdays\n", nbdays) ); DB( hb_print_date(GLOBALS->today + nbdays, "maxpostdate") ); return GLOBALS->today + nbdays; } gint scheduled_post_all_pending(void) { GList *list; gint count; guint32 maxpostdate; Transaction *txn; DB( g_print("\n[scheduled] post_all_pending\n") ); count = 0; maxpostdate = scheduled_date_get_post_max(); txn = da_transaction_malloc(); list = g_list_first(GLOBALS->arc_list); while (list != NULL) { Archive *arc = list->data; DB( g_print("\n eval %d for '%s'\n", scheduled_is_postable(arc), arc->wording) ); if(scheduled_is_postable(arc) == TRUE) { DB( g_print(" - every %d limit %d (to %d)\n", arc->every, arc->flags & OF_LIMIT, arc->limit) ); DB( hb_print_date(arc->nextdate, "next post") ); if(arc->nextdate < maxpostdate) { guint32 mydate = arc->nextdate; while(mydate < maxpostdate) { DB( hb_print_date(mydate, arc->wording) ); da_transaction_init_from_template(txn, arc); txn->date = scheduled_get_postdate(arc, mydate); /* todo: ? fill in cheque number */ transaction_add(txn, NULL, 0); GLOBALS->changes_count++; count++; da_transaction_clean(txn); mydate = scheduled_date_advance(arc); //DB( hb_print_date(mydate, "next on") ); if(mydate == 0) goto nextarchive; } } } nextarchive: list = g_list_next(list); } da_transaction_free (txn); return count; }