/* 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-filter.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = */
/* Filter */
Filter *da_filter_malloc(void)
{
return g_malloc0(sizeof(Filter));
}
void da_filter_free(Filter *flt)
{
if(flt != NULL)
{
g_free(flt->wording);
g_free(flt->info);
g_free(flt->tag);
g_free(flt);
}
}
/* = = = = = = = = = = = = = = = = = = = = */
gchar *filter_daterange_text_get(Filter *flt)
{
gchar buffer1[128];
gchar buffer2[128];
GDate *date;
date = g_date_new_julian(flt->mindate);
g_date_strftime (buffer1, 128-1, PREFS->date_format, date);
g_date_set_julian(date, flt->maxdate);
g_date_strftime (buffer2, 128-1, PREFS->date_format, date);
g_date_free(date);
return g_strdup_printf(_("from %s to %s"), buffer1, buffer2);
}
static void filter_default_date_set(Filter *flt)
{
flt->mindate = 693596; //01/01/1900
flt->maxdate = 803533; //31/12/2200
}
static void filter_clear(Filter *flt)
{
guint i;
for(i=0;ioption[i] = 0;
}
g_free(flt->info);
g_free(flt->wording);
g_free(flt->tag);
flt->info = NULL;
flt->wording = NULL;
flt->tag = NULL;
flt->last_tab = 0;
}
void filter_default_all_set(Filter *flt)
{
gint i;
DB( g_print("(filter) reset %p\n", flt) );
filter_clear(flt);
flt->range = FLT_RANGE_LAST12MONTHS;
flt->type = FLT_TYPE_ALL;
flt->status = FLT_STATUS_ALL;
flt->option[FILTER_DATE] = 1;
filter_default_date_set(flt);
for(i=0;ipaymode[i] = TRUE;
filter_preset_daterange_set(flt, flt->range);
}
void filter_preset_daterange_set(Filter *flt, gint range)
{
GDate *date;
GList *list;
guint32 refjuliandate, month, year, qnum;
// any date :: todo : get date of current accout only when account
flt->range = range;
if(g_list_length(GLOBALS->ope_list) > 0) // get all transaction date bound
{
GLOBALS->ope_list = da_transaction_sort(GLOBALS->ope_list);
list = g_list_first(GLOBALS->ope_list);
flt->mindate = ((Transaction *)list->data)->date;
list = g_list_last(GLOBALS->ope_list);
flt->maxdate = ((Transaction *)list->data)->date;
}
else
filter_default_date_set(flt);
// by default refjuliandate is today
// but we adjust if to max transaction date found
refjuliandate = GLOBALS->today;
if(flt->maxdate < refjuliandate)
refjuliandate = flt->maxdate;
date = g_date_new_julian(refjuliandate);
month = g_date_get_month(date);
year = g_date_get_year(date);
qnum = ((month - 1) / 3) + 1;
DB( g_print("m=%d, y=%d, qnum=%d\n", month, year, qnum) );
switch( range )
{
case FLT_RANGE_THISMONTH:
g_date_set_day(date, 1);
flt->mindate = g_date_get_julian(date);
g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
flt->maxdate = g_date_get_julian(date);
break;
case FLT_RANGE_LASTMONTH:
g_date_set_day(date, 1);
g_date_subtract_months(date, 1);
flt->mindate = g_date_get_julian(date);
month = g_date_get_month(date);
year = g_date_get_year(date);
g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
flt->maxdate = g_date_get_julian(date);
break;
case FLT_RANGE_THISQUARTER:
g_date_set_day(date, 1);
g_date_set_month(date, (qnum-1)*3+1);
flt->mindate = g_date_get_julian(date);
g_date_add_months(date, 3);
g_date_subtract_days(date, 1);
flt->maxdate = g_date_get_julian(date);
break;
case FLT_RANGE_LASTQUARTER:
g_date_set_day(date, 1);
g_date_set_month(date, (qnum-1)*3+1);
g_date_subtract_months(date, 3);
flt->mindate = g_date_get_julian(date);
g_date_add_months(date, 3);
g_date_subtract_days(date, 1);
flt->maxdate = g_date_get_julian(date);
break;
case FLT_RANGE_THISYEAR:
g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
if( refjuliandate >= g_date_get_julian (date))
{
flt->mindate = g_date_get_julian(date);
}
else
{
g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-1);
flt->mindate = g_date_get_julian(date);
}
g_date_add_years (date, 1);
g_date_subtract_days (date, 1);
flt->maxdate = g_date_get_julian(date);
break;
case FLT_RANGE_LASTYEAR:
g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
if( refjuliandate >= g_date_get_julian (date))
{
g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-1);
flt->mindate = g_date_get_julian(date);
}
else
{
g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-2);
flt->mindate = g_date_get_julian(date);
}
g_date_add_years (date, 1);
g_date_subtract_days (date, 1);
flt->maxdate = g_date_get_julian(date);
break;
case FLT_RANGE_LAST30DAYS:
flt->mindate = refjuliandate - 30;
flt->maxdate = refjuliandate;
break;
case FLT_RANGE_LAST60DAYS:
flt->mindate = refjuliandate - 60;
flt->maxdate = refjuliandate;
break;
case FLT_RANGE_LAST90DAYS:
flt->mindate = refjuliandate - 90;
flt->maxdate = refjuliandate;
break;
case FLT_RANGE_LAST12MONTHS:
g_date_subtract_months(date, 12);
flt->mindate = g_date_get_julian(date);
flt->maxdate = refjuliandate;
break;
// case FLT_RANGE_OTHER:
// case FLT_RANGE_ALLDATE:
}
g_date_free(date);
}
void filter_preset_type_set(Filter *flt, gint type)
{
/* any type */
flt->type = type;
flt->option[FILTER_AMOUNT] = 0;
flt->minamount = G_MINDOUBLE;
flt->maxamount = G_MINDOUBLE;
switch( type )
{
case FLT_TYPE_EXPENSE:
flt->option[FILTER_AMOUNT] = 1;
flt->minamount = -G_MAXDOUBLE;
flt->maxamount = G_MINDOUBLE;
break;
case FLT_TYPE_INCOME:
flt->option[FILTER_AMOUNT] = 1;
flt->minamount = G_MINDOUBLE;
flt->maxamount = G_MAXDOUBLE;
break;
}
}
void filter_preset_status_set(Filter *flt, gint status)
{
Category *catitem;
GList *lcat, *list;
/* any status */
flt->status = status;
flt->option[FILTER_STATUS] = 0;
flt->reconciled = TRUE;
flt->reminded = TRUE;
flt->forceadd = FALSE;
flt->forcechg = FALSE;
flt->option[FILTER_CATEGORY] = 0;
lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
while (list != NULL)
{
catitem = list->data;
catitem->filter = FALSE;
list = g_list_next(list);
}
g_list_free(lcat);
switch( status )
{
case FLT_STATUS_UNCATEGORIZED:
flt->option[FILTER_CATEGORY] = 1;
catitem = da_cat_get(0); // no category
catitem->filter = TRUE;
break;
case FLT_STATUS_UNRECONCILED:
flt->option[FILTER_STATUS] = 2;
flt->reconciled = TRUE;
//#1336882
flt->reminded = FALSE;
break;
}
}
static gint filter_text_compare(gchar *txntext, gchar *searchtext, gboolean exact)
{
gint retval = 0;
if( exact )
{
if( g_strstr_len(txntext, -1, searchtext) != NULL )
{
DB( g_print(" found case '%s'\n", searchtext) );
retval = 1;
}
}
else
{
gchar *word = g_utf8_casefold(txntext, -1);
gchar *needle = g_utf8_casefold(searchtext, -1);
if( g_strrstr(word, needle) != NULL )
{
DB( g_print(" found nocase '%s'\n", needle) );
retval = 1;
}
g_free(word);
g_free(needle);
}
return retval;
}
/* used for quicksearch text into transaction */
gboolean filter_txn_search_match(gchar *needle, Transaction *txn, gint flags)
{
gboolean retval = FALSE;
Payee *payitem;
Category *catitem;
gchar *tags;
if(flags & FLT_QSEARCH_MEMO)
{
if(txn->wording)
{
retval |= filter_text_compare(txn->wording, needle, FALSE);
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_INFO)
{
if(txn->info)
{
retval |= filter_text_compare(txn->info, needle, FALSE);
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_PAYEE)
{
payitem = da_pay_get(txn->kpay);
if(payitem)
{
retval |= filter_text_compare(payitem->name, needle, FALSE);
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_CATEGORY)
{
catitem = da_cat_get(txn->kcat);
if(catitem)
{
gchar *fullname = da_cat_get_fullname (catitem);
retval |= filter_text_compare(fullname, needle, FALSE);
g_free(fullname);
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_TAGS)
{
tags = transaction_tags_tostring(txn);
if(tags)
{
retval |= filter_text_compare(tags, needle, FALSE);
}
g_free(tags);
//if(retval) goto end;
}
end:
return retval;
}
gint filter_test(Filter *flt, Transaction *txn)
{
Account *accitem;
Payee *payitem;
Category *catitem;
gint insert;
//DB( g_print("(filter) test\n") );
insert = 1;
/*** start filtering ***/
/* add/change force */
if(flt->forceadd == TRUE && (txn->flags & OF_ADDED))
goto end;
if(flt->forcechg == TRUE && (txn->flags & OF_CHANGED))
goto end;
/* date */
if(flt->option[FILTER_DATE]) {
insert = ( (txn->date >= flt->mindate) && (txn->date <= flt->maxdate) ) ? 1 : 0;
if(flt->option[FILTER_DATE] == 2) insert ^= 1;
}
if(!insert) goto end;
/* account */
if(flt->option[FILTER_ACCOUNT]) {
accitem = da_acc_get(txn->kacc);
if(accitem)
{
insert = ( accitem->filter == TRUE ) ? 1 : 0;
if(flt->option[FILTER_ACCOUNT] == 2) insert ^= 1;
}
}
if(!insert) goto end;
/* payee */
if(flt->option[FILTER_PAYEE]) {
payitem = da_pay_get(txn->kpay);
if(payitem)
{
insert = ( payitem->filter == TRUE ) ? 1 : 0;
if(flt->option[FILTER_PAYEE] == 2) insert ^= 1;
}
}
if(!insert) goto end;
/* category */
if(flt->option[FILTER_CATEGORY]) {
if(txn->flags & OF_SPLIT)
{
guint count, i;
Split *split;
insert = 0; //fix: 1151259
count = da_transaction_splits_count(txn);
for(i=0;isplits[i];
catitem = da_cat_get(split->kcat);
if(catitem)
{
tmpinsert = ( catitem->filter == TRUE ) ? 1 : 0;
if(flt->option[FILTER_CATEGORY] == 2) tmpinsert ^= 1;
}
insert |= tmpinsert;
}
}
else
{
catitem = da_cat_get(txn->kcat);
if(catitem)
{
insert = ( catitem->filter == TRUE ) ? 1 : 0;
if(flt->option[FILTER_CATEGORY] == 2) insert ^= 1;
}
}
}
if(!insert) goto end;
/* status */
if(flt->option[FILTER_STATUS]) {
gint insert1 = 0, insert2 = 0;
if(flt->reconciled)
insert1 = ( txn->flags & OF_VALID ) ? 1 : 0;
if(flt->reminded)
insert2 = ( txn->flags & OF_REMIND ) ? 1 : 0;
insert = insert1 | insert2;
if(flt->option[FILTER_STATUS] == 2) insert ^= 1;
}
if(!insert) goto end;
/* paymode */
if(flt->option[FILTER_PAYMODE]) {
insert = ( flt->paymode[txn->paymode] == TRUE) ? 1 : 0;
if(flt->option[FILTER_PAYMODE] == 2) insert ^= 1;
}
if(!insert) goto end;
/* amount */
if(flt->option[FILTER_AMOUNT]) {
insert = ( (txn->amount >= flt->minamount) && (txn->amount <= flt->maxamount) ) ? 1 : 0;
if(flt->option[FILTER_AMOUNT] == 2) insert ^= 1;
}
if(!insert) goto end;
/* info/wording/tag */
if(flt->option[FILTER_TEXT])
{
gchar *tags;
gint insert1, insert2, insert3;
insert1 = insert2 = insert3 = 0;
if(flt->info)
{
if(txn->info)
{
insert1 = filter_text_compare(txn->info, flt->info, flt->exact);
}
}
else
insert1 = 1;
if(flt->wording)
{
if(txn->wording)
{
insert2 = filter_text_compare(txn->wording, flt->wording, flt->exact);
}
}
else
insert2 = 1;
if(flt->tag)
{
tags = transaction_tags_tostring(txn);
if(tags)
{
insert3 = filter_text_compare(tags, flt->tag, flt->exact);
}
g_free(tags);
}
else
insert3 = 1;
insert = insert1 && insert2 && insert3 ? 1 : 0;
if(flt->option[FILTER_TEXT] == 2) insert ^= 1;
}
if(!insert) goto end;
end:
// DB( g_print(" %d :: %d :: %d\n", flt->mindate, txn->date, flt->maxdate) );
// DB( g_print(" [%d] %s => %d (%d)\n", txn->account, txn->wording, insert, count) );
return(insert);
}