add gitignore
[chaz/homebank] / src / hb-filter.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2014 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 = 693596; //01/01/1900
82 flt->maxdate = 803533; //31/12/2200
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->option[FILTER_DATE] = 1;
119 filter_default_date_set(flt);
120
121 for(i=0;i<NUM_PAYMODE_MAX;i++)
122 flt->paymode[i] = TRUE;
123
124 filter_preset_daterange_set(flt, flt->range);
125
126 }
127
128
129 void filter_preset_daterange_set(Filter *flt, gint range)
130 {
131 GDate *date;
132 GList *list;
133 guint32 refjuliandate, month, year, qnum;
134
135 // any date :: todo : get date of current accout only when account
136 flt->range = range;
137 if(g_list_length(GLOBALS->ope_list) > 0) // get all transaction date bound
138 {
139 GLOBALS->ope_list = da_transaction_sort(GLOBALS->ope_list);
140 list = g_list_first(GLOBALS->ope_list);
141 flt->mindate = ((Transaction *)list->data)->date;
142 list = g_list_last(GLOBALS->ope_list);
143 flt->maxdate = ((Transaction *)list->data)->date;
144 }
145 else
146 filter_default_date_set(flt);
147
148
149 // by default refjuliandate is today
150 // but we adjust if to max transaction date found
151 refjuliandate = GLOBALS->today;
152 if(flt->maxdate < refjuliandate)
153 refjuliandate = flt->maxdate;
154
155 date = g_date_new_julian(refjuliandate);
156 month = g_date_get_month(date);
157 year = g_date_get_year(date);
158 qnum = ((month - 1) / 3) + 1;
159
160 DB( g_print("m=%d, y=%d, qnum=%d\n", month, year, qnum) );
161
162 switch( range )
163 {
164 case FLT_RANGE_THISMONTH:
165 g_date_set_day(date, 1);
166 flt->mindate = g_date_get_julian(date);
167 g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
168 flt->maxdate = g_date_get_julian(date);
169 break;
170
171 case FLT_RANGE_LASTMONTH:
172 g_date_set_day(date, 1);
173 g_date_subtract_months(date, 1);
174 flt->mindate = g_date_get_julian(date);
175 month = g_date_get_month(date);
176 year = g_date_get_year(date);
177 g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
178 flt->maxdate = g_date_get_julian(date);
179 break;
180
181 case FLT_RANGE_THISQUARTER:
182 g_date_set_day(date, 1);
183 g_date_set_month(date, (qnum-1)*3+1);
184 flt->mindate = g_date_get_julian(date);
185 g_date_add_months(date, 3);
186 g_date_subtract_days(date, 1);
187 flt->maxdate = g_date_get_julian(date);
188 break;
189
190 case FLT_RANGE_LASTQUARTER:
191 g_date_set_day(date, 1);
192 g_date_set_month(date, (qnum-1)*3+1);
193 g_date_subtract_months(date, 3);
194 flt->mindate = g_date_get_julian(date);
195 g_date_add_months(date, 3);
196 g_date_subtract_days(date, 1);
197 flt->maxdate = g_date_get_julian(date);
198 break;
199
200 case FLT_RANGE_THISYEAR:
201 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
202 if( refjuliandate >= g_date_get_julian (date))
203 {
204 flt->mindate = g_date_get_julian(date);
205 }
206 else
207 {
208 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-1);
209 flt->mindate = g_date_get_julian(date);
210 }
211 g_date_add_years (date, 1);
212 g_date_subtract_days (date, 1);
213 flt->maxdate = g_date_get_julian(date);
214 break;
215
216 case FLT_RANGE_LASTYEAR:
217 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
218 if( refjuliandate >= g_date_get_julian (date))
219 {
220 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-1);
221 flt->mindate = g_date_get_julian(date);
222 }
223 else
224 {
225 g_date_set_dmy(date, PREFS->fisc_year_day, PREFS->fisc_year_month, year-2);
226 flt->mindate = g_date_get_julian(date);
227 }
228 g_date_add_years (date, 1);
229 g_date_subtract_days (date, 1);
230 flt->maxdate = g_date_get_julian(date);
231 break;
232
233 case FLT_RANGE_LAST30DAYS:
234 flt->mindate = refjuliandate - 30;
235 flt->maxdate = refjuliandate;
236 break;
237
238 case FLT_RANGE_LAST60DAYS:
239 flt->mindate = refjuliandate - 60;
240 flt->maxdate = refjuliandate;
241 break;
242
243 case FLT_RANGE_LAST90DAYS:
244 flt->mindate = refjuliandate - 90;
245 flt->maxdate = refjuliandate;
246 break;
247
248 case FLT_RANGE_LAST12MONTHS:
249 g_date_subtract_months(date, 12);
250 flt->mindate = g_date_get_julian(date);
251 flt->maxdate = refjuliandate;
252 break;
253
254 // case FLT_RANGE_OTHER:
255
256 // case FLT_RANGE_ALLDATE:
257
258
259 }
260 g_date_free(date);
261
262 }
263
264 void filter_preset_type_set(Filter *flt, gint type)
265 {
266
267 /* any type */
268 flt->type = type;
269 flt->option[FILTER_AMOUNT] = 0;
270 flt->minamount = G_MINDOUBLE;
271 flt->maxamount = G_MINDOUBLE;
272
273 switch( type )
274 {
275 case FLT_TYPE_EXPENSE:
276 flt->option[FILTER_AMOUNT] = 1;
277 flt->minamount = -G_MAXDOUBLE;
278 flt->maxamount = G_MINDOUBLE;
279 break;
280
281 case FLT_TYPE_INCOME:
282 flt->option[FILTER_AMOUNT] = 1;
283 flt->minamount = G_MINDOUBLE;
284 flt->maxamount = G_MAXDOUBLE;
285 break;
286 }
287
288 }
289
290
291 void filter_preset_status_set(Filter *flt, gint status)
292 {
293 Category *catitem;
294 GList *lcat, *list;
295
296 /* any status */
297 flt->status = status;
298 flt->option[FILTER_STATUS] = 0;
299 flt->reconciled = TRUE;
300 flt->reminded = TRUE;
301 flt->forceadd = FALSE;
302 flt->forcechg = FALSE;
303
304 flt->option[FILTER_CATEGORY] = 0;
305 lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
306 while (list != NULL)
307 {
308 catitem = list->data;
309 catitem->filter = FALSE;
310 list = g_list_next(list);
311 }
312 g_list_free(lcat);
313
314 switch( status )
315 {
316 case FLT_STATUS_UNCATEGORIZED:
317 flt->option[FILTER_CATEGORY] = 1;
318 catitem = da_cat_get(0); // no category
319 catitem->filter = TRUE;
320 break;
321
322 case FLT_STATUS_UNRECONCILED:
323 flt->option[FILTER_STATUS] = 2;
324 flt->reconciled = TRUE;
325 //#1336882
326 flt->reminded = FALSE;
327 break;
328 }
329
330 }
331
332
333 static gint filter_text_compare(gchar *txntext, gchar *searchtext, gboolean exact)
334 {
335 gint retval = 0;
336
337 if( exact )
338 {
339 if( g_strstr_len(txntext, -1, searchtext) != NULL )
340 {
341 DB( g_print(" found case '%s'\n", searchtext) );
342 retval = 1;
343 }
344 }
345 else
346 {
347 gchar *word = g_utf8_casefold(txntext, -1);
348 gchar *needle = g_utf8_casefold(searchtext, -1);
349
350 if( g_strrstr(word, needle) != NULL )
351 {
352 DB( g_print(" found nocase '%s'\n", needle) );
353 retval = 1;
354 }
355
356 g_free(word);
357 g_free(needle);
358 }
359 return retval;
360 }
361
362
363 /* used for quicksearch text into transaction */
364 gboolean filter_txn_search_match(gchar *needle, Transaction *txn, gint flags)
365 {
366 gboolean retval = FALSE;
367 Payee *payitem;
368 Category *catitem;
369 gchar *tags;
370
371 if(flags & FLT_QSEARCH_MEMO)
372 {
373 if(txn->wording)
374 {
375 retval |= filter_text_compare(txn->wording, needle, FALSE);
376 }
377 if(retval) goto end;
378 }
379
380 if(flags & FLT_QSEARCH_INFO)
381 {
382 if(txn->info)
383 {
384 retval |= filter_text_compare(txn->info, needle, FALSE);
385 }
386 if(retval) goto end;
387 }
388
389 if(flags & FLT_QSEARCH_PAYEE)
390 {
391 payitem = da_pay_get(txn->kpay);
392 if(payitem)
393 {
394 retval |= filter_text_compare(payitem->name, needle, FALSE);
395 }
396 if(retval) goto end;
397 }
398
399 if(flags & FLT_QSEARCH_CATEGORY)
400 {
401 catitem = da_cat_get(txn->kcat);
402 if(catitem)
403 {
404 gchar *fullname = da_cat_get_fullname (catitem);
405
406 retval |= filter_text_compare(fullname, needle, FALSE);
407 g_free(fullname);
408 }
409 if(retval) goto end;
410 }
411
412 if(flags & FLT_QSEARCH_TAGS)
413 {
414 tags = transaction_tags_tostring(txn);
415 if(tags)
416 {
417 retval |= filter_text_compare(tags, needle, FALSE);
418 }
419 g_free(tags);
420 //if(retval) goto end;
421 }
422
423
424 end:
425 return retval;
426 }
427
428
429 gint filter_test(Filter *flt, Transaction *txn)
430 {
431 Account *accitem;
432 Payee *payitem;
433 Category *catitem;
434 gint insert;
435
436 //DB( g_print("(filter) test\n") );
437
438 insert = 1;
439
440 /*** start filtering ***/
441
442 /* add/change force */
443 if(flt->forceadd == TRUE && (txn->flags & OF_ADDED))
444 goto end;
445
446 if(flt->forcechg == TRUE && (txn->flags & OF_CHANGED))
447 goto end;
448
449 /* date */
450 if(flt->option[FILTER_DATE]) {
451 insert = ( (txn->date >= flt->mindate) && (txn->date <= flt->maxdate) ) ? 1 : 0;
452 if(flt->option[FILTER_DATE] == 2) insert ^= 1;
453 }
454 if(!insert) goto end;
455
456 /* account */
457 if(flt->option[FILTER_ACCOUNT]) {
458 accitem = da_acc_get(txn->kacc);
459 if(accitem)
460 {
461 insert = ( accitem->filter == TRUE ) ? 1 : 0;
462 if(flt->option[FILTER_ACCOUNT] == 2) insert ^= 1;
463 }
464 }
465 if(!insert) goto end;
466
467 /* payee */
468 if(flt->option[FILTER_PAYEE]) {
469 payitem = da_pay_get(txn->kpay);
470 if(payitem)
471 {
472 insert = ( payitem->filter == TRUE ) ? 1 : 0;
473 if(flt->option[FILTER_PAYEE] == 2) insert ^= 1;
474 }
475 }
476 if(!insert) goto end;
477
478 /* category */
479 if(flt->option[FILTER_CATEGORY]) {
480 if(txn->flags & OF_SPLIT)
481 {
482 guint count, i;
483 Split *split;
484
485 insert = 0; //fix: 1151259
486 count = da_transaction_splits_count(txn);
487 for(i=0;i<count;i++)
488 {
489 gint tmpinsert = 0;
490
491 split = txn->splits[i];
492 catitem = da_cat_get(split->kcat);
493 if(catitem)
494 {
495 tmpinsert = ( catitem->filter == TRUE ) ? 1 : 0;
496 if(flt->option[FILTER_CATEGORY] == 2) tmpinsert ^= 1;
497 }
498 insert |= tmpinsert;
499 }
500 }
501 else
502 {
503 catitem = da_cat_get(txn->kcat);
504 if(catitem)
505 {
506 insert = ( catitem->filter == TRUE ) ? 1 : 0;
507 if(flt->option[FILTER_CATEGORY] == 2) insert ^= 1;
508 }
509 }
510 }
511 if(!insert) goto end;
512
513 /* status */
514 if(flt->option[FILTER_STATUS]) {
515 gint insert1 = 0, insert2 = 0;
516
517 if(flt->reconciled)
518 insert1 = ( txn->flags & OF_VALID ) ? 1 : 0;
519 if(flt->reminded)
520 insert2 = ( txn->flags & OF_REMIND ) ? 1 : 0;
521
522 insert = insert1 | insert2;
523 if(flt->option[FILTER_STATUS] == 2) insert ^= 1;
524 }
525 if(!insert) goto end;
526
527 /* paymode */
528 if(flt->option[FILTER_PAYMODE]) {
529 insert = ( flt->paymode[txn->paymode] == TRUE) ? 1 : 0;
530 if(flt->option[FILTER_PAYMODE] == 2) insert ^= 1;
531 }
532 if(!insert) goto end;
533
534 /* amount */
535 if(flt->option[FILTER_AMOUNT]) {
536 insert = ( (txn->amount >= flt->minamount) && (txn->amount <= flt->maxamount) ) ? 1 : 0;
537
538 if(flt->option[FILTER_AMOUNT] == 2) insert ^= 1;
539 }
540 if(!insert) goto end;
541
542 /* info/wording/tag */
543 if(flt->option[FILTER_TEXT])
544 {
545 gchar *tags;
546 gint insert1, insert2, insert3;
547
548 insert1 = insert2 = insert3 = 0;
549 if(flt->info)
550 {
551 if(txn->info)
552 {
553 insert1 = filter_text_compare(txn->info, flt->info, flt->exact);
554 }
555 }
556 else
557 insert1 = 1;
558
559 if(flt->wording)
560 {
561 if(txn->wording)
562 {
563 insert2 = filter_text_compare(txn->wording, flt->wording, flt->exact);
564 }
565 }
566 else
567 insert2 = 1;
568
569 if(flt->tag)
570 {
571 tags = transaction_tags_tostring(txn);
572 if(tags)
573 {
574 insert3 = filter_text_compare(tags, flt->tag, flt->exact);
575 }
576 g_free(tags);
577 }
578 else
579 insert3 = 1;
580
581 insert = insert1 && insert2 && insert3 ? 1 : 0;
582
583 if(flt->option[FILTER_TEXT] == 2) insert ^= 1;
584
585 }
586 if(!insert) goto end;
587
588 end:
589 // DB( g_print(" %d :: %d :: %d\n", flt->mindate, txn->date, flt->maxdate) );
590 // DB( g_print(" [%d] %s => %d (%d)\n", txn->account, txn->wording, insert, count) );
591 return(insert);
592 }
593
This page took 0.068711 seconds and 4 git commands to generate.