]> Dogcows Code - chaz/homebank/blob - src/hb-filter.c
import homebank-5.1.2
[chaz/homebank] / src / hb-filter.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2016 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
41 /* = = = = = = = = = = = = = = = = = = = = */
42 /* Filter */
43
44 Filter *da_filter_malloc(void)
45 {
46 return g_malloc0(sizeof(Filter));
47 }
48
49 void da_filter_free(Filter *flt)
50 {
51 if(flt != NULL)
52 {
53 g_free(flt->wording);
54 g_free(flt->info);
55 g_free(flt->tag);
56 g_free(flt);
57 }
58 }
59
60 /* = = = = = = = = = = = = = = = = = = = = */
61
62 gchar *filter_daterange_text_get(Filter *flt)
63 {
64 gchar buffer1[128];
65 gchar buffer2[128];
66 GDate *date;
67
68 date = g_date_new_julian(flt->mindate);
69 g_date_strftime (buffer1, 128-1, PREFS->date_format, date);
70 g_date_set_julian(date, flt->maxdate);
71 g_date_strftime (buffer2, 128-1, PREFS->date_format, date);
72 g_date_free(date);
73
74 return g_strdup_printf(_("<i>from</i> %s <i>to</i> %s"), buffer1, buffer2);
75 }
76
77
78
79 static void filter_default_date_set(Filter *flt)
80 {
81 flt->mindate = HB_MINDATE;
82 flt->maxdate = HB_MAXDATE;
83 }
84
85
86 static void filter_clear(Filter *flt)
87 {
88 guint i;
89
90 for(i=0;i<FILTER_MAX;i++)
91 {
92 flt->option[i] = 0;
93 }
94
95 g_free(flt->info);
96 g_free(flt->wording);
97 g_free(flt->tag);
98 flt->info = NULL;
99 flt->wording = NULL;
100 flt->tag = NULL;
101
102 flt->last_tab = 0;
103 }
104
105
106 void filter_default_all_set(Filter *flt)
107 {
108 gint i;
109
110 DB( g_print("(filter) reset %p\n", flt) );
111
112 filter_clear(flt);
113
114 flt->range = FLT_RANGE_LAST12MONTHS;
115 flt->type = FLT_TYPE_ALL;
116 flt->status = FLT_STATUS_ALL;
117
118 flt->forceremind = PREFS->showremind;
119
120 flt->option[FILTER_DATE] = 1;
121 filter_default_date_set(flt);
122
123 for(i=0;i<NUM_PAYMODE_MAX;i++)
124 flt->paymode[i] = TRUE;
125
126 filter_preset_daterange_set(flt, flt->range, 0);
127
128 }
129
130
131 static void filter_set_date_bounds(Filter *flt, guint32 kacc)
132 {
133 GList *lst_acc, *lnk_acc;
134 GList *lnk_txn;
135
136 DB( g_print("(filter) set date bounds %p\n", flt) );
137
138 flt->mindate = 0;
139 flt->maxdate = 0;
140
141 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
142 lnk_acc = g_list_first(lst_acc);
143 while (lnk_acc != NULL)
144 {
145 Account *acc = lnk_acc->data;
146
147 if( !(acc->flags & AF_CLOSED) )
148 {
149 Transaction *txn;
150
151 DB( g_print(" - do '%s'\n", acc->name) );
152
153 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
154 if(lnk_txn) {
155 txn = lnk_txn->data;
156 if( (kacc == 0) || (txn->kacc == kacc) )
157 {
158 if( flt->mindate == 0 )
159 flt->mindate = txn->date;
160 else
161 flt->mindate = MIN(flt->mindate, txn->date);
162 }
163 }
164
165 lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
166 if(lnk_txn) {
167 txn = lnk_txn->data;
168 if( (kacc == 0) || (txn->kacc == kacc) )
169 {
170 if( flt->maxdate == 0 )
171 flt->maxdate = txn->date;
172 else
173 flt->maxdate = MAX(flt->maxdate, txn->date);
174 }
175 }
176
177 }
178 lnk_acc = g_list_next(lnk_acc);
179 }
180
181 if( flt->mindate == 0 )
182 flt->mindate = HB_MINDATE;
183
184 if( flt->maxdate == 0 )
185 flt->maxdate = HB_MAXDATE;
186
187 g_list_free(lst_acc);
188 }
189
190
191 void filter_preset_daterange_add_futuregap(Filter *filter, gint nbdays)
192 {
193
194 if( nbdays <= 0 )
195 return;
196
197 switch( filter->range )
198 {
199 case FLT_RANGE_THISMONTH:
200 case FLT_RANGE_THISQUARTER:
201 case FLT_RANGE_THISYEAR:
202 case FLT_RANGE_LAST30DAYS:
203 case FLT_RANGE_LAST60DAYS:
204 case FLT_RANGE_LAST90DAYS:
205 case FLT_RANGE_LAST12MONTHS:
206 filter->maxdate += nbdays;
207 break;
208 }
209
210 }
211
212
213 void filter_preset_daterange_set(Filter *flt, gint range, guint32 kacc)
214 {
215 GDate *date;
216 guint32 refjuliandate, month, year, qnum;
217
218 DB( g_print("(filter) daterange set %p %d\n", flt, range) );
219
220 //filter_default_date_set(flt);
221 filter_set_date_bounds(flt, kacc);
222
223 flt->range = range;
224
225 // by default refjuliandate is today
226 // but we adjust if to max transaction date found
227 // removed for 5.0.4
228 refjuliandate = GLOBALS->today;
229 /*if(flt->maxdate < refjuliandate)
230 refjuliandate = flt->maxdate;*/
231
232 date = g_date_new_julian(refjuliandate);
233 month = g_date_get_month(date);
234 year = g_date_get_year(date);
235 qnum = ((month - 1) / 3) + 1;
236
237 DB( g_print("m=%d, y=%d, qnum=%d\n", month, year, qnum) );
238
239 switch( range )
240 {
241 case FLT_RANGE_THISMONTH:
242 g_date_set_day(date, 1);
243 flt->mindate = g_date_get_julian(date);
244 g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
245 flt->maxdate = g_date_get_julian(date);
246 break;
247
248 case FLT_RANGE_LASTMONTH:
249 g_date_set_day(date, 1);
250 g_date_subtract_months(date, 1);
251 flt->mindate = g_date_get_julian(date);
252 month = g_date_get_month(date);
253 year = g_date_get_year(date);
254 g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
255 flt->maxdate = g_date_get_julian(date);
256 break;
257
258 case FLT_RANGE_THISQUARTER:
259 g_date_set_day(date, 1);
260 g_date_set_month(date, (qnum-1)*3+1);
261 flt->mindate = g_date_get_julian(date);
262 g_date_add_months(date, 3);
263 g_date_subtract_days(date, 1);
264 flt->maxdate = g_date_get_julian(date);
265 break;
266
267 case FLT_RANGE_LASTQUARTER:
268 g_date_set_day(date, 1);
269 g_date_set_month(date, (qnum-1)*3+1);
270 g_date_subtract_months(date, 3);
271 flt->mindate = g_date_get_julian(date);
272 g_date_add_months(date, 3);
273 g_date_subtract_days(date, 1);
274 flt->maxdate = g_date_get_julian(date);
275 break;
276
277 case FLT_RANGE_THISYEAR:
278 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
279 if( refjuliandate >= g_date_get_julian (date))
280 {
281 flt->mindate = g_date_get_julian(date);
282 }
283 else
284 {
285 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-1);
286 flt->mindate = g_date_get_julian(date);
287 }
288 g_date_add_years (date, 1);
289 g_date_subtract_days (date, 1);
290 flt->maxdate = g_date_get_julian(date);
291 break;
292
293 case FLT_RANGE_LASTYEAR:
294 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
295 if( refjuliandate >= g_date_get_julian (date))
296 {
297 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-1);
298 flt->mindate = g_date_get_julian(date);
299 }
300 else
301 {
302 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-2);
303 flt->mindate = g_date_get_julian(date);
304 }
305 g_date_add_years (date, 1);
306 g_date_subtract_days (date, 1);
307 flt->maxdate = g_date_get_julian(date);
308 break;
309
310 case FLT_RANGE_LAST30DAYS:
311 flt->mindate = refjuliandate - 30;
312 flt->maxdate = refjuliandate;
313 break;
314
315 case FLT_RANGE_LAST60DAYS:
316 flt->mindate = refjuliandate - 60;
317 flt->maxdate = refjuliandate;
318 break;
319
320 case FLT_RANGE_LAST90DAYS:
321 flt->mindate = refjuliandate - 90;
322 flt->maxdate = refjuliandate;
323 break;
324
325 case FLT_RANGE_LAST12MONTHS:
326 g_date_subtract_months(date, 12);
327 flt->mindate = g_date_get_julian(date);
328 flt->maxdate = refjuliandate;
329 break;
330
331 // case FLT_RANGE_OTHER:
332
333 // case FLT_RANGE_ALLDATE:
334
335
336 }
337 g_date_free(date);
338
339 }
340
341 void filter_preset_type_set(Filter *flt, gint type)
342 {
343
344 /* any type */
345 flt->type = type;
346 flt->option[FILTER_AMOUNT] = 0;
347 flt->minamount = G_MINDOUBLE;
348 flt->maxamount = G_MINDOUBLE;
349
350 switch( type )
351 {
352 case FLT_TYPE_EXPENSE:
353 flt->option[FILTER_AMOUNT] = 1;
354 flt->minamount = -G_MAXDOUBLE;
355 flt->maxamount = G_MINDOUBLE;
356 break;
357
358 case FLT_TYPE_INCOME:
359 flt->option[FILTER_AMOUNT] = 1;
360 flt->minamount = G_MINDOUBLE;
361 flt->maxamount = G_MAXDOUBLE;
362 break;
363 }
364
365 }
366
367
368 void filter_preset_status_set(Filter *flt, gint status)
369 {
370 Category *catitem;
371 GList *lcat, *list;
372
373 /* any status */
374 flt->status = status;
375 flt->option[FILTER_STATUS] = 0;
376 flt->reconciled = TRUE;
377 flt->cleared = TRUE;
378 //#1602835 fautly set
379 //flt->forceadd = TRUE;
380 //flt->forcechg = TRUE;
381
382 flt->option[FILTER_CATEGORY] = 0;
383 lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
384 while (list != NULL)
385 {
386 catitem = list->data;
387 catitem->filter = FALSE;
388 list = g_list_next(list);
389 }
390 g_list_free(lcat);
391
392 switch( status )
393 {
394 case FLT_STATUS_UNCATEGORIZED:
395 flt->option[FILTER_CATEGORY] = 1;
396 catitem = da_cat_get(0); // no category
397 catitem->filter = TRUE;
398 break;
399
400 case FLT_STATUS_UNRECONCILED:
401 flt->option[FILTER_STATUS] = 2;
402 flt->reconciled = TRUE;
403 flt->cleared = FALSE;
404 break;
405
406 case FLT_STATUS_UNCLEARED:
407 flt->option[FILTER_STATUS] = 2;
408 flt->reconciled = FALSE;
409 flt->cleared = TRUE;
410 break;
411
412 case FLT_STATUS_RECONCILED:
413 flt->option[FILTER_STATUS] = 1;
414 flt->reconciled = TRUE;
415 flt->cleared = FALSE;
416 break;
417
418 case FLT_STATUS_CLEARED:
419 flt->option[FILTER_STATUS] = 1;
420 flt->reconciled = FALSE;
421 flt->cleared = TRUE;
422 break;
423
424 }
425 }
426
427
428 static gint filter_text_compare(gchar *txntext, gchar *searchtext, gboolean exact)
429 {
430 gint retval = 0;
431
432 if( exact )
433 {
434 if( g_strstr_len(txntext, -1, searchtext) != NULL )
435 {
436 DB( g_print(" found case '%s'\n", searchtext) );
437 retval = 1;
438 }
439 }
440 else
441 {
442 gchar *word = g_utf8_casefold(txntext, -1);
443 gchar *needle = g_utf8_casefold(searchtext, -1);
444
445 if( g_strrstr(word, needle) != NULL )
446 {
447 DB( g_print(" found nocase '%s'\n", needle) );
448 retval = 1;
449 }
450
451 g_free(word);
452 g_free(needle);
453 }
454 return retval;
455 }
456
457
458 /* used for quicksearch text into transaction */
459 gboolean filter_txn_search_match(gchar *needle, Transaction *txn, gint flags)
460 {
461 gboolean retval = FALSE;
462 Payee *payitem;
463 Category *catitem;
464 gchar *tags;
465
466 if(flags & FLT_QSEARCH_MEMO)
467 {
468 //#1509485
469 if(txn->flags & OF_SPLIT)
470 {
471 guint count, i;
472 Split *split;
473
474 count = da_splits_count(txn->splits);
475 for(i=0;i<count;i++)
476 {
477 gint tmpinsert = 0;
478
479 split = txn->splits[i];
480 tmpinsert = filter_text_compare(split->memo, needle, FALSE);
481 retval |= tmpinsert;
482 if( tmpinsert )
483 break;
484 }
485 }
486 else
487 {
488 if(txn->wording)
489 {
490 retval |= filter_text_compare(txn->wording, needle, FALSE);
491 }
492 }
493 if(retval) goto end;
494 }
495
496 if(flags & FLT_QSEARCH_INFO)
497 {
498 if(txn->info)
499 {
500 retval |= filter_text_compare(txn->info, needle, FALSE);
501 }
502 if(retval) goto end;
503 }
504
505 if(flags & FLT_QSEARCH_PAYEE)
506 {
507 payitem = da_pay_get(txn->kpay);
508 if(payitem)
509 {
510 retval |= filter_text_compare(payitem->name, needle, FALSE);
511 }
512 if(retval) goto end;
513 }
514
515 if(flags & FLT_QSEARCH_CATEGORY)
516 {
517 //#1509485
518 if(txn->flags & OF_SPLIT)
519 {
520 guint count, i;
521 Split *split;
522
523 count = da_splits_count(txn->splits);
524 for(i=0;i<count;i++)
525 {
526 gint tmpinsert = 0;
527
528 split = txn->splits[i];
529 catitem = da_cat_get(split->kcat);
530 if(catitem)
531 {
532 gchar *fullname = da_cat_get_fullname (catitem);
533
534 tmpinsert = filter_text_compare(fullname, needle, FALSE);
535 retval |= tmpinsert;
536 g_free(fullname);
537 }
538
539 if( tmpinsert )
540 break;
541 }
542 }
543 else
544 {
545 catitem = da_cat_get(txn->kcat);
546 if(catitem)
547 {
548 gchar *fullname = da_cat_get_fullname (catitem);
549
550 retval |= filter_text_compare(fullname, needle, FALSE);
551 g_free(fullname);
552 }
553 }
554 if(retval) goto end;
555 }
556
557 if(flags & FLT_QSEARCH_TAGS)
558 {
559 tags = transaction_tags_tostring(txn);
560 if(tags)
561 {
562 retval |= filter_text_compare(tags, needle, FALSE);
563 }
564 g_free(tags);
565 //if(retval) goto end;
566 }
567
568
569 end:
570 return retval;
571 }
572
573
574 gint filter_test(Filter *flt, Transaction *txn)
575 {
576 Account *accitem;
577 Payee *payitem;
578 Category *catitem;
579 gint insert;
580
581 //DB( g_print("(filter) test\n") );
582
583 insert = 1;
584
585 /*** start filtering ***/
586
587 /* force display */
588 if(flt->forceadd == TRUE && (txn->flags & OF_ADDED))
589 goto end;
590
591 if(flt->forcechg == TRUE && (txn->flags & OF_CHANGED))
592 goto end;
593
594 /* force remind if not filter on status */
595 if(flt->forceremind == TRUE && (txn->status == TXN_STATUS_REMIND))
596 goto end;
597
598 /* date */
599 if(flt->option[FILTER_DATE]) {
600 insert = ( (txn->date >= flt->mindate) && (txn->date <= flt->maxdate) ) ? 1 : 0;
601 if(flt->option[FILTER_DATE] == 2) insert ^= 1;
602 }
603 if(!insert) goto end;
604
605 /* account */
606 if(flt->option[FILTER_ACCOUNT]) {
607 accitem = da_acc_get(txn->kacc);
608 if(accitem)
609 {
610 insert = ( accitem->filter == TRUE ) ? 1 : 0;
611 if(flt->option[FILTER_ACCOUNT] == 2) insert ^= 1;
612 }
613 }
614 if(!insert) goto end;
615
616 /* payee */
617 if(flt->option[FILTER_PAYEE]) {
618 payitem = da_pay_get(txn->kpay);
619 if(payitem)
620 {
621 insert = ( payitem->filter == TRUE ) ? 1 : 0;
622 if(flt->option[FILTER_PAYEE] == 2) insert ^= 1;
623 }
624 }
625 if(!insert) goto end;
626
627 /* category */
628 if(flt->option[FILTER_CATEGORY]) {
629 if(txn->flags & OF_SPLIT)
630 {
631 guint count, i;
632 Split *split;
633
634 insert = 0; //fix: 1151259
635 count = da_splits_count(txn->splits);
636 for(i=0;i<count;i++)
637 {
638 gint tmpinsert = 0;
639
640 split = txn->splits[i];
641 catitem = da_cat_get(split->kcat);
642 if(catitem)
643 {
644 tmpinsert = ( catitem->filter == TRUE ) ? 1 : 0;
645 if(flt->option[FILTER_CATEGORY] == 2) tmpinsert ^= 1;
646 }
647 insert |= tmpinsert;
648 }
649 }
650 else
651 {
652 catitem = da_cat_get(txn->kcat);
653 if(catitem)
654 {
655 insert = ( catitem->filter == TRUE ) ? 1 : 0;
656 if(flt->option[FILTER_CATEGORY] == 2) insert ^= 1;
657 }
658 }
659 }
660 if(!insert) goto end;
661
662 /* status */
663 if(flt->option[FILTER_STATUS]) {
664 gint insert1 = 0, insert2 = 0;
665
666 if(flt->reconciled)
667 insert1 = ( txn->status == TXN_STATUS_RECONCILED ) ? 1 : 0;
668 if(flt->cleared)
669 insert2 = ( txn->status == TXN_STATUS_CLEARED ) ? 1 : 0;
670
671 insert = insert1 | insert2;
672 if(flt->option[FILTER_STATUS] == 2) insert ^= 1;
673 }
674 if(!insert) goto end;
675
676 /* paymode */
677 if(flt->option[FILTER_PAYMODE]) {
678 insert = ( flt->paymode[txn->paymode] == TRUE) ? 1 : 0;
679 if(flt->option[FILTER_PAYMODE] == 2) insert ^= 1;
680 }
681 if(!insert) goto end;
682
683 /* amount */
684 if(flt->option[FILTER_AMOUNT]) {
685 insert = ( (txn->amount >= flt->minamount) && (txn->amount <= flt->maxamount) ) ? 1 : 0;
686
687 if(flt->option[FILTER_AMOUNT] == 2) insert ^= 1;
688 }
689 if(!insert) goto end;
690
691 /* info/wording/tag */
692 if(flt->option[FILTER_TEXT])
693 {
694 gchar *tags;
695 gint insert1, insert2, insert3;
696
697 insert1 = insert2 = insert3 = 0;
698 if(flt->info)
699 {
700 if(txn->info)
701 {
702 insert1 = filter_text_compare(txn->info, flt->info, flt->exact);
703 }
704 }
705 else
706 insert1 = 1;
707
708 if(flt->wording)
709 {
710 if(txn->flags & OF_SPLIT)
711 {
712 guint count, i;
713 Split *split;
714
715 count = da_splits_count(txn->splits);
716 for(i=0;i<count;i++)
717 {
718 gint tmpinsert = 0;
719
720 split = txn->splits[i];
721 tmpinsert = filter_text_compare(split->memo, flt->wording, flt->exact);
722 insert2 |= tmpinsert;
723 if( tmpinsert )
724 break;
725 }
726 }
727 else
728 {
729 if(txn->wording)
730 {
731 insert2 = filter_text_compare(txn->wording, flt->wording, flt->exact);
732 }
733 }
734 }
735 else
736 insert2 = 1;
737
738 if(flt->tag)
739 {
740 tags = transaction_tags_tostring(txn);
741 if(tags)
742 {
743 insert3 = filter_text_compare(tags, flt->tag, flt->exact);
744 }
745 g_free(tags);
746 }
747 else
748 insert3 = 1;
749
750 insert = insert1 && insert2 && insert3 ? 1 : 0;
751
752 if(flt->option[FILTER_TEXT] == 2) insert ^= 1;
753
754 }
755 if(!insert) goto end;
756
757 end:
758 // DB( g_print(" %d :: %d :: %d\n", flt->mindate, txn->date, flt->maxdate) );
759 // DB( g_print(" [%d] %s => %d (%d)\n", txn->account, txn->wording, insert, count) );
760 return(insert);
761 }
762
This page took 0.061232 seconds and 4 git commands to generate.