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