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 
22 #include "hb-transaction.h"
23 #include "hb-tag.h"
24 #include "hb-split.h"
25 
26 /****************************************************************************/
27 /* Debug macro								    */
28 /****************************************************************************/
29 #define MYDEBUG 0
30 
31 #if MYDEBUG
32 #define DB(x) (x);
33 #else
34 #define DB(x);
35 #endif
36 
37 /* our global datas */
38 extern struct HomeBank *GLOBALS;
39 extern struct Preferences *PREFS;
40 
41 
42 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
43 
44 static void
da_transaction_clean(Transaction * item)45 da_transaction_clean(Transaction *item)
46 {
47 	if(item != NULL)
48 	{
49 		if(item->memo != NULL)
50 		{
51 			g_free(item->memo);
52 			item->memo = NULL;
53 		}
54 		if(item->info != NULL)
55 		{
56 			g_free(item->info);
57 			item->info = NULL;
58 		}
59 		if(item->tags != NULL)
60 		{
61 			g_free(item->tags);
62 			item->tags = NULL;
63 		}
64 
65 		if(item->splits != NULL)
66 		{
67 			da_split_destroy(item->splits);
68 			item->splits = NULL;
69 			item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
70 		}
71 	}
72 }
73 
74 
75 void
da_transaction_free(Transaction * item)76 da_transaction_free(Transaction *item)
77 {
78 	if(item != NULL)
79 	{
80 		da_transaction_clean(item);
81 		g_free(item);
82 	}
83 }
84 
85 
86 Transaction *
da_transaction_malloc(void)87 da_transaction_malloc(void)
88 {
89 	return g_malloc0(sizeof(Transaction));
90 }
91 
92 
da_transaction_init(Transaction * txn,guint32 kacc)93 Transaction *da_transaction_init(Transaction *txn, guint32 kacc)
94 {
95 guint32 date;
96 
97 	DB( g_print("da_transaction_init\n") );
98 
99 	//#1860309 keep the date when init for add/inherit
100 	date = txn->date;
101 	da_transaction_clean(txn);
102 	memset(txn, 0, sizeof(Transaction));
103 	txn->date = date;
104 
105 	//fix: 318733 / 1335285
106 	if( PREFS->heritdate == FALSE )
107 		txn->date = GLOBALS->today;
108 
109 	txn->kacc = kacc;
110 
111 	da_transaction_set_default_template(txn);
112 
113 	return txn;
114 }
115 
116 
da_transaction_init_from_template(Transaction * txn,Archive * arc)117 Transaction *da_transaction_init_from_template(Transaction *txn, Archive *arc)
118 {
119 guint32 date;
120 
121 	DB( g_print("da_transaction_init_from_template\n") );
122 
123 	//#5.4.2 date must remains when we set a template, as template has no date
124 	date = txn->date;
125 	da_transaction_clean(txn);
126 	txn->date = date;
127 
128 	txn->amount	= arc->amount;
129 	//#1258344 keep the current account if tpl is empty
130 	if(arc->kacc)
131 		txn->kacc	= arc->kacc;
132 	txn->paymode	= arc->paymode;
133 	txn->flags		= arc->flags;
134 	txn->status		= arc->status;
135 	txn->kpay		= arc->kpay;
136 	txn->kcat		= arc->kcat;
137 	txn->kxferacc	= arc->kxferacc;
138 	if(arc->memo != NULL)
139 		txn->memo	    = g_strdup(arc->memo);
140 	if(arc->info != NULL)
141 		txn->info	    = g_strdup(arc->info);
142 
143 	txn->tags = tags_clone(arc->tags);
144 
145 	txn->splits = da_splits_clone(arc->splits);
146 	if( da_splits_length (txn->splits) > 0 )
147 		txn->flags |= OF_SPLIT; //Flag that Splits are active
148 
149 	return txn;
150 }
151 
152 
da_transaction_set_default_template(Transaction * txn)153 Transaction *da_transaction_set_default_template(Transaction *txn)
154 {
155 Account *acc;
156 Archive *arc;
157 
158 	DB( g_print("da_transaction_set_default_template\n") );
159 
160 	acc = da_acc_get(txn->kacc);
161 	if(acc != NULL && acc->karc > 0)
162 	{
163 		arc = da_archive_get(acc->karc);
164 		if( arc )
165 		{
166 			DB( g_print(" - init with default template\n") );
167 			da_transaction_init_from_template(txn, arc);
168 		}
169 	}
170 
171 	return txn;
172 }
173 
174 
da_transaction_clone(Transaction * src_item)175 Transaction *da_transaction_clone(Transaction *src_item)
176 {
177 Transaction *new_item = g_memdup(src_item, sizeof(Transaction));
178 
179 	DB( g_print("da_transaction_clone\n") );
180 
181 	if(new_item)
182 	{
183 		//duplicate the string
184 		new_item->memo = g_strdup(src_item->memo);
185 		new_item->info = g_strdup(src_item->info);
186 
187 		//duplicate tags/splits
188 		//no g_free here to avoid free the src tags (memdup copied the ptr)
189 		new_item->tags = tags_clone(src_item->tags);
190 
191 		new_item->splits = da_splits_clone(src_item->splits);
192 		if( da_splits_length (new_item->splits) > 0 )
193 			new_item->flags |= OF_SPLIT; //Flag that Splits are active
194 
195 	}
196 	return new_item;
197 }
198 
199 
200 GList *
da_transaction_new(void)201 da_transaction_new(void)
202 {
203 	return NULL;
204 }
205 
206 
207 guint
da_transaction_length(void)208 da_transaction_length(void)
209 {
210 GList *lst_acc, *lnk_acc;
211 guint count = 0;
212 
213 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
214 	lnk_acc = g_list_first(lst_acc);
215 	while (lnk_acc != NULL)
216 	{
217 	Account *acc = lnk_acc->data;
218 
219 		count += g_queue_get_length (acc->txn_queue);
220 		lnk_acc = g_list_next(lnk_acc);
221 	}
222 	g_list_free(lst_acc);
223 	return count;
224 }
225 
226 
da_transaction_queue_free_ghfunc(Transaction * item,gpointer data)227 static void da_transaction_queue_free_ghfunc(Transaction *item, gpointer data)
228 {
229 	da_transaction_free (item);
230 }
231 
232 
da_transaction_destroy(void)233 void da_transaction_destroy(void)
234 {
235 GList *lacc, *list;
236 
237 	lacc = g_hash_table_get_values(GLOBALS->h_acc);
238 	list = g_list_first(lacc);
239 	while (list != NULL)
240 	{
241 	Account *acc = list->data;
242 
243 		g_queue_foreach(acc->txn_queue, (GFunc)da_transaction_queue_free_ghfunc, NULL);
244 		//txn queue is freed into account
245 		list = g_list_next(list);
246 	}
247 	g_list_free(lacc);
248 }
249 
250 
251 // used from register only
da_transaction_compare_datafunc(Transaction * a,Transaction * b,gpointer data)252 static gint da_transaction_compare_datafunc(Transaction *a, Transaction *b, gpointer data)
253 {
254 gint retval = (gint)a->date - b->date;
255 
256 	if(!retval) //#1749457
257 		retval = a->pos - b->pos;
258 
259 	return retval;
260 }
261 
262 
da_transaction_queue_sort(GQueue * queue)263 void da_transaction_queue_sort(GQueue *queue)
264 {
265 	g_queue_sort(queue, (GCompareDataFunc)da_transaction_compare_datafunc, NULL);
266 }
267 
268 
da_transaction_compare_func(Transaction * a,Transaction * b)269 static gint da_transaction_compare_func(Transaction *a, Transaction *b)
270 {
271 	return ((gint)a->date - b->date);
272 }
273 
274 
da_transaction_sort(GList * list)275 GList *da_transaction_sort(GList *list)
276 {
277 	return( g_list_sort(list, (GCompareFunc)da_transaction_compare_func));
278 }
279 
280 
da_transaction_insert_memo(Transaction * item)281 gboolean da_transaction_insert_memo(Transaction *item)
282 {
283 gboolean retval = FALSE;
284 
285 	if( item->memo != NULL )
286 	{
287 		//# 1673048 add filter on status and date obsolete
288 		if( (PREFS->txn_memoacp == TRUE) && (item->date >= (GLOBALS->today - PREFS->txn_memoacp_days)) )
289 		{
290 			if( g_hash_table_lookup(GLOBALS->h_memo, item->memo) == NULL )
291 			{
292 				retval = g_hash_table_insert(GLOBALS->h_memo, g_strdup(item->memo), NULL);
293 			}
294 		}
295 	}
296 	return retval;
297 }
298 
299 
da_transaction_insert_sorted(Transaction * newitem)300 gboolean da_transaction_insert_sorted(Transaction *newitem)
301 {
302 Account *acc;
303 GList *lnk_txn;
304 
305 	acc = da_acc_get(newitem->kacc);
306 	if(!acc)
307 		return FALSE;
308 
309 	lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
310 	while (lnk_txn != NULL)
311 	{
312 	Transaction *item = lnk_txn->data;
313 
314 		if(item->date <= newitem->date)
315 			break;
316 
317 		lnk_txn = g_list_previous(lnk_txn);
318 	}
319 
320 	// we're at insert point, insert after txn
321 	g_queue_insert_after(acc->txn_queue, lnk_txn, newitem);
322 
323 	da_transaction_insert_memo(newitem);
324 	return TRUE;
325 }
326 
327 
328 // nota: this is called only when loading xml file
da_transaction_prepend(Transaction * item)329 gboolean da_transaction_prepend(Transaction *item)
330 {
331 Account *acc;
332 
333 	acc = da_acc_get(item->kacc);
334 	//#1661279
335 	if(!acc)
336 		return FALSE;
337 
338 	item->kcur = acc->kcur;
339 	g_queue_push_tail(acc->txn_queue, item);
340 	da_transaction_insert_memo(item);
341 	return TRUE;
342 }
343 
344 
345 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
346 
347 static guint32
da_transaction_get_max_kxfer(void)348 da_transaction_get_max_kxfer(void)
349 {
350 GList *lst_acc, *lnk_acc;
351 GList *list;
352 guint32 max_key = 0;
353 
354 	DB( g_print("da_transaction_get_max_kxfer\n") );
355 
356 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
357 	lnk_acc = g_list_first(lst_acc);
358 	while (lnk_acc != NULL)
359 	{
360 	Account *acc = lnk_acc->data;
361 
362 		list = g_queue_peek_head_link(acc->txn_queue);
363 		while (list != NULL)
364 		{
365 		Transaction *item = list->data;
366 
367 			if( item->flags & OF_INTXFER )
368 			{
369 				max_key = MAX(max_key, item->kxfer);
370 			}
371 			list = g_list_next(list);
372 		}
373 
374 		lnk_acc = g_list_next(lnk_acc);
375 	}
376 	g_list_free(lst_acc);
377 
378 	DB( g_print(" max_key : %d \n", max_key) );
379 
380 	return max_key;
381 }
382 
383 
da_transaction_goto_orphan(Transaction * txn)384 static void da_transaction_goto_orphan(Transaction *txn)
385 {
386 const gchar *oatn = "orphaned transactions";
387 Account *ori_acc, *acc;
388 gboolean found;
389 
390 	DB( g_print("\n[transaction] goto orphan\n") );
391 
392 	g_warning("txn consistency: moving to orphan %d '%s' %.2f", txn->date, txn->memo, txn->amount);
393 
394 	acc = da_acc_get_by_name((gchar *)oatn);
395 	if(acc == NULL)
396 	{
397 		acc = da_acc_malloc();
398 		acc->name = g_strdup(oatn);
399 		da_acc_append(acc);
400 		DB( g_print(" - created orphan acc %d\n", acc->key) );
401 	}
402 
403 	ori_acc = da_acc_get(txn->kacc);
404 	if( ori_acc )
405 	{
406 		found = g_queue_remove(ori_acc->txn_queue, txn);
407 		DB( g_print(" - found in origin ? %d\n", found) );
408 		if(found)
409 		{
410 			txn->kacc = acc->key;
411 			da_transaction_insert_sorted (txn);
412 			DB( g_print("moved txn to %d\n", txn->kacc) );
413 		}
414 	}
415 }
416 
417 
da_transaction_set_flag(Transaction * item)418 void da_transaction_set_flag(Transaction *item)
419 {
420 	//DB( g_print("\n[transaction] set flag\n") );
421 	item->flags &= ~(OF_INCOME);
422 	if( item->amount > 0)
423 		item->flags |= (OF_INCOME);
424 }
425 
426 
da_transaction_consistency(Transaction * item)427 void da_transaction_consistency(Transaction *item)
428 {
429 Account *acc;
430 Category *cat;
431 Payee *pay;
432 guint nbsplit;
433 
434 	DB( g_print("\n[transaction] consistency\n") );
435 
436 
437 	DB( g_print(" %d %.2f %s\n", item->date, item->amount, item->memo) );
438 
439 	// ensure date is between range
440 	item->date = CLAMP(item->date, HB_MINDATE, HB_MAXDATE);
441 
442 	// check account exists
443 	acc = da_acc_get(item->kacc);
444 	if(acc == NULL)
445 	{
446 		g_warning("txn consistency: fixed invalid acc %d", item->kacc);
447 		da_transaction_goto_orphan(item);
448 		GLOBALS->changes_count++;
449 	}
450 
451 	// check category exists
452 	cat = da_cat_get(item->kcat);
453 	if(cat == NULL)
454 	{
455 		g_warning("txn consistency: fixed invalid cat %d", item->kcat);
456 		item->kcat = 0;
457 		GLOBALS->changes_count++;
458 	}
459 
460 	//#1340142 check split category
461 	if( item->splits != NULL )
462 	{
463 		nbsplit = da_splits_consistency(item->splits);
464 		//# 1416624 empty category when split
465 		if(nbsplit > 0 && item->kcat > 0)
466 		{
467 			g_warning("txn consistency: fixed invalid cat on split txn");
468 			item->kcat = 0;
469 			GLOBALS->changes_count++;
470 		}
471 	}
472 
473 	// check payee exists
474 	pay = da_pay_get(item->kpay);
475 	if(pay == NULL)
476 	{
477 		g_warning("txn consistency: fixed invalid pay %d", item->kpay);
478 		item->kpay = 0;
479 		GLOBALS->changes_count++;
480 	}
481 
482 	// 5.3: fix split on intxfer
483 	if( ((item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER)) && (item->splits != NULL) )
484 	{
485 		g_warning("txn consistency: fixed invalid split on xfer");
486 		item->flags &= ~(OF_INTXFER);
487 		item->paymode = PAYMODE_XFER;
488 		item->kxfer = 0;
489 		item->kxferacc = 0;
490 	}
491 
492 	// reset dst acc for non xfer transaction
493 	if( !((item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER)) )
494 	{
495 		if( (item->kxfer != 0) || (item->kxferacc != 0) )
496 		{
497 			g_warning("txn consistency: fixed invalid xfer");
498 			item->kxfer = 0;
499 			item->kxferacc = 0;
500 		}
501 	}
502 
503 	// intxfer: check dst account exists
504 	if( (item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER) )
505 	{
506 	gint tak = item->kxferacc;
507 
508 		item->kxferacc = ABS(tak);  //I crossed negative here one day
509 		acc = da_acc_get(item->kxferacc);
510 		if(acc == NULL)
511 		{
512 			g_warning("txn consistency: fixed invalid dst_acc %d", item->kxferacc);
513 			da_transaction_goto_orphan(item);
514 			item->kxfer = 0;
515 			item->paymode = PAYMODE_XFER;
516 			GLOBALS->changes_count++;
517 		}
518 	}
519 
520 	//#1628678 tags for internal xfer should be checked as well
521 	//#1787826 intxfer should not have split
522 
523 	//#1295877 ensure income flag is correctly set
524 	da_transaction_set_flag(item);
525 
526 	//#1308745 ensure remind flag unset if reconciled
527 	//useless since 5.0
528 	//if( item->flags & OF_VALID )
529 	//	item->flags &= ~(OF_REMIND);
530 
531 }
532 
533 
534 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
535 /* new transfer functions */
536 
transaction_xfer_create_child(Transaction * ope)537 static void transaction_xfer_create_child(Transaction *ope)
538 {
539 Transaction *child;
540 Account *acc;
541 guint32 swap;
542 
543 	DB( g_print("\n[transaction] xfer_create_child\n") );
544 
545 	if( ope->kxferacc > 0 )
546 	{
547 		child = da_transaction_clone(ope);
548 
549 		ope->flags |= (OF_CHANGED | OF_INTXFER);
550 		child->flags |= (OF_ADDED | OF_INTXFER);
551 
552 		child->amount = -child->amount;
553 		child->flags ^= (OF_INCOME);	// invert flag
554 		//#1268026 #1690555
555 		if( (child->status == TXN_STATUS_CLEARED) || (child->status == TXN_STATUS_RECONCILED) )
556 			child->status = TXN_STATUS_NONE;
557 		//child->flags &= ~(OF_VALID);	// delete reconcile state
558 
559 		swap = child->kacc;
560 		child->kacc = child->kxferacc;
561 		child->kxferacc = swap;
562 
563 		/* update acc flags */
564 		acc = da_acc_get( child->kacc );
565 		if(acc != NULL)
566 		{
567 			acc->flags |= AF_ADDED;
568 
569 			//strong link
570 			guint maxkey = da_transaction_get_max_kxfer();
571 
572 			DB( g_print(" + maxkey is %d\n", maxkey) );
573 
574 
575 			ope->kxfer = maxkey+1;
576 			child->kxfer = maxkey+1;
577 
578 			DB( g_print(" + strong link to %d\n", ope->kxfer) );
579 
580 
581 			DB( g_print(" + add transfer, %p to acc %d\n", child, acc->key) );
582 
583 			da_transaction_insert_sorted(child);
584 
585 			account_balances_add (child);
586 
587 		}
588 	}
589 
590 }
591 
592 
transaction_xfer_child_match_rate(Transaction * stxn,Transaction * ptxn)593 static gint transaction_xfer_child_match_rate(Transaction *stxn, Transaction *ptxn)
594 {
595 gint rate = 0;
596 
597 	DB( g_print("\n[transaction] xfer_child_match_rate\n") );
598 
599 	//default rate is based on exact match field: cur+amount
600 	rate = 2;
601 	if( stxn->kpay == ptxn->kpay)
602 		rate++;
603 	if( stxn->kcat == ptxn->kcat)
604 		rate++;
605 	if( hb_string_compare(stxn->memo, ptxn->memo) == 0 )
606 		rate++;
607 	//TODO: maybe add info & tag here
608 
609 	rate = (rate *100) / 5;
610 
611 	DB( g_print(" return %d\n", rate) );
612 
613 	return rate;
614 }
615 
616 
617 //todo: add strong control and extend to payee, maybe memo ?
618 // #1708974 enable different date
transaction_xfer_child_might(Transaction * stxn,Transaction * dtxn,guint32 daygap)619 static gboolean transaction_xfer_child_might(Transaction *stxn, Transaction *dtxn, guint32 daygap)
620 {
621 gboolean retval = FALSE;
622 
623 	DB( g_print("\n[transaction] xfer_child_might\n") );
624 
625 	if(stxn == dtxn)
626 		return FALSE;
627 
628 	DB( g_print(" src: %d %d %d %f %d\n", stxn->kcur, stxn->date, stxn->kacc, ABS(stxn->amount), stxn->kxfer ) );
629 	DB( g_print(" dst: %d %d %d %f %d\n", dtxn->kcur, dtxn->date, dtxn->kacc, ABS(dtxn->amount), dtxn->kxfer ) );
630 
631 	if( stxn->kcur == dtxn->kcur &&
632 	    //stxn->date == dtxn->date &&
633 		//# 1708974 enable different date
634 		(dtxn->date <= (stxn->date + daygap)) &&
635 	    (dtxn->date >= (stxn->date - daygap)) &&
636 	    //v5.1 make no sense: stxn->kxferacc == dtxn->kacc &&
637 	    stxn->kacc != dtxn->kacc &&
638 	    ABS(stxn->amount) == ABS(dtxn->amount) &&
639 	    dtxn->kxfer == 0)
640 	{
641 		//TODO: we could evaluate here a match rate based on several fields
642 		retval = TRUE;
643 	}
644 
645 	DB( g_print(" return %d\n", retval) );
646 	return retval;
647 }
648 
649 
transaction_xfer_child_might_list_get(Transaction * ope,guint32 kdstacc)650 static GList *transaction_xfer_child_might_list_get(Transaction *ope, guint32 kdstacc)
651 {
652 GList *lst_acc, *lnk_acc;
653 GList *list, *matchlist = NULL;
654 
655 	DB( g_print("\n[transaction] xfer_child_might_list_get\n") );
656 
657 	DB( g_print(" - kdstacc:%d\n", kdstacc) );
658 
659 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
660 	lnk_acc = g_list_first(lst_acc);
661 	while (lnk_acc != NULL)
662 	{
663 	Account *acc = lnk_acc->data;
664 
665 		if( !(acc->flags & AF_CLOSED) && (acc->key != ope->kacc) && ( (acc->key == kdstacc) || kdstacc == 0 ) )
666 		{
667 			list = g_queue_peek_tail_link(acc->txn_queue);
668 			while (list != NULL)
669 			{
670 			Transaction *item = list->data;
671 
672 				// no need to go higher than src txn date - daygap
673 				if(item->date < (ope->date - PREFS->txn_xfer_daygap))
674 					break;
675 
676 				if( transaction_xfer_child_might(ope, item, PREFS->txn_xfer_daygap) == TRUE )
677 				{
678 					item->matchrate = transaction_xfer_child_match_rate(ope, item);
679 					DB( g_print(" - match %3d: %d %s %f %d=>%d\n", item->matchrate, item->date, item->memo, item->amount, item->kacc, item->kxferacc) );
680 					matchlist = g_list_append(matchlist, item);
681 				}
682 				list = g_list_previous(list);
683 			}
684 		}
685 
686 		lnk_acc = g_list_next(lnk_acc);
687 	}
688 	g_list_free(lst_acc);
689 
690 	return matchlist;
691 }
692 
693 
transaction_xfer_search_or_add_child(GtkWindow * parent,Transaction * ope,guint32 kdstacc)694 void transaction_xfer_search_or_add_child(GtkWindow *parent, Transaction *ope, guint32 kdstacc)
695 {
696 GList *matchlist;
697 gint count;
698 
699 	DB( g_print("\n[transaction] xfer_search_or_add_child\n") );
700 
701 	matchlist = transaction_xfer_child_might_list_get(ope, kdstacc);
702 	count = g_list_length(matchlist);
703 
704 	DB( g_print(" - found %d might match, switching\n", count) );
705 
706 	switch(count)
707 	{
708 		case 0:		//we should create the child
709 			transaction_xfer_create_child(ope);
710 			break;
711 
712 		//todo: maybe with just 1 match the user must choose ?
713 		//#942346: bad idea so to no let the user confirm, so let him confirm
714 		/*
715 		case 1:		//transform the transaction to a child transfer
716 		{
717 			GList *list = g_list_first(matchlist);
718 			transaction_xfer_change_to_child(ope, list->data);
719 			break;
720 		}
721 		*/
722 
723 		default:	//the user must choose himself
724 		{
725 		gint result;
726 		Transaction *child;
727 
728 			result = ui_dialog_transaction_xfer_select_child(parent, ope, matchlist, &child);
729 			if( result == GTK_RESPONSE_ACCEPT )
730 			{
731 				//#1827193 child can be null...
732 				DB( g_print(" child %p\n", child) );
733 				if( child != NULL )
734 					transaction_xfer_change_to_child(ope, child);
735 				else
736 					transaction_xfer_create_child(ope);
737 			}
738 			else //GTK_RESPONSE_CANCEL
739 			{
740 				ope->paymode = PAYMODE_NONE;
741 				ope->kxfer = 0;
742 				ope->kxferacc = 0;
743 				//fixed 5.5 when cancel xfer remains
744 				ope->flags &= ~(OF_INTXFER);
745 				da_transaction_set_flag(ope);
746 			}
747 		}
748 	}
749 
750 	g_list_free(matchlist);
751 }
752 
753 
transaction_xfer_child_strong_get(Transaction * src)754 Transaction *transaction_xfer_child_strong_get(Transaction *src)
755 {
756 Account *dstacc;
757 GList *list;
758 
759 	DB( g_print("\n[transaction] xfer_child_strong_get\n") );
760 
761 	dstacc = da_acc_get(src->kxferacc);
762 	if( !dstacc || src->kxfer <= 0 )
763 		return NULL;
764 
765 	DB( g_print(" - search: %d %s %f %d=>%d - %d\n", src->date, src->memo, src->amount, src->kacc, src->kxferacc, src->kxfer) );
766 
767 	list = g_queue_peek_tail_link(dstacc->txn_queue);
768 	while (list != NULL)
769 	{
770 	Transaction *item = list->data;
771 
772 		//#1252230
773 		//if( item->paymode == PAYMODE_INTXFER
774 		//	&& item->kacc == src->kxferacc
775 		if( (item->flags & OF_INTXFER)
776 		 && (item->kxfer == src->kxfer)
777 		 && (item != src) )
778 		{
779 			DB( g_print(" - found : %d %s %f %d=>%d - %d\n", item->date, item->memo, item->amount, item->kacc, item->kxferacc, src->kxfer) );
780 			return item;
781 		}
782 		list = g_list_previous(list);
783 	}
784 
785 	DB( g_print(" - not found...\n") );
786 	return NULL;
787 }
788 
789 
790 
791 //TODO: should be static but used in hb_import
transaction_xfer_change_to_child(Transaction * ope,Transaction * child)792 void transaction_xfer_change_to_child(Transaction *ope, Transaction *child)
793 {
794 Account *dstacc;
795 
796 	DB( g_print("\n[transaction] xfer_change_to_child\n") );
797 
798 	if(ope->kcur != child->kcur)
799 		return;
800 
801 	ope->flags |= (OF_CHANGED | OF_INTXFER);
802 	child->flags |= (OF_ADDED | OF_INTXFER);
803 
804 	ope->kxferacc = child->kacc;
805 	child->kxferacc = ope->kacc;
806 
807 	/* update acc flags */
808 	dstacc = da_acc_get( child->kacc);
809 	if(dstacc != NULL)
810 		dstacc->flags |= AF_CHANGED;
811 
812 	//strong link
813 	guint maxkey = da_transaction_get_max_kxfer();
814 	ope->kxfer = maxkey+1;
815 	child->kxfer = maxkey+1;
816 
817 }
818 
819 
transaction_xfer_child_sync(Transaction * s_txn,Transaction * child)820 void transaction_xfer_child_sync(Transaction *s_txn, Transaction *child)
821 {
822 Account *acc;
823 
824 	DB( g_print("\n[transaction] xfer_child_sync\n") );
825 
826 	if( child == NULL )
827 	{
828 		DB( g_print(" - no child found\n") );
829 		return;
830 	}
831 
832 	DB( g_print(" - found do sync\n") );
833 
834 	/* update acc flags */
835 	acc = da_acc_get( child->kacc);
836 	if(acc != NULL)
837 		acc->flags |= AF_CHANGED;
838 
839 	account_balances_sub (child);
840 
841 	//# 1708974 enable different date
842 	//child->date		= s_txn->date;
843 	child->amount   = -s_txn->amount;
844 	child->flags	= child->flags | OF_CHANGED;
845 	//#1295877
846 	child->flags &= ~(OF_INCOME);
847 	if( child->amount > 0)
848 	  child->flags |= (OF_INCOME);
849 	child->kpay		= s_txn->kpay;
850 	child->kcat		= s_txn->kcat;
851 	if(child->memo)
852 		g_free(child->memo);
853 	child->memo	= g_strdup(s_txn->memo);
854 	if(child->info)
855 		g_free(child->info);
856 	child->info		= g_strdup(s_txn->info);
857 
858 	account_balances_add (child);
859 
860 	//#1252230 sync account also
861 	//#1663789 idem after 5.1
862 	//source changed: update child key (move of s_txn is done in external_edit)
863 	if( s_txn->kacc != child->kxferacc )
864 	{
865 		child->kxferacc = s_txn->kacc;
866 	}
867 
868 	//dest changed: move child & update child key
869 	if( s_txn->kxferacc != child->kacc )
870 	{
871 		transaction_acc_move(child, child->kacc, s_txn->kxferacc);
872 	}
873 
874 	//synchronise tags since 5.1
875 	g_free(child->tags);
876 	child->tags = tags_clone (s_txn->tags);
877 
878 }
879 
880 
transaction_xfer_remove_child(Transaction * src)881 void transaction_xfer_remove_child(Transaction *src)
882 {
883 Transaction *dst;
884 
885 	DB( g_print("\n[transaction] xfer_remove_child\n") );
886 
887 	dst = transaction_xfer_child_strong_get( src );
888 	if( dst != NULL )
889 	{
890 	Account *acc = da_acc_get(dst->kacc);
891 
892 		if( acc != NULL )
893 		{
894 			DB( g_print("deleting...") );
895 			account_balances_sub(dst);
896 			g_queue_remove(acc->txn_queue, dst);
897 			//#1419304 we keep the deleted txn to a trash stack
898 			//da_transaction_free (dst);
899 			//g_trash_stack_push(&GLOBALS->txn_stk, dst);
900 			GLOBALS->deltxn_list = g_slist_prepend(GLOBALS->deltxn_list, dst);
901 
902 			//#1691992
903 			acc->flags |= AF_CHANGED;
904 		}
905 	}
906 
907 	src->kxfer = 0;
908 	src->kxferacc = 0;
909 }
910 
911 
912 // still useful for upgrade from < file v0.6 (hb v4.4 kxfer)
transaction_old_get_child_transfer(Transaction * src)913 Transaction *transaction_old_get_child_transfer(Transaction *src)
914 {
915 Account *acc;
916 GList *list;
917 
918 	DB( g_print("\n[transaction] get_child_transfer\n") );
919 
920 	//DB( g_print(" search: %d %s %f %d=>%d\n", src->date, src->memo, src->amount, src->account, src->kxferacc) );
921 	acc = da_acc_get(src->kxferacc);
922 
923 	if( acc != NULL )
924 	{
925 		list = g_queue_peek_head_link(acc->txn_queue);
926 		while (list != NULL)
927 		{
928 		Transaction *item = list->data;
929 
930 			// no need to go higher than src txn date
931 			if(item->date > src->date)
932 				break;
933 
934 			// keep this one for backward compatibility
935 			if( item->paymode == OLDPAYMODE_INTXFER)
936 			{
937 				if( src->date == item->date &&
938 					src->kacc == item->kxferacc &&
939 					src->kxferacc == item->kacc &&
940 					ABS(src->amount) == ABS(item->amount) )
941 				{
942 					//DB( g_print(" found : %d %s %f %d=>%d\n", item->date, item->memo, item->amount, item->account, item->kxferacc) );
943 
944 					return item;
945 				}
946 			}
947 			list = g_list_next(list);
948 		}
949 	}
950 
951 	DB( g_print(" not found...\n") );
952 
953 	return NULL;
954 }
955 
956 
957 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
958 
959 
transaction_get_status_string(Transaction * txn)960 gchar *transaction_get_status_string(Transaction *txn)
961 {
962 gchar *retval = "";
963 
964 	if(txn->status == TXN_STATUS_CLEARED)
965 		retval = "c";
966 	else
967 	if(txn->status == TXN_STATUS_RECONCILED)
968 		retval = "R";
969 	//else
970 	//if(txn->status == TXN_STATUS_REMIND)
971 	//	retval = "?";
972 	else
973 	if(txn->status == TXN_STATUS_VOID)
974 		retval = "v";
975 
976 	return retval;
977 }
978 
979 
transaction_is_balanceable(Transaction * ope)980 gboolean transaction_is_balanceable(Transaction *ope)
981 {
982 gboolean retval = TRUE;
983 
984 	if( (ope->status == TXN_STATUS_VOID) )
985 		retval = FALSE;
986 	else if( (ope->status == TXN_STATUS_REMIND) && (PREFS->includeremind == FALSE) )
987 		retval = FALSE;
988 
989 	   return retval;
990 }
991 
992 
transaction_remove(Transaction * ope)993 void transaction_remove(Transaction *ope)
994 {
995 Account *acc;
996 
997 	//controls accounts valid (archive scheduled maybe bad)
998 	acc = da_acc_get(ope->kacc);
999 	if(acc == NULL) return;
1000 
1001 	account_balances_sub(ope);
1002 
1003 	if( ope->flags & OF_INTXFER )
1004 	{
1005 		transaction_xfer_remove_child( ope );
1006 	}
1007 
1008 	g_queue_remove(acc->txn_queue, ope);
1009 	acc->flags |= AF_CHANGED;
1010 	//#1419304 we keep the deleted txn to a trash stack
1011 	//da_transaction_free(entry);
1012 	//g_trash_stack_push(&GLOBALS->txn_stk, ope);
1013 	GLOBALS->deltxn_list = g_slist_prepend(GLOBALS->deltxn_list, ope);
1014 }
1015 
1016 
transaction_changed(Transaction * txn,gboolean saverecondate)1017 void transaction_changed(Transaction *txn, gboolean saverecondate)
1018 {
1019 Account *acc;
1020 
1021 	if( txn == NULL )
1022 		return;
1023 
1024 	acc = da_acc_get(txn->kacc);
1025 	if(acc == NULL)
1026 		return;
1027 
1028 	acc->flags |= AF_CHANGED;
1029 
1030 	//#1581863 store reconciled date
1031 	if( saverecondate == TRUE )
1032 		acc->rdate = GLOBALS->today;
1033 
1034 }
1035 
1036 
transaction_add(GtkWindow * parent,Transaction * ope)1037 Transaction *transaction_add(GtkWindow *parent, Transaction *ope)
1038 {
1039 Transaction *newope;
1040 Account *acc;
1041 
1042 	DB( g_print("\n[transaction] transaction_add\n") );
1043 
1044 	//controls accounts valid (archive scheduled maybe bad)
1045 	acc = da_acc_get(ope->kacc);
1046 	if(acc == NULL) return NULL;
1047 
1048 	DB( g_print(" acc is '%s' %d\n", acc->name, acc->key) );
1049 
1050 	ope->kcur = acc->kcur;
1051 
1052 	if(ope->flags & OF_INTXFER)
1053 	{
1054 		acc = da_acc_get(ope->kxferacc);
1055 		if(acc == NULL) return NULL;
1056 
1057 		// delete any splits
1058 		da_split_destroy(ope->splits);
1059 		ope->splits = NULL;
1060 		ope->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
1061 	}
1062 
1063 
1064 	//allocate a new entry and copy from our edited structure
1065 	newope = da_transaction_clone(ope);
1066 
1067 	//init flag and keep remind status
1068 	// already done in deftransaction_get
1069 	//ope->flags |= (OF_ADDED);
1070 	//remind = (ope->flags & OF_REMIND) ? TRUE : FALSE;
1071 	//ope->flags &= (~OF_REMIND);
1072 
1073 	/* cheque number is already stored in deftransaction_get */
1074 	/* todo:move this to transaction add
1075 		store a new cheque number into account ? */
1076 
1077 	if( (newope->paymode == PAYMODE_CHECK) && (newope->info) && !(newope->flags & OF_INCOME) )
1078 	{
1079 	guint cheque;
1080 
1081 		/* get the active account and the corresponding cheque number */
1082 		acc = da_acc_get( newope->kacc);
1083 		if( acc != NULL )
1084 		{
1085 			cheque = atol(newope->info);
1086 
1087 			//DB( g_print(" -> should store cheque number %d to %d", cheque, newope->account) );
1088 			if( newope->flags & OF_CHEQ2 )
1089 			{
1090 				acc->cheque2 = MAX(acc->cheque2, cheque);
1091 			}
1092 			else
1093 			{
1094 				acc->cheque1 = MAX(acc->cheque1, cheque);
1095 			}
1096 		}
1097 	}
1098 
1099 	/* add normal transaction */
1100 	acc = da_acc_get( newope->kacc);
1101 	if(acc != NULL)
1102 	{
1103 		acc->flags |= AF_ADDED;
1104 
1105 		DB( g_print(" + add normal %p to acc %d\n", newope, acc->key) );
1106 		//da_transaction_append(newope);
1107 		da_transaction_insert_sorted(newope);
1108 
1109 		account_balances_add(newope);
1110 
1111 		if(newope->flags & OF_INTXFER)
1112 		{
1113 			transaction_xfer_search_or_add_child(parent, newope, newope->kxferacc);
1114 		}
1115 
1116 		//#1581863 store reconciled date
1117 		if( newope->status == TXN_STATUS_RECONCILED )
1118 			acc->rdate = GLOBALS->today;
1119 	}
1120 
1121 	return newope;
1122 }
1123 
1124 
transaction_acc_move(Transaction * txn,guint32 okacc,guint32 nkacc)1125 gboolean transaction_acc_move(Transaction *txn, guint32 okacc, guint32 nkacc)
1126 {
1127 Account *oacc, *nacc;
1128 
1129 	DB( g_print("\n[transaction] acc_move\n") );
1130 
1131 	if( okacc == nkacc )
1132 		return TRUE;
1133 
1134 	oacc = da_acc_get(okacc);
1135 	nacc = da_acc_get(nkacc);
1136 	if( oacc && nacc )
1137 	{
1138 		account_balances_sub(txn);
1139 		if( g_queue_remove(oacc->txn_queue, txn) )
1140 		{
1141 			g_queue_push_tail(nacc->txn_queue, txn);
1142 			txn->kacc = nacc->key;
1143 			txn->kcur = nacc->kcur;
1144 			nacc->flags |= AF_CHANGED;
1145 			account_balances_add(txn);
1146 			//#1865083 src acc also changed (balance)
1147 			oacc->flags |= AF_CHANGED;
1148 			return TRUE;
1149 		}
1150 		else
1151 		{
1152 			//ensure to keep txn into current account
1153 			txn->kacc = okacc;
1154 			account_balances_add(txn);
1155 		}
1156 	}
1157 	return FALSE;
1158 }
1159 
1160 
1161 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1162 
1163 
transaction_similar_match(Transaction * stxn,Transaction * dtxn,guint32 daygap)1164 static gboolean transaction_similar_match(Transaction *stxn, Transaction *dtxn, guint32 daygap)
1165 {
1166 gboolean retval = FALSE;
1167 
1168 	if(stxn == dtxn)
1169 		return FALSE;
1170 
1171 	DB( g_print(" date: %d - %d = %d\n", stxn->date, dtxn->date, stxn->date - dtxn->date) );
1172 
1173 	if( stxn->kcur == dtxn->kcur
1174 	 &&	stxn->amount == dtxn->amount
1175 	 && ( (stxn->date - dtxn->date) <= daygap )
1176 	 //todo: at import we also check payee, but maybe too strict here
1177 	 && (hb_string_compare(stxn->memo, dtxn->memo) == 0)
1178 	  )
1179 	{
1180 		retval = TRUE;
1181 	}
1182 	return retval;
1183 }
1184 
1185 
transaction_similar_unmark(Account * acc)1186 void transaction_similar_unmark(Account *acc)
1187 {
1188 GList *lnk_txn;
1189 
1190 	lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
1191 	while (lnk_txn != NULL)
1192 	{
1193 	Transaction *stxn = lnk_txn->data;
1194 
1195 		stxn->dspflags &= ~(TXN_DSPFLG_DUPSRC|TXN_DSPFLG_DUPDST);
1196 		//stxn->marker = TXN_MARK_NONE;
1197 		lnk_txn = g_list_previous(lnk_txn);
1198 	}
1199 }
1200 
1201 
transaction_similar_mark(Account * acc,guint32 daygap)1202 gint transaction_similar_mark(Account *acc, guint32 daygap)
1203 {
1204 GList *lnk_txn, *list2;
1205 gint nball = 0;
1206 gint nbdup = 0;
1207 
1208 	//warning the list must be sorted by date then amount
1209 	//ideally (easier to parse) we shoudl get a list sorted by amount, then date
1210 	DB( g_print("\n[transaction] check duplicate\n") );
1211 
1212 	DB( g_print("\n - account:'%s' gap:%d\n", acc->name, daygap) );
1213 
1214 	#if MYDEBUG == 1
1215 	GTimer *t = g_timer_new();
1216 	g_print(" - start parse\n");
1217 	#endif
1218 
1219 
1220 	/*
1221 	llast = g_list_last(old ope list);
1222 	DB( g_print("- end last : %f sec\n", g_timer_elapsed(t, NULL)) );
1223 	g_timer_reset(t);
1224 
1225 	ltxn = llast->data;
1226 	g_date_clear(&gd, 1);
1227 	g_date_set_julian(&gd, ltxn->date);
1228 	g_print(" - last julian=%u %02d-%02d-%04d\n", ltxn->date, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
1229 
1230 	minjulian = ltxn->date - (366*2);
1231 	g_date_clear(&gd, 1);
1232 	g_date_set_julian(&gd, minjulian);
1233 	g_print(" - min julian=%u %02d-%02d-%04d\n", minjulian, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
1234 	*/
1235 
1236 	transaction_similar_unmark(acc);
1237 
1238 	//mark duplicate
1239 	lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
1240 	while (lnk_txn != NULL)
1241 	{
1242 	Transaction *stxn = lnk_txn->data;
1243 
1244 		//if(stxn->date < minjulian)
1245 		//	break;
1246 		DB( g_print("------\n eval src: %d, '%s', '%s', %.2f\n", stxn->date, stxn->info, stxn->memo, stxn->amount) );
1247 
1248 		list2 = g_list_previous(lnk_txn);
1249 		while (list2 != NULL)
1250 		{
1251 		Transaction *dtxn = list2->data;
1252 
1253 			DB( g_print(" + with dst: %d, '%s', '%s', %.2f\n", dtxn->date, dtxn->info, dtxn->memo, dtxn->amount) );
1254 
1255 			if( (stxn->date - dtxn->date) > daygap )
1256 			{
1257 				DB( g_print(" break %d %d\n", (dtxn->date - daygap) , (stxn->date - daygap)) );
1258 				break;
1259 			}
1260 
1261 			//if( dtxn->marker == TXN_MARK_NONE )
1262 			if( (dtxn->dspflags & (TXN_DSPFLG_DUPSRC|TXN_DSPFLG_DUPDST)) == 0 )
1263 			{
1264 				if( transaction_similar_match(stxn, dtxn, daygap) )
1265 				{
1266 					//stxn->marker = TXN_MARK_DUPSRC;
1267 					stxn->dspflags |= TXN_DSPFLG_DUPSRC;
1268 					//dtxn->marker = TXN_MARK_DUPDST;
1269 					dtxn->dspflags |= TXN_DSPFLG_DUPDST;
1270 					DB( g_print(" = dtxn marker=%d\n", dtxn->marker) );
1271 					nball++;
1272 				}
1273 			}
1274 			else
1275 			{
1276 				DB( g_print(" already marked %d\n", dtxn->marker) );
1277 			}
1278 
1279 
1280 			list2 = g_list_previous(list2);
1281 		}
1282 
1283 		DB( g_print(" = stxn marker=%d\n", stxn->marker) );
1284 		//if( stxn->marker == TXN_MARK_DUPSRC )
1285 		if( stxn->dspflags & TXN_DSPFLG_DUPSRC )
1286 			nbdup++;
1287 
1288 		lnk_txn = g_list_previous(lnk_txn);
1289 	}
1290 
1291 	DB( g_print(" - end parse : %f sec\n", g_timer_elapsed(t, NULL)) );
1292 	DB( g_timer_destroy (t) );
1293 
1294 	DB( g_print(" - found: %d/%d dup\n", nbdup, nball ) );
1295 
1296 	return nbdup;
1297 }
1298 
1299 
1300 guint
transaction_auto_all_from_payee(GList * txnlist)1301 transaction_auto_all_from_payee(GList *txnlist)
1302 {
1303 GList *list;
1304 Payee *pay;
1305 guint changes = 0;
1306 
1307 	DB( g_print("\n[transaction] auto from payee\n") );
1308 
1309 	list = g_list_first(txnlist);
1310 	while(list != NULL)
1311 	{
1312 	Transaction *txn = list->data;
1313 
1314 		pay = da_pay_get(txn->kpay);
1315 		if( pay != NULL )
1316 		{
1317 			if( txn->kcat == 0 )
1318 			{
1319 				txn->kcat = pay->kcat;
1320 				changes++;
1321 			}
1322 
1323 			if( txn->paymode == PAYMODE_NONE )
1324 			{
1325 				txn->paymode = pay->paymode;
1326 				changes++;
1327 			}
1328 		}
1329 		list = g_list_next(list);
1330 	}
1331 
1332 	return changes;
1333 }
1334 
1335 
1336 
1337 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1338 /* = = experimental = = */
1339 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1340 
1341 
1342 /*
1343 probably add a structure hosted into a glist here
1344 with kind of problem: duplicate, child xfer, orphan xfer
1345 and collect all that with target txn
1346 */
1347 
1348 
1349 /*void future_transaction_test_account(Account *acc)
1350 {
1351 GList *lnk_txn, *list2;
1352 gint nball = 0;
1353 gint nbdup = 0;
1354 gint nbxfer = 0;
1355 GPtrArray *array;
1356 
1357 //future
1358 gint gapday = 0, i;
1359 
1360 	//warning the list must be sorted by date then amount
1361 	//ideally (easier to parse) we shoudl get a list sorted by amount, then date
1362 
1363 	DB( g_print("\n[transaction] check duplicate\n") );
1364 
1365 
1366 
1367 	DB( g_print("\n - account:'%s'\n", acc->name) );
1368 
1369 	GTimer *t = g_timer_new();
1370 	g_print(" - start parse\n");
1371 
1372 
1373 	llast = g_list_last(old ope list);
1374 	DB( g_print("- end last : %f sec\n", g_timer_elapsed(t, NULL)) );
1375 	g_timer_reset(t);
1376 
1377 	ltxn = llast->data;
1378 	g_date_clear(&gd, 1);
1379 	g_date_set_julian(&gd, ltxn->date);
1380 	g_print(" - last julian=%u %02d-%02d-%04d\n", ltxn->date, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
1381 
1382 	minjulian = ltxn->date - (366*2);
1383 	g_date_clear(&gd, 1);
1384 	g_date_set_julian(&gd, minjulian);
1385 	g_print(" - min julian=%u %02d-%02d-%04d\n", minjulian, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
1386 
1387 	array = g_ptr_array_sized_new (25);
1388 
1389 	lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
1390 	while (lnk_txn != NULL)
1391 	{
1392 	Transaction *stxn = lnk_txn->data;
1393 
1394 		//if(stxn->date < minjulian)
1395 		//	break;
1396 		DB( g_print("------\n eval src: %d, '%s', '%s', %2.f\n", stxn->date, stxn->info, stxn->memo, stxn->amount) );
1397 
1398 		stxn->marker = 0;
1399 		list2 = g_list_previous(lnk_txn);
1400 		while (list2 != NULL)
1401 		{
1402 		Transaction *dtxn = list2->data;
1403 
1404 			stxn->marker = 0;
1405 			if( (dtxn->date + gapday) < (stxn->date + gapday) )
1406 				break;
1407 
1408 			DB( g_print(" + with dst: %d, '%s', '%s', %2.f\n", dtxn->date, dtxn->info, dtxn->memo, dtxn->amount) );
1409 
1410 			if( transaction_similar_match(stxn, dtxn, gapday) )
1411 			{
1412 				g_ptr_array_add (array, stxn);
1413 				g_ptr_array_add (array, dtxn);
1414 				nbdup++;
1415 				DB( g_print(" + dst=1 src=1\n") );
1416 			}
1417 
1418 			nball++;
1419 			list2 = g_list_previous(list2);
1420 		}
1421 
1422 		lnk_txn = g_list_previous(lnk_txn);
1423 	}
1424 
1425 	DB( g_print(" - end parse : %f sec\n", g_timer_elapsed(t, NULL)) );
1426 	DB( g_timer_destroy (t) );
1427 
1428 	for(i=0;i<array->len;i++)
1429 	{
1430 	Transaction *txn = g_ptr_array_index(array, i);
1431 		txn->marker = 1;
1432 	}
1433 
1434 	g_ptr_array_free(array, TRUE);
1435 
1436 	DB( g_print(" - found: %d/%d dup, %d xfer\n", nbdup, nball, nbxfer ) );
1437 
1438 }
1439 
1440 
1441 
1442 
1443 
1444 //todo: add a limitation, no need to go through all txn
1445 // 1 year in th past, or abolute number ?
1446 gint future_transaction_test_notification(void)
1447 {
1448 GList *lst_acc, *lnk_acc;
1449 
1450 	DB( g_print("\ntransaction_test_notification\n") );
1451 
1452 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
1453 	lnk_acc = g_list_first(lst_acc);
1454 	while (lnk_acc != NULL)
1455 	{
1456 	Account *acc = lnk_acc->data;
1457 
1458 		transaction_similar_mark(acc);
1459 
1460 		lnk_acc = g_list_next(lnk_acc);
1461 	}
1462 	g_list_free(lst_acc);
1463 
1464 	return 0;
1465 }
1466 */
1467 
1468 
1469 
1470