1 /*  HomeBank -- Free, easy, personal accounting for everyone.
2  *  Copyright (C) 1995-2021 Maxime DOYEN
3  *
4  *  This file is part of HomeBank.
5  *
6  *  HomeBank is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  HomeBank is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "homebank.h"
21 #include "hb-filter.h"
22 
23 /****************************************************************************/
24 /* Debug macros                                                             */
25 /****************************************************************************/
26 #define MYDEBUG 0
27 
28 #if MYDEBUG
29 #define DB(x) (x);
30 #else
31 #define DB(x);
32 #endif
33 
34 /* our global datas */
35 extern struct HomeBank *GLOBALS;
36 extern struct Preferences *PREFS;
37 
38 
39 /* = = = = = = = = = = = = = = = = */
40 
da_flt_free(Filter * flt)41 void da_flt_free(Filter *flt)
42 {
43 	DB( g_print("da_flt_free\n") );
44 	if(flt != NULL)
45 	{
46 		g_free(flt->memo);
47 		g_free(flt->info);
48 		g_free(flt->tag);
49 		g_free(flt);
50 	}
51 }
52 
53 
da_flt_malloc(void)54 Filter *da_flt_malloc(void)
55 {
56 	DB( g_print("da_flt_malloc\n") );
57 	return g_malloc0(sizeof(Filter));
58 }
59 
da_flt_status_cat_set(Filter * flt,guint32 kcat,gboolean status)60 void da_flt_status_cat_set(Filter *flt, guint32 kcat, gboolean status)
61 {
62 Category *cat = da_cat_get(kcat);
63 	if(cat)
64 		cat->flt_select = status;
65 }
66 
67 
da_flt_status_pay_set(Filter * flt,guint32 kpay,gboolean status)68 void da_flt_status_pay_set(Filter *flt, guint32 kpay, gboolean status)
69 {
70 Payee *pay = da_pay_get(kpay);
71 	if(pay)
72 		pay->flt_select = status;
73 }
74 
75 
da_flt_status_acc_set(Filter * flt,guint32 kacc,gboolean status)76 void da_flt_status_acc_set(Filter *flt, guint32 kacc, gboolean status)
77 {
78 Account *acc = da_acc_get(kacc);
79 	if(acc)
80 		acc->flt_select = status;
81 }
82 
83 
da_flt_status_cat_get(Filter * flt,guint32 kcat)84 gboolean da_flt_status_cat_get(Filter *flt, guint32 kcat)
85 {
86 Category *cat = da_cat_get(kcat);
87 	if(cat)
88 		return cat->flt_select;
89 	return FALSE;
90 }
91 
92 
da_flt_status_pay_get(Filter * flt,guint32 kpay)93 gboolean da_flt_status_pay_get(Filter *flt, guint32 kpay)
94 {
95 Payee *pay = da_pay_get(kpay);
96 	if(pay)
97 		return pay->flt_select;
98 	return FALSE;
99 }
100 
101 
da_flt_status_acc_get(Filter * flt,guint32 kacc)102 gboolean da_flt_status_acc_get(Filter *flt, guint32 kacc)
103 {
104 Account *acc = da_acc_get(kacc);
105 	if(acc)
106 		return acc->flt_select;
107 	return FALSE;
108 }
109 
110 
111 /* = = = = = = = = = = = = = = = = = = = = */
112 
113 
filter_status_acc_clear_except(Filter * flt,guint32 selkey)114 void filter_status_acc_clear_except(Filter *flt, guint32 selkey)
115 {
116 GHashTableIter iter;
117 gpointer key, value;
118 
119 	// set all account
120 	g_hash_table_iter_init (&iter, GLOBALS->h_acc);
121 	while (g_hash_table_iter_next (&iter, &key, &value))
122 	{
123 	Account *item = value;
124 		item->flt_select = item->key == selkey ? TRUE : FALSE;
125 	}
126 
127 }
128 
129 
filter_status_pay_clear_except(Filter * flt,guint32 selkey)130 void filter_status_pay_clear_except(Filter *flt, guint32 selkey)
131 {
132 GHashTableIter iter;
133 gpointer key, value;
134 
135 	// set all payee
136 	g_hash_table_iter_init (&iter, GLOBALS->h_pay);
137 	while (g_hash_table_iter_next (&iter, &key, &value))
138 	{
139 	Payee *item = value;
140 		item->flt_select = item->key == selkey ? TRUE : FALSE;
141 	}
142 
143 }
144 
145 
filter_status_cat_clear_except(Filter * flt,guint32 selkey)146 void filter_status_cat_clear_except(Filter *flt, guint32 selkey)
147 {
148 GHashTableIter iter;
149 gpointer key, value;
150 
151 	// set all category
152 	g_hash_table_iter_init (&iter, GLOBALS->h_cat);
153 	while (g_hash_table_iter_next (&iter, &key, &value))
154 	{
155 	Category *item = value;
156 
157 
158 		item->flt_select = FALSE;
159 		if( (item->key == selkey)
160 		//#1824561 don't forget subcat
161 		//#1829076 but not when selkey is 0
162 		   || ((item->parent == selkey) && selkey > 0)
163 		)
164 			item->flt_select = TRUE;
165 	}
166 
167 }
168 
169 
170 /* = = = = = = = = = = = = = = = = */
171 
172 
filter_reset(Filter * flt)173 void filter_reset(Filter *flt)
174 {
175 gint i;
176 
177 	DB( g_print("\n[filter] default reset all %p\n", flt) );
178 
179 	for(i=0;i<FLT_GRP_MAX;i++)
180 	{
181 		flt->option[i] = 0;
182 	}
183 
184 	flt->option[FLT_GRP_DATE] = 1;
185 
186 	//5.4.2: useless, as it is always changed after all
187 	//flt->range  = FLT_RANGE_LAST12MONTHS;
188 	//filter_preset_daterange_set(flt, flt->range, 0);
189 	flt->range = FLT_RANGE_LAST30DAYS;
190 	flt->mindate = GLOBALS->today-30;
191 	flt->maxdate = GLOBALS->today;
192 
193 	for(i=0;i<NUM_PAYMODE_MAX;i++)
194 		flt->paymode[i] = TRUE;
195 
196 	g_free(flt->info);
197 	g_free(flt->memo);
198 	g_free(flt->tag);
199 	flt->info = NULL;
200 	flt->memo = NULL;
201 	flt->tag = NULL;
202 
203 	//unsaved
204 	flt->nbdaysfuture = 0;
205 	flt->type   = FLT_TYPE_ALL;
206 	flt->status = FLT_STATUS_ALL;
207 	flt->forceremind = PREFS->showremind;
208 	flt->forcevoid = PREFS->showvoid;
209 
210 	*flt->last_tab = '\0';
211 }
212 
213 
filter_set_tag_by_id(Filter * flt,guint32 key)214 void filter_set_tag_by_id(Filter *flt, guint32 key)
215 {
216 Tag *tag;
217 
218 	DB( g_print("\n[filter] set tag by id\n") );
219 
220 	if(flt->tag)
221 	{
222 		g_free(flt->tag);
223 		flt->tag = NULL;
224 	}
225 
226 	tag = da_tag_get(key);
227 	if(tag)
228 	{
229 		flt->tag = g_strdup(tag->name);
230 	}
231 }
232 
233 
filter_set_date_bounds(Filter * flt,guint32 kacc)234 static void filter_set_date_bounds(Filter *flt, guint32 kacc)
235 {
236 GList *lst_acc, *lnk_acc;
237 GList *lnk_txn;
238 
239 	DB( g_print("\n[filter] set date bounds %p\n", flt) );
240 
241 	flt->mindate = 0;
242 	flt->maxdate = 0;
243 
244 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
245 	lnk_acc = g_list_first(lst_acc);
246 	while (lnk_acc != NULL)
247 	{
248 	Account *acc = lnk_acc->data;
249 
250 		//#1674045 only rely on nosummary
251 		//if( !(acc->flags & AF_CLOSED) )
252 		{
253 		Transaction *txn;
254 
255 			DB( g_print(" - do '%s'\n", acc->name) );
256 
257 			lnk_txn = g_queue_peek_head_link(acc->txn_queue);
258 			if(lnk_txn) {
259 				txn = lnk_txn->data;
260 				if( (kacc == 0) || (txn->kacc == kacc) )
261 				{
262 					if( flt->mindate == 0 )
263 						flt->mindate = txn->date;
264 					else
265 						flt->mindate = MIN(flt->mindate, txn->date);
266 				}
267 			}
268 
269 			lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
270 			if(lnk_txn) {
271 				txn = lnk_txn->data;
272 				if( (kacc == 0) || (txn->kacc == kacc) )
273 				{
274 					if( flt->maxdate == 0 )
275 						flt->maxdate = txn->date;
276 					else
277 						flt->maxdate = MAX(flt->maxdate, txn->date);
278 				}
279 			}
280 
281 		}
282 		lnk_acc = g_list_next(lnk_acc);
283 	}
284 
285 	if( flt->mindate == 0 )
286 		//changed 5.3
287 		//flt->mindate = HB_MINDATE;
288 		flt->mindate = GLOBALS->today - 365;
289 
290 	if( flt->maxdate == 0 )
291 		//changed 5.3
292 		//flt->maxdate = HB_MAXDATE;
293 		flt->maxdate = GLOBALS->today + flt->nbdaysfuture;
294 
295 	g_list_free(lst_acc);
296 }
297 
298 
filter_preset_daterange_future_enable(gint range)299 gboolean filter_preset_daterange_future_enable(gint range)
300 {
301 	switch( range )
302 	{
303 		case FLT_RANGE_THISMONTH:
304 		case FLT_RANGE_THISQUARTER:
305 		case FLT_RANGE_THISYEAR:
306 		case FLT_RANGE_LAST30DAYS:
307 		case FLT_RANGE_LAST60DAYS:
308 		case FLT_RANGE_LAST90DAYS:
309 		case FLT_RANGE_LAST12MONTHS:
310 			return TRUE;
311 			break;
312 	}
313 
314 	return FALSE;
315 }
316 
317 
filter_preset_daterange_add_futuregap(Filter * filter,gint nbdays)318 void filter_preset_daterange_add_futuregap(Filter *filter, gint nbdays)
319 {
320 
321 	DB( g_print("\n[filter] range add future gap\n") );
322 
323 	filter->nbdaysfuture = 0;
324 	//#1840998 if future active and visible: we should always maxdate to today + nbdays
325 	if( filter_preset_daterange_future_enable(filter->range) )
326 	{
327 	guint32 jtmpmax = GLOBALS->today + nbdays;
328 
329 		if( filter->maxdate < jtmpmax )
330 			filter->nbdaysfuture = jtmpmax - filter->maxdate;
331 		else
332 			filter->nbdaysfuture = nbdays;
333 	}
334 }
335 
336 
filter_preset_daterange_set(Filter * flt,gint range,guint32 kacc)337 void filter_preset_daterange_set(Filter *flt, gint range, guint32 kacc)
338 {
339 GDate *tmpdate;
340 guint32 jtoday, jfiscal;
341 guint16 month, year, yfiscal, qnum;
342 
343 	DB( g_print("\n[filter] daterange set %p %d\n", flt, range) );
344 
345 	flt->range = range;
346 
347 	jtoday = GLOBALS->today;
348 
349 	tmpdate  = g_date_new_julian(jtoday);
350 
351 	month = g_date_get_month(tmpdate);
352 	year  = g_date_get_year(tmpdate);
353 	DB( hb_print_date(jtoday , "today ") );
354 
355 	g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
356 	jfiscal = g_date_get_julian(tmpdate);
357 	DB( hb_print_date(jfiscal, "fiscal") );
358 
359 	yfiscal = (jtoday >= jfiscal) ? year : year-1;
360 	qnum = 0;
361 
362 	if( range == FLT_RANGE_THISQUARTER || range == FLT_RANGE_LASTQUARTER )
363 	{
364 		g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
365 		while( (qnum < 5) && (g_date_get_julian(tmpdate) < jtoday) )
366 		{
367 			qnum++;
368 			g_date_add_months (tmpdate, 3);
369 		}
370 		DB( g_print(" qnum: %d\n", qnum ) );
371 	}
372 
373 	switch( range )
374 	{
375 		case FLT_RANGE_THISMONTH:
376 		case FLT_RANGE_LASTMONTH:
377 			g_date_set_dmy(tmpdate, 1, month, year);
378 			if( range == FLT_RANGE_LASTMONTH )
379 				g_date_subtract_months(tmpdate, 1);
380 			flt->mindate = g_date_get_julian(tmpdate);
381 			month = g_date_get_month(tmpdate);
382 			year = g_date_get_year(tmpdate);
383 			g_date_add_days(tmpdate, g_date_get_days_in_month(month, year));
384 			flt->maxdate = g_date_get_julian(tmpdate) - 1;
385 			break;
386 
387 		case FLT_RANGE_THISQUARTER:
388 		case FLT_RANGE_LASTQUARTER:
389 			g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
390 			if( range == FLT_RANGE_LASTQUARTER )
391 				g_date_subtract_months(tmpdate, 3);
392 			g_date_add_months(tmpdate, 3 * (qnum-1) );
393 			flt->mindate = g_date_get_julian(tmpdate);
394 			g_date_add_months(tmpdate, 3);
395 			flt->maxdate = g_date_get_julian(tmpdate) - 1;
396 			break;
397 
398 		case FLT_RANGE_THISYEAR:
399 		case FLT_RANGE_LASTYEAR:
400 			g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
401 			if( range == FLT_RANGE_LASTYEAR )
402 				g_date_subtract_years(tmpdate, 1);
403 			flt->mindate = g_date_get_julian(tmpdate);
404 			g_date_add_years (tmpdate, 1);
405 			flt->maxdate = g_date_get_julian(tmpdate) - 1;
406 			break;
407 
408 		case FLT_RANGE_LAST30DAYS:
409 			flt->mindate = jtoday - 30;
410 			flt->maxdate = jtoday;
411 			break;
412 
413 		case FLT_RANGE_LAST60DAYS:
414 			flt->mindate = jtoday - 60;
415 			flt->maxdate = jtoday;
416 			break;
417 
418 		case FLT_RANGE_LAST90DAYS:
419 			flt->mindate = jtoday - 90;
420 			flt->maxdate = jtoday;
421 			break;
422 
423 		case FLT_RANGE_LAST12MONTHS:
424 			g_date_set_julian (tmpdate, jtoday);
425 			g_date_subtract_months(tmpdate, 12);
426 			flt->mindate = g_date_get_julian(tmpdate);
427 			flt->maxdate = jtoday;
428 			break;
429 
430 		// case FLT_RANGE_OTHER:
431 			//nothing to do
432 
433 		case FLT_RANGE_ALLDATE:
434 			filter_set_date_bounds(flt, kacc);
435 			break;
436 	}
437 	g_date_free(tmpdate);
438 }
439 
440 
filter_preset_type_set(Filter * flt,gint type)441 void filter_preset_type_set(Filter *flt, gint type)
442 {
443 
444 	DB( g_print("\n[filter] preset type set\n") );
445 
446 	/* any type */
447 	flt->option[FLT_GRP_TYPE] = 1;
448 	flt->type = type;
449 	/*flt->option[FLT_GRP_AMOUNT] = 0;
450 	flt->minamount = G_MINDOUBLE;
451 	flt->maxamount = G_MINDOUBLE;
452 
453 	switch( type )
454 	{
455 		case FLT_TYPE_EXPENSE:
456 			flt->option[FLT_GRP_AMOUNT] = 1;
457 			flt->minamount = -G_MAXDOUBLE;
458 			flt->maxamount = G_MINDOUBLE;
459 			break;
460 
461 		case FLT_TYPE_INCOME:
462 			flt->option[FLT_GRP_AMOUNT] = 1;
463 			flt->minamount = G_MINDOUBLE;
464 			flt->maxamount = G_MAXDOUBLE;
465 			break;
466 	}*/
467 
468 }
469 
470 
filter_preset_status_set(Filter * flt,gint status)471 void filter_preset_status_set(Filter *flt, gint status)
472 {
473 
474 	DB( g_print("\n[filter] preset status set\n") );
475 
476 	/* any status */
477 	flt->option[FLT_GRP_TYPE] = 0;
478 	flt->option[FLT_GRP_STATUS] = 0;
479 	flt->option[FLT_GRP_CATEGORY] = 0;
480 
481 	flt->type   = FLT_TYPE_ALL;
482 	flt->status = FLT_STATUS_ALL;
483 	//#1602835 fautly set
484 	//flt->forceadd = TRUE;
485 	//flt->forcechg = TRUE;
486 
487 	//#1860356 keep widget active_id
488 	//#1873324 register status quick filter do not reset
489 	//note: status revert to UNRECONCILED here is normal if PREFS->hidereconciled=TRUE
490 	//flt->rawstatus = status;
491 
492 	switch( status )
493 	{
494 		case FLT_STATUS_UNCATEGORIZED:
495 			flt->option[FLT_GRP_TYPE] = 2;
496 			flt->type = FLT_TYPE_INTXFER;
497 			flt->option[FLT_GRP_CATEGORY] = 1;
498 			filter_status_cat_clear_except(flt, 0);
499 			break;
500 
501 		case FLT_STATUS_UNRECONCILED:
502 			flt->option[FLT_GRP_STATUS] = 2;
503 			flt->status = FLT_STATUS_RECONCILED;
504 			break;
505 
506 		case FLT_STATUS_UNCLEARED:
507 			flt->option[FLT_GRP_STATUS] = 2;
508 			flt->status = FLT_STATUS_CLEARED;
509 			break;
510 
511 		case FLT_STATUS_RECONCILED:
512 			flt->option[FLT_GRP_STATUS] = 1;
513 			flt->status = FLT_STATUS_RECONCILED;
514 			break;
515 
516 		case FLT_STATUS_CLEARED:
517 			flt->option[FLT_GRP_STATUS] = 1;
518 			flt->status = FLT_STATUS_CLEARED;
519 			break;
520 
521 	}
522 }
523 
524 
filter_daterange_text_get(Filter * flt)525 gchar *filter_daterange_text_get(Filter *flt)
526 {
527 gchar buffer1[128];
528 gchar buffer2[128];
529 gchar buffer3[128];
530 GDate *date;
531 gchar *retval = NULL;
532 
533 	DB( g_print("\n[filter] daterange text get\n") );
534 
535 	date = g_date_new_julian(flt->mindate);
536 	g_date_strftime (buffer1, 128-1, PREFS->date_format, date);
537 
538 	g_date_set_julian(date, flt->maxdate);
539 	g_date_strftime (buffer2, 128-1, PREFS->date_format, date);
540 
541 	if( flt->nbdaysfuture > 0 )
542 	{
543 		g_date_set_julian(date, flt->maxdate + flt->nbdaysfuture);
544 		g_date_strftime (buffer3, 128-1, PREFS->date_format, date);
545 		retval = g_strdup_printf("%s — <s>%s</s> %s", buffer1, buffer2, buffer3);
546 	}
547 	else
548 		retval = g_strdup_printf("%s — %s", buffer1, buffer2);
549 
550 	g_date_free(date);
551 
552 	//return g_strdup_printf(_("<i>from</i> %s <i>to</i> %s — "), buffer1, buffer2);
553 	return retval;
554 }
555 
556 
557 /* = = = = = = = = = = = = = = = = */
558 
559 
560 /* used for quicksearch text into transaction */
filter_tpl_search_match(gchar * needle,Archive * arc)561 gboolean filter_tpl_search_match(gchar *needle, Archive *arc)
562 {
563 gboolean retval = FALSE;
564 Payee *payitem;
565 
566 	DB( g_print("\n[filter] tpl search match\n") );
567 
568 	//#1668036 always try match on txn memo first
569 	if(arc->memo)
570 	{
571 		retval |= hb_string_utf8_strstr(arc->memo, needle, FALSE);
572 	}
573 	if(retval) goto end;
574 
575 	//#1509485
576 	if(arc->flags & OF_SPLIT)
577 	{
578 	guint count, i;
579 	Split *split;
580 
581 		count = da_splits_length(arc->splits);
582 		for(i=0;i<count;i++)
583 		{
584 		gint tmpinsert = 0;
585 
586 			split = da_splits_get(arc->splits, i);
587 			tmpinsert = hb_string_utf8_strstr(split->memo, needle, FALSE);
588 			retval |= tmpinsert;
589 			if( tmpinsert )
590 				break;
591 		}
592 	}
593 	if(retval) goto end;
594 
595 	if(arc->info)
596 	{
597 		retval |= hb_string_utf8_strstr(arc->info, needle, FALSE);
598 	}
599 	if(retval) goto end;
600 
601 	payitem = da_pay_get(arc->kpay);
602 	if(payitem)
603 	{
604 		retval |= hb_string_utf8_strstr(payitem->name, needle, FALSE);
605 	}
606 	if(retval) goto end;
607 
608 	//#1741339 add quicksearch for amount
609 	{
610 	gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
611 
612 		hb_strfnum(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, arc->amount, GLOBALS->kcur, FALSE);
613 		retval |= hb_string_utf8_strstr(formatd_buf, needle, FALSE);
614 	}
615 
616 
617 end:
618 	return retval;
619 }
620 
621 
622 /* used for quicksearch text into transaction */
filter_txn_search_match(gchar * needle,Transaction * txn,gint flags)623 gboolean filter_txn_search_match(gchar *needle, Transaction *txn, gint flags)
624 {
625 gboolean retval = FALSE;
626 Payee *payitem;
627 Category *catitem;
628 gchar *tags;
629 
630 	DB( g_print("\n[filter] tnx search match\n") );
631 
632 	if(flags & FLT_QSEARCH_MEMO)
633 	{
634 		//#1668036 always try match on txn memo first
635 		if(txn->memo)
636 		{
637 			retval |= hb_string_utf8_strstr(txn->memo, needle, FALSE);
638 		}
639 		if(retval) goto end;
640 
641 		//#1509485
642 		if(txn->flags & OF_SPLIT)
643 		{
644 		guint count, i;
645 		Split *split;
646 
647 			count = da_splits_length(txn->splits);
648 			for(i=0;i<count;i++)
649 			{
650 			gint tmpinsert = 0;
651 
652 				split = da_splits_get(txn->splits, i);
653 				tmpinsert = hb_string_utf8_strstr(split->memo, needle, FALSE);
654 				retval |= tmpinsert;
655 				if( tmpinsert )
656 					break;
657 			}
658 		}
659 		if(retval) goto end;
660 	}
661 
662 	if(flags & FLT_QSEARCH_INFO)
663 	{
664 		if(txn->info)
665 		{
666 			retval |= hb_string_utf8_strstr(txn->info, needle, FALSE);
667 		}
668 		if(retval) goto end;
669 	}
670 
671 	if(flags & FLT_QSEARCH_PAYEE)
672 	{
673 		payitem = da_pay_get(txn->kpay);
674 		if(payitem)
675 		{
676 			retval |= hb_string_utf8_strstr(payitem->name, needle, FALSE);
677 		}
678 		if(retval) goto end;
679 	}
680 
681 	if(flags & FLT_QSEARCH_CATEGORY)
682 	{
683 		//#1509485
684 		if(txn->flags & OF_SPLIT)
685 		{
686 		guint count, i;
687 		Split *split;
688 
689 			count = da_splits_length(txn->splits);
690 			for(i=0;i<count;i++)
691 			{
692 			gint tmpinsert = 0;
693 
694 				split = da_splits_get(txn->splits, i);
695 				catitem = da_cat_get(split->kcat);
696 				if(catitem)
697 				{
698 					tmpinsert = hb_string_utf8_strstr(catitem->fullname, needle, FALSE);
699 					retval |= tmpinsert;
700 				}
701 
702 				if( tmpinsert )
703 					break;
704 			}
705 		}
706 		else
707 		{
708 			catitem = da_cat_get(txn->kcat);
709 			if(catitem)
710 			{
711 				retval |= hb_string_utf8_strstr(catitem->fullname, needle, FALSE);
712 			}
713 		}
714 		if(retval) goto end;
715 	}
716 
717 	if(flags & FLT_QSEARCH_TAGS)
718 	{
719 		tags = tags_tostring(txn->tags);
720 		if(tags)
721 		{
722 			retval |= hb_string_utf8_strstr(tags, needle, FALSE);
723 		}
724 		g_free(tags);
725 		if(retval) goto end;
726 	}
727 
728 	//#1741339 add quicksearch for amount
729 	if(flags & FLT_QSEARCH_AMOUNT)
730 	{
731 	gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
732 
733 		DB( g_print(" needle='%s' txnamt='%s'\n", needle, formatd_buf) );
734 
735 		hb_strfnum(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, txn->amount, txn->kcur, FALSE);
736 		retval |= hb_string_utf8_strstr(formatd_buf, needle, FALSE);
737 	}
738 
739 
740 end:
741 	return retval;
742 }
743 
744 
filter_txn_match(Filter * flt,Transaction * txn)745 gint filter_txn_match(Filter *flt, Transaction *txn)
746 {
747 Account *accitem;
748 Payee *payitem;
749 Category *catitem;
750 gint insert;
751 
752 	//DB( g_print("\n[filter] txn match\n") );
753 
754 	insert = 1;
755 
756 /*** start filtering ***/
757 
758 /* date */
759 	if(flt->option[FLT_GRP_DATE]) {
760 		insert = ( (txn->date >= flt->mindate) && (txn->date <= (flt->maxdate + flt->nbdaysfuture) ) ) ? 1 : 0;
761 		if(flt->option[FLT_GRP_DATE] == 2) insert ^= 1;
762 	}
763 	if(!insert) goto end;
764 
765 /* account */
766 	if(flt->option[FLT_GRP_ACCOUNT]) {
767 		accitem = da_acc_get(txn->kacc);
768 		if(accitem)
769 		{
770 			insert = ( accitem->flt_select == TRUE ) ? 1 : 0;
771 			if(flt->option[FLT_GRP_ACCOUNT] == 2) insert ^= 1;
772 		}
773 	}
774 	if(!insert) goto end;
775 
776 /* payee */
777 	if(flt->option[FLT_GRP_PAYEE]) {
778 		payitem = da_pay_get(txn->kpay);
779 		if(payitem)
780 		{
781 			insert = ( payitem->flt_select == TRUE ) ? 1 : 0;
782 			if(flt->option[FLT_GRP_PAYEE] == 2) insert ^= 1;
783 		}
784 	}
785 	if(!insert) goto end;
786 
787 /* category */
788 	if(flt->option[FLT_GRP_CATEGORY]) {
789 		if(txn->flags & OF_SPLIT)
790 		{
791 		guint count, i;
792 		Split *split;
793 
794 			insert = 0;	 //fix: 1151259
795 			count = da_splits_length(txn->splits);
796 			for(i=0;i<count;i++)
797 			{
798 			gint tmpinsert = 0;
799 
800 				split = da_splits_get(txn->splits, i);
801 				catitem = da_cat_get(split->kcat);
802 				if(catitem)
803 				{
804 					tmpinsert = ( catitem->flt_select == TRUE ) ? 1 : 0;
805 					if(flt->option[FLT_GRP_CATEGORY] == 2) tmpinsert ^= 1;
806 				}
807 				insert |= tmpinsert;
808 			}
809 		}
810 		else
811 		{
812 			catitem = da_cat_get(txn->kcat);
813 			if(catitem)
814 			{
815 				insert = ( catitem->flt_select == TRUE ) ? 1 : 0;
816 				if(flt->option[FLT_GRP_CATEGORY] == 2) insert ^= 1;
817 			}
818 		}
819 	}
820 	if(!insert) goto end;
821 
822 /* type */
823 	if(flt->option[FLT_GRP_TYPE]) {
824 		switch( flt->type )
825 		{
826 			case FLT_TYPE_EXPENSE:
827 				insert = !(txn->flags & (OF_INCOME|OF_INTXFER)) ? 1 : 0;
828 				break;
829 			case FLT_TYPE_INCOME:
830 				insert = (txn->flags & (OF_INCOME)) ? 1 : 0;
831 				break;
832 			case FLT_TYPE_INTXFER:
833 				insert = (txn->flags & (OF_INTXFER)) ? 1 : 0;
834 				break;
835 			case FLT_TYPE_ALL:
836 				insert = 1;
837 				break;
838 		}
839 		if(flt->option[FLT_GRP_TYPE] == 2) insert ^= 1;
840 	}
841 	if(!insert) goto end;
842 
843 /* status */
844 	if(flt->option[FLT_GRP_STATUS]) {
845 
846 		switch( flt->status )
847 		{
848 			case FLT_STATUS_CLEARED:
849 				insert = (txn->status == TXN_STATUS_CLEARED) ? 1 : 0;
850 				break;
851 			case FLT_STATUS_RECONCILED:
852 				insert = (txn->status == TXN_STATUS_RECONCILED) ? 1 : 0;
853 				break;
854 			case FLT_STATUS_ALL:
855 				insert = 1;
856 				break;
857 		}
858 		if(flt->option[FLT_GRP_STATUS] == 2)
859 		{
860 			//#1853531 fix for more exact uncleared/unreconciled
861 			switch( flt->status )
862 			{
863 			case FLT_STATUS_CLEARED:	//uncleared
864 				insert = (txn->status == TXN_STATUS_NONE) ? 1 : 0;
865 				break;
866 			case FLT_STATUS_RECONCILED:	//unreconciled
867 				insert = (txn->status == TXN_STATUS_NONE) || (txn->status == TXN_STATUS_CLEARED) ? 1 : 0;
868 				break;
869 			default:
870 				insert ^= 1;
871 				break;
872 			}
873 		}
874 	}
875 	if(!insert) goto end;
876 
877 /* paymode */
878 	if(flt->option[FLT_GRP_PAYMODE]) {
879 		insert = ( flt->paymode[txn->paymode] == TRUE) ? 1 : 0;
880 		if(flt->option[FLT_GRP_PAYMODE] == 2) insert ^= 1;
881 	}
882 	if(!insert) goto end;
883 
884 /* amount */
885 	if(flt->option[FLT_GRP_AMOUNT]) {
886 		insert = ( (txn->amount >= flt->minamount) && (txn->amount <= flt->maxamount) ) ? 1 : 0;
887 
888 		if(flt->option[FLT_GRP_AMOUNT] == 2) insert ^= 1;
889 	}
890 	if(!insert) goto end;
891 
892 /* info/memo/tag */
893 	if(flt->option[FLT_GRP_TEXT])
894 	{
895 	gchar *tags;
896 	gint insert1, insert2, insert3;
897 
898 		insert1 = insert2 = insert3 = 0;
899 		if(flt->info)
900 		{
901 			if(txn->info)
902 			{
903 				insert1 = hb_string_utf8_strstr(txn->info, flt->info, flt->exact);
904 			}
905 		}
906 		else
907 			insert1 = 1;
908 
909 		if(flt->memo)
910 		{
911 			//#1668036 always try match on txn memo first
912 			if(txn->memo)
913 			{
914 				insert2 = hb_string_utf8_strstr(txn->memo, flt->memo, flt->exact);
915 			}
916 
917 			if( (insert2 == 0) && (txn->flags & OF_SPLIT) )
918 			{
919 			guint count, i;
920 			Split *split;
921 
922 				count = da_splits_length(txn->splits);
923 				for(i=0;i<count;i++)
924 				{
925 				gint tmpinsert = 0;
926 
927 					split = da_splits_get(txn->splits, i);
928 					tmpinsert = hb_string_utf8_strstr(split->memo, flt->memo, flt->exact);
929 					insert2 |= tmpinsert;
930 					if( tmpinsert )
931 						break;
932 				}
933 			}
934 		}
935 		else
936 			insert2 = 1;
937 
938 		if(flt->tag)
939 		{
940 			tags = tags_tostring(txn->tags);
941 			if(tags)
942 			{
943 				insert3 = hb_string_utf8_strstr(tags, flt->tag, flt->exact);
944 			}
945 			g_free(tags);
946 		}
947 		else
948 			insert3 = 1;
949 
950 		insert = insert1 && insert2 && insert3 ? 1 : 0;
951 
952 		if(flt->option[FLT_GRP_TEXT] == 2) insert ^= 1;
953 
954 	}
955 	//if(!insert) goto end;
956 
957 end:
958 	/* force display */
959 	if(!insert)
960 	{
961 		if( ((flt->forceadd == TRUE) && (txn->flags & OF_ADDED))
962 		 || ((flt->forcechg == TRUE) && (txn->flags & OF_CHANGED))
963 		 || ((flt->forceremind == TRUE) && (txn->status == TXN_STATUS_REMIND))
964 		 || ((flt->forcevoid == TRUE) && (txn->status == TXN_STATUS_VOID))
965 		  )
966 		insert = 1;
967 	}
968 
969 //	DB( g_print(" %d :: %d :: %d\n", flt->mindate, txn->date, flt->maxdate) );
970 //	DB( g_print(" [%d] %s => %d (%d)\n", txn->account, txn->memo, insert, count) );
971 	return(insert);
972 }
973 
974