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