]> Dogcows Code - chaz/homebank/blob - src/hb-transaction.c
Merge branch 'master' into ext-perl
[chaz/homebank] / src / hb-transaction.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2017 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 #include "ext.h"
27 #include "refcount.h"
28
29 /****************************************************************************/
30 /* Debug macros */
31 /****************************************************************************/
32 #define MYDEBUG 0
33
34 #if MYDEBUG
35 #define DB(x) (x);
36 #else
37 #define DB(x);
38 #endif
39
40 /* our global datas */
41 extern struct HomeBank *GLOBALS;
42 extern struct Preferences *PREFS;
43
44
45 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
46
47 void
48 da_transaction_clean(Transaction *item)
49 {
50 if(item != NULL)
51 {
52 if(item->wording != NULL)
53 {
54 g_free(item->wording);
55 item->wording = NULL;
56 }
57 if(item->info != NULL)
58 {
59 g_free(item->info);
60 item->info = NULL;
61 }
62 if(item->tags != NULL)
63 {
64 DB( g_print(" -> item->tags %p\n", item->tags) );
65 g_free(item->tags);
66 item->tags = NULL;
67 }
68
69 da_splits_free(item->splits);
70 item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
71
72 if(item->same != NULL)
73 {
74 g_list_free(item->same);
75 item->same = NULL;
76 }
77 }
78 }
79
80
81
82 void
83 da_transaction_free(Transaction *item)
84 {
85 if(rc_unref(item))
86 {
87 da_transaction_clean(item);
88 rc_free(item);
89 }
90 }
91
92
93 Transaction *
94 da_transaction_malloc(void)
95 {
96 return rc_alloc(sizeof(Transaction));
97 }
98
99
100 Transaction *da_transaction_copy(Transaction *src_txn, Transaction *dst_txn)
101 {
102 DB( g_print("da_transaction_copy\n") );
103
104 da_transaction_clean (dst_txn);
105
106 memmove(dst_txn, src_txn, sizeof(Transaction));
107
108 //duplicate the string
109 dst_txn->wording = g_strdup(src_txn->wording);
110 dst_txn->info = g_strdup(src_txn->info);
111
112 //duplicate tags
113 transaction_tags_clone(src_txn, dst_txn);
114
115 if (da_splits_clone(src_txn->splits, dst_txn->splits) > 0)
116 dst_txn->flags |= OF_SPLIT; //Flag that Splits are active
117
118 return dst_txn;
119 }
120
121
122 Transaction *da_transaction_init_from_template(Transaction *txn, Archive *arc)
123 {
124 //txn->date = 0;
125 txn->amount = arc->amount;
126 //#1258344 keep the current account if tpl is empty
127 if(arc->kacc)
128 txn->kacc = arc->kacc;
129 txn->paymode = arc->paymode;
130 txn->flags = arc->flags | OF_ADDED;
131 txn->status = arc->status;
132 txn->kpay = arc->kpay;
133 txn->kcat = arc->kcat;
134 txn->kxferacc = arc->kxferacc;
135 txn->wording = g_strdup(arc->wording);
136 txn->info = NULL;
137 if( da_splits_clone(arc->splits, txn->splits) > 0)
138 txn->flags |= OF_SPLIT; //Flag that Splits are active
139
140 return txn;
141 }
142
143
144 Transaction *da_transaction_clone(Transaction *src_item)
145 {
146 Transaction *new_item = rc_dup(src_item, sizeof(Transaction));
147
148 DB( g_print("da_transaction_clone\n") );
149
150 if(new_item)
151 {
152 //duplicate the string
153 new_item->wording = g_strdup(src_item->wording);
154 new_item->info = g_strdup(src_item->info);
155
156 //duplicate tags
157 transaction_tags_clone(src_item, new_item);
158
159 if( da_splits_clone(src_item->splits, new_item->splits) > 0)
160 new_item->flags |= OF_SPLIT; //Flag that Splits are active
161
162 }
163 return new_item;
164 }
165
166
167 GList *
168 da_transaction_new(void)
169 {
170 return NULL;
171 }
172
173
174 guint
175 da_transaction_length(void)
176 {
177 GList *lst_acc, *lnk_acc;
178 guint count = 0;
179
180 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
181 lnk_acc = g_list_first(lst_acc);
182 while (lnk_acc != NULL)
183 {
184 Account *acc = lnk_acc->data;
185
186 count += g_queue_get_length (acc->txn_queue);
187 lnk_acc = g_list_next(lnk_acc);
188 }
189 g_list_free(lst_acc);
190 return count;
191 }
192
193
194 static void da_transaction_queue_free_ghfunc(Transaction *item, gpointer data)
195 {
196 da_transaction_free (item);
197 }
198
199
200 void da_transaction_destroy(void)
201 {
202 GList *lacc, *list;
203
204 lacc = g_hash_table_get_values(GLOBALS->h_acc);
205 list = g_list_first(lacc);
206 while (list != NULL)
207 {
208 Account *acc = list->data;
209
210 g_queue_foreach(acc->txn_queue, (GFunc)da_transaction_queue_free_ghfunc, NULL);
211 list = g_list_next(list);
212 }
213 g_list_free(lacc);
214 }
215
216
217 static gint da_transaction_compare_datafunc(Transaction *a, Transaction *b, gpointer data)
218 {
219 return ((gint)a->date - b->date);
220 }
221
222
223 void da_transaction_queue_sort(GQueue *queue)
224 {
225 g_queue_sort(queue, (GCompareDataFunc)da_transaction_compare_datafunc, NULL);
226 }
227
228
229 static gint da_transaction_compare_func(Transaction *a, Transaction *b)
230 {
231 return ((gint)a->date - b->date);
232 }
233
234
235 GList *da_transaction_sort(GList *list)
236 {
237 return( g_list_sort(list, (GCompareFunc)da_transaction_compare_func));
238 }
239
240
241 static void da_transaction_insert_memo(Transaction *item)
242 {
243 // append the memo if new
244 if( item->wording != NULL )
245 {
246 if( g_hash_table_lookup(GLOBALS->h_memo, item->wording) == NULL )
247 {
248 g_hash_table_insert(GLOBALS->h_memo, g_strdup(item->wording), NULL);
249 }
250 }
251 }
252
253
254 gboolean da_transaction_insert_sorted(Transaction *newitem)
255 {
256 Account *acc;
257 GList *lnk_txn;
258
259 acc = da_acc_get(newitem->kacc);
260 if(!acc)
261 return FALSE;
262
263 lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
264 while (lnk_txn != NULL)
265 {
266 Transaction *item = lnk_txn->data;
267
268 if(item->date <= newitem->date)
269 break;
270
271 lnk_txn = g_list_previous(lnk_txn);
272 }
273
274 // we're at insert point, insert after txn
275 g_queue_insert_after(acc->txn_queue, lnk_txn, newitem);
276
277 da_transaction_insert_memo(newitem);
278 return TRUE;
279 }
280
281
282 // nota: this is called only when loading xml file
283 gboolean da_transaction_prepend(Transaction *item)
284 {
285 Account *acc;
286
287 acc = da_acc_get(item->kacc);
288 if(acc)
289 item->kcur = acc->kcur;
290 g_queue_push_tail(acc->txn_queue, item);
291 da_transaction_insert_memo(item);
292 return TRUE;
293 }
294
295
296 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
297
298 static guint32
299 da_transaction_get_max_kxfer(void)
300 {
301 GList *lst_acc, *lnk_acc;
302 GList *list;
303 guint32 max_key = 0;
304
305 DB( g_print("da_transaction_get_max_kxfer\n") );
306
307 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
308 lnk_acc = g_list_first(lst_acc);
309 while (lnk_acc != NULL)
310 {
311 Account *acc = lnk_acc->data;
312
313 list = g_queue_peek_head_link(acc->txn_queue);
314 while (list != NULL)
315 {
316 Transaction *item = list->data;
317
318 if( item->paymode == PAYMODE_INTXFER )
319 {
320 max_key = MAX(max_key, item->kxfer);
321 }
322 list = g_list_next(list);
323 }
324
325 lnk_acc = g_list_next(lnk_acc);
326 }
327 g_list_free(lst_acc);
328
329 DB( g_print(" max_key : %d \n", max_key) );
330
331 return max_key;
332 }
333
334
335 static void da_transaction_goto_orphan(Transaction *txn)
336 {
337 const gchar *oatn = "orphaned transactions";
338 Account *acc;
339
340 acc = da_acc_get_by_name((gchar *)oatn);
341 if(acc == NULL)
342 {
343 acc = da_acc_malloc();
344 acc->name = g_strdup(oatn);
345 da_acc_append(acc);
346 }
347 txn->kacc = acc->key;
348 }
349
350
351 void da_transaction_consistency(Transaction *item)
352 {
353 Account *acc;
354 Category *cat;
355 Payee *pay;
356 gint nbsplit;
357
358 // ensure date is between range
359 item->date = CLAMP(item->date, HB_MINDATE, HB_MAXDATE);
360
361 // check account exists
362 acc = da_acc_get(item->kacc);
363 if(acc == NULL)
364 {
365 g_warning("txn consistency: fixed invalid acc %d", item->kacc);
366 da_transaction_goto_orphan(item);
367 GLOBALS->changes_count++;
368 }
369
370 // check category exists
371 cat = da_cat_get(item->kcat);
372 if(cat == NULL)
373 {
374 g_warning("txn consistency: fixed invalid cat %d", item->kcat);
375 item->kcat = 0;
376 GLOBALS->changes_count++;
377 }
378
379 // check split category #1340142
380 split_cat_consistency(item->splits);
381
382 //# 1416624 empty category when split
383 nbsplit = da_splits_count(item->splits);
384 if(nbsplit > 0 && item->kcat > 0)
385 {
386 g_warning("txn consistency: fixed invalid cat on split txn");
387 item->kcat = 0;
388 GLOBALS->changes_count++;
389 }
390
391 // check payee exists
392 pay = da_pay_get(item->kpay);
393 if(pay == NULL)
394 {
395 g_warning("txn consistency: fixed invalid pay %d", item->kpay);
396 item->kpay = 0;
397 GLOBALS->changes_count++;
398 }
399
400 // reset dst acc for non xfer transaction
401 if( item->paymode != PAYMODE_INTXFER )
402 item->kxferacc = 0;
403
404 //#1628678 tags for internal xfer should be checked as well
405
406
407 //#1295877 ensure income flag is correctly set
408 item->flags &= ~(OF_INCOME);
409 if( item->amount > 0)
410 item->flags |= (OF_INCOME);
411
412 //#1308745 ensure remind flag unset if reconciled
413 //useless since 5.0
414 //if( item->flags & OF_VALID )
415 // item->flags &= ~(OF_REMIND);
416
417 }
418
419
420 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
421 /* new transfer functions */
422
423 static void transaction_xfer_create_child(Transaction *ope)
424 {
425 Transaction *child;
426 Account *acc;
427 gchar swap;
428
429 DB( g_print("\n[transaction] xfer_create_child\n") );
430
431 if( ope->kxferacc > 0 )
432 {
433 child = da_transaction_clone(ope);
434
435 ope->flags |= OF_CHANGED;
436 child->flags |= OF_ADDED;
437
438 child->amount = -child->amount;
439 child->flags ^= (OF_INCOME); // invert flag
440 //#1268026
441 child->status = TXN_STATUS_NONE;
442 //child->flags &= ~(OF_VALID); // delete reconcile state
443
444 swap = child->kacc;
445 child->kacc = child->kxferacc;
446 child->kxferacc = swap;
447
448 /* update acc flags */
449 acc = da_acc_get( child->kacc );
450 if(acc != NULL)
451 {
452 acc->flags |= AF_ADDED;
453
454 //strong link
455 guint maxkey = da_transaction_get_max_kxfer();
456
457 DB( g_print(" + maxkey is %d\n", maxkey) );
458
459
460 ope->kxfer = maxkey+1;
461 child->kxfer = maxkey+1;
462
463 DB( g_print(" + strong link to %d\n", ope->kxfer) );
464
465
466 DB( g_print(" + add transfer, %p to acc %d\n", child, acc->key) );
467
468 da_transaction_insert_sorted(child);
469
470 account_balances_add (child);
471
472 GValue txn_value = G_VALUE_INIT;
473 ext_hook("transaction_inserted", EXT_TRANSACTION(&txn_value, child), NULL);
474
475 }
476 }
477
478 }
479
480
481 //todo: add strong control and extend to payee, maybe memo ?
482 static gboolean transaction_xfer_child_might(Transaction *stxn, Transaction *dtxn, gint daygap)
483 {
484 gboolean retval = FALSE;
485
486 //DB( g_print("\n[transaction] xfer_child_might\n") );
487
488 if(stxn == dtxn)
489 return FALSE;
490
491 /*g_print("test\n");
492
493 g_print(" %d %d %d %f %d\n",
494 stxn->kcur, stxn->date, stxn->kacc, ABS(stxn->amount), stxn->kxfer );
495
496
497 g_print(" %d %d %d %f %d\n",
498 dtxn->kcur, dtxn->date, dtxn->kacc, ABS(dtxn->amount), dtxn->kxfer );
499 */
500
501 if( stxn->kcur == dtxn->kcur &&
502 stxn->date == dtxn->date &&
503 //v5.1 make no sense: stxn->kxferacc == dtxn->kacc &&
504 stxn->kacc != dtxn->kacc &&
505 ABS(stxn->amount) == ABS(dtxn->amount) &&
506 dtxn->kxfer == 0)
507 {
508 retval = TRUE;
509 }
510
511 //g_print(" return %d\n", retval);
512 return retval;
513 }
514
515
516 static GList *transaction_xfer_child_might_list_get(Transaction *ope)
517 {
518 GList *lst_acc, *lnk_acc;
519 GList *list, *matchlist = NULL;
520
521 //DB( g_print("\n[transaction] xfer_child_might_list_get\n") );
522
523 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
524 lnk_acc = g_list_first(lst_acc);
525 while (lnk_acc != NULL)
526 {
527 Account *acc = lnk_acc->data;
528
529 if( !(acc->flags & AF_CLOSED) && (acc->key != ope->kacc) )
530 {
531 list = g_queue_peek_tail_link(acc->txn_queue);
532 while (list != NULL)
533 {
534 Transaction *item = list->data;
535
536 // no need to go higher than src txn date
537 if(item->date < ope->date)
538 break;
539
540 if( transaction_xfer_child_might(ope, item, 0) == TRUE )
541 {
542 //DB( g_print(" - match : %d %s %f %d=>%d\n", item->date, item->wording, item->amount, item->account, item->kxferacc) );
543 matchlist = g_list_append(matchlist, item);
544 }
545 list = g_list_previous(list);
546 }
547 }
548
549 lnk_acc = g_list_next(lnk_acc);
550 }
551 g_list_free(lst_acc);
552
553 return matchlist;
554 }
555
556
557 void transaction_xfer_search_or_add_child(GtkWindow *parentwindow, Transaction *ope, gboolean manual)
558 {
559 GList *matchlist;
560 gint count;
561
562 DB( g_print("\n[transaction] xfer_search_or_add_child\n") );
563
564 matchlist = transaction_xfer_child_might_list_get(ope);
565 count = g_list_length(matchlist);
566
567 DB( g_print(" - found %d might match, switching\n", count) );
568
569 switch(count)
570 {
571 case 0: //we should create the child
572 transaction_xfer_create_child(ope);
573 break;
574
575 //todo: maybe with just 1 match the user must choose ?
576 //#942346: bad idea so to no let the user confirm, so let hil confirm
577 /*
578 case 1: //transform the transaction to a child transfer
579 {
580 GList *list = g_list_first(matchlist);
581 transaction_xfer_change_to_child(ope, list->data);
582 break;
583 }
584 */
585
586 default: //the user must choose himself
587 {
588 Transaction *child;
589
590 child = ui_dialog_transaction_xfer_select_child(ope, matchlist);
591 if(child == NULL)
592 transaction_xfer_create_child(ope);
593 else
594 transaction_xfer_change_to_child(ope, child);
595 }
596 }
597
598 g_list_free(matchlist);
599 }
600
601
602 Transaction *transaction_xfer_child_strong_get(Transaction *src)
603 {
604 Account *dstacc;
605 GList *list;
606
607 DB( g_print("\n[transaction] xfer_child_strong_get\n") );
608
609 dstacc = da_acc_get(src->kxferacc);
610 if( !dstacc || src->kxfer <= 0 )
611 return NULL;
612
613 DB( g_print(" - search: %d %s %f %d=>%d - %d\n", src->date, src->wording, src->amount, src->kacc, src->kxferacc, src->kxfer) );
614
615 list = g_queue_peek_tail_link(dstacc->txn_queue);
616 while (list != NULL)
617 {
618 Transaction *item = list->data;
619
620 //#1252230
621 //if( item->paymode == PAYMODE_INTXFER
622 // && item->kacc == src->kxferacc
623 // && item->kxfer == src->kxfer )
624 if( item->paymode == PAYMODE_INTXFER
625 && item->kxfer == src->kxfer
626 && item != src )
627 {
628 DB( g_print(" - found : %d %s %f %d=>%d - %d\n", item->date, item->wording, item->amount, item->kacc, item->kxferacc, src->kxfer) );
629 return item;
630 }
631 list = g_list_previous(list);
632 }
633
634 DB( g_print(" - not found...\n") );
635 return NULL;
636 }
637
638
639
640
641 void transaction_xfer_change_to_child(Transaction *ope, Transaction *child)
642 {
643 Account *dstacc;
644
645 DB( g_print("\n[transaction] xfer_change_to_child\n") );
646
647 if(ope->kcur != child->kcur)
648 return;
649
650 ope->flags |= OF_CHANGED;
651 child->flags |= OF_CHANGED;
652
653 child->paymode = PAYMODE_INTXFER;
654
655 ope->kxferacc = child->kacc;
656 child->kxferacc = ope->kacc;
657
658 /* update acc flags */
659 dstacc = da_acc_get( child->kacc);
660 if(dstacc != NULL)
661 dstacc->flags |= AF_CHANGED;
662
663 //strong link
664 guint maxkey = da_transaction_get_max_kxfer();
665 ope->kxfer = maxkey+1;
666 child->kxfer = maxkey+1;
667
668 }
669
670
671 void transaction_xfer_sync_child(Transaction *s_txn, Transaction *child)
672 {
673
674 DB( g_print("\n[transaction] xfer_sync_child\n") );
675
676 account_balances_sub (child);
677
678 child->date = s_txn->date;
679 child->amount = -s_txn->amount;
680 child->flags = child->flags | OF_CHANGED;
681 //#1295877
682 child->flags &= ~(OF_INCOME);
683 if( child->amount > 0)
684 child->flags |= (OF_INCOME);
685 child->kpay = s_txn->kpay;
686 child->kcat = s_txn->kcat;
687 if(child->wording)
688 g_free(child->wording);
689 child->wording = g_strdup(s_txn->wording);
690 if(child->info)
691 g_free(child->info);
692 child->info = g_strdup(s_txn->info);
693
694 //#1252230 sync account also
695 child->kacc = s_txn->kxferacc;
696 child->kxferacc = s_txn->kacc;
697
698 account_balances_add (child);
699
700 //synchronise tags since 5.1
701 if(child->tags)
702 g_free(child->tags);
703 transaction_tags_clone (s_txn, child);
704
705 }
706
707
708 void transaction_xfer_remove_child(Transaction *src)
709 {
710 Transaction *dst;
711
712 DB( g_print("\n[transaction] xfer_remove_child\n") );
713
714 dst = transaction_xfer_child_strong_get( src );
715
716 DB( g_print(" -> return is %s, %p\n", dst->wording, dst) );
717
718 if( dst != NULL )
719 {
720 Account *acc = da_acc_get(dst->kacc);
721
722 DB( g_print("deleting...") );
723 src->kxfer = 0;
724 src->kxferacc = 0;
725 account_balances_sub(dst);
726 g_queue_remove(acc->txn_queue, dst);
727 da_transaction_free (dst);
728 }
729 }
730
731
732 // still useful for upgrade from < file v0.6 (hb v4.4 kxfer)
733 Transaction *transaction_old_get_child_transfer(Transaction *src)
734 {
735 Account *acc;
736 GList *list;
737
738 DB( g_print("\n[transaction] get_child_transfer\n") );
739
740 //DB( g_print(" search: %d %s %f %d=>%d\n", src->date, src->wording, src->amount, src->account, src->kxferacc) );
741 acc = da_acc_get(src->kxferacc);
742
743 list = g_queue_peek_head_link(acc->txn_queue);
744 while (list != NULL)
745 {
746 Transaction *item = list->data;
747
748 // no need to go higher than src txn date
749 if(item->date > src->date)
750 break;
751
752 if( item->paymode == PAYMODE_INTXFER)
753 {
754 if( src->date == item->date &&
755 src->kacc == item->kxferacc &&
756 src->kxferacc == item->kacc &&
757 ABS(src->amount) == ABS(item->amount) )
758 {
759 //DB( g_print(" found : %d %s %f %d=>%d\n", item->date, item->wording, item->amount, item->account, item->kxferacc) );
760
761 return item;
762 }
763 }
764 list = g_list_next(list);
765 }
766
767 DB( g_print(" not found...\n") );
768
769 return NULL;
770 }
771
772
773 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
774
775
776 void transaction_add(Transaction *ope, GtkWidget *treeview, guint32 accnum)
777 {
778 Transaction *newope;
779 Account *acc;
780
781 DB( g_print("\n[transaction] transaction_add\n") );
782
783 //controls accounts valid (archive scheduled maybe bad)
784 acc = da_acc_get(ope->kacc);
785 if(acc == NULL) return;
786
787 DB( g_print(" acc is '%s' %d\n", acc->name, acc->key) );
788
789 ope->kcur = acc->kcur;
790
791 if(ope->paymode == PAYMODE_INTXFER)
792 {
793 acc = da_acc_get(ope->kxferacc);
794 if(acc == NULL) return;
795
796 // delete any splits
797 da_splits_free(ope->splits);
798 ope->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
799 }
800
801
802 //allocate a new entry and copy from our edited structure
803 newope = da_transaction_clone(ope);
804
805 //init flag and keep remind status
806 // already done in deftransaction_get
807 //ope->flags |= (OF_ADDED);
808 //remind = (ope->flags & OF_REMIND) ? TRUE : FALSE;
809 //ope->flags &= (~OF_REMIND);
810
811 /* cheque number is already stored in deftransaction_get */
812 /* todo:move this to transaction add
813 store a new cheque number into account ? */
814
815 if( (newope->paymode == PAYMODE_CHECK) && (newope->info) && !(newope->flags & OF_INCOME) )
816 {
817 guint cheque;
818
819 /* get the active account and the corresponding cheque number */
820 acc = da_acc_get( newope->kacc);
821 cheque = atol(newope->info);
822
823 //DB( g_print(" -> should store cheque number %d to %d", cheque, newope->account) );
824 if( newope->flags & OF_CHEQ2 )
825 {
826 acc->cheque2 = MAX(acc->cheque2, cheque);
827 }
828 else
829 {
830 acc->cheque1 = MAX(acc->cheque1, cheque);
831 }
832 }
833
834 /* add normal transaction */
835 acc = da_acc_get( newope->kacc);
836 if(acc != NULL)
837 {
838 acc->flags |= AF_ADDED;
839
840 DB( g_print(" + add normal %p to acc %d\n", newope, acc->key) );
841 //da_transaction_append(newope);
842 da_transaction_insert_sorted(newope);
843
844 if(treeview != NULL)
845 transaction_add_treeview(newope, treeview, accnum);
846
847 account_balances_add(newope);
848
849 if(newope->paymode == PAYMODE_INTXFER)
850 {
851 transaction_xfer_search_or_add_child(NULL, newope, FALSE);
852 }
853
854 GValue txn_value = G_VALUE_INIT;
855 ext_hook("transaction_inserted", EXT_TRANSACTION(&txn_value, newope), NULL);
856 }
857 }
858
859
860 void transaction_add_treeview(Transaction *ope, GtkWidget *treeview, guint32 accnum)
861 {
862 GtkTreeModel *model;
863 GtkTreeIter iter;
864 //GtkTreePath *path;
865 //GtkTreeSelection *sel;
866
867 DB( g_print("\n[transaction] add_treeview\n") );
868
869 if(ope->kacc == accnum)
870 {
871 model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
872 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
873
874 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
875 LST_DSPOPE_DATAS, ope,
876 -1);
877
878 //activate that new line
879 //path = gtk_tree_model_get_path(model, &iter);
880 //gtk_tree_view_expand_to_path(GTK_TREE_VIEW(treeview), path);
881
882 //sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
883 //gtk_tree_selection_select_iter(sel, &iter);
884
885 //gtk_tree_path_free(path);
886
887 }
888 }
889
890
891 gboolean transaction_acc_move(Transaction *txn, guint32 okacc, guint32 nkacc)
892 {
893 Account *oacc, *nacc;
894
895 DB( g_print("\n[transaction] acc_move\n") );
896
897 oacc = da_acc_get(okacc);
898 nacc = da_acc_get(nkacc);
899 if( oacc && nacc )
900 {
901 if( g_queue_remove(oacc->txn_queue, txn) )
902 {
903 g_queue_push_tail(nacc->txn_queue, txn);
904 txn->kacc = nacc->key;
905 txn->kcur = nacc->kcur;
906 nacc->flags |= AF_CHANGED;
907 return TRUE;
908 }
909 else
910 //ensure to keep txn into current account
911 txn->kacc = okacc;
912 }
913 return FALSE;
914 }
915
916
917 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
918
919
920 static gboolean misc_text_match(gchar *text, gchar *searchtext, gboolean exact)
921 {
922 gboolean match = FALSE;
923
924 if(text == NULL)
925 return FALSE;
926
927 //DB( g_print("search %s in %s\n", rul->name, ope->wording) );
928 if( searchtext != NULL )
929 {
930 if( exact == TRUE )
931 {
932 if( g_strrstr(text, searchtext) != NULL )
933 {
934 DB( g_print(" found case '%s'\n", searchtext) );
935 match = TRUE;
936 }
937 }
938 else
939 {
940 gchar *word = g_utf8_casefold(text, -1);
941 gchar *needle = g_utf8_casefold(searchtext, -1);
942
943 if( g_strrstr(word, needle) != NULL )
944 {
945 DB( g_print(" found nocase '%s'\n", searchtext) );
946 match = TRUE;
947 }
948 g_free(word);
949 g_free(needle);
950 }
951 }
952
953 return match;
954 }
955
956 static gboolean misc_regex_match(gchar *text, gchar *searchtext, gboolean exact)
957 {
958 gboolean match = FALSE;
959
960 if(text == NULL)
961 return FALSE;
962
963 DB( g_print("- match RE %s in %s\n", searchtext, text) );
964 if( searchtext != NULL )
965 {
966 match = g_regex_match_simple(searchtext, text, ((exact == TRUE)?0:G_REGEX_CASELESS) | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY );
967 if (match == TRUE) { DB( g_print(" found pattern '%s'\n", searchtext) ); }
968 }
969 return match;
970 }
971
972
973 static Assign *transaction_auto_assign_eval_txn(GList *l_rul, Transaction *txn)
974 {
975 Assign *rule = NULL;
976 GList *list;
977
978 DB( g_print("\n[transaction] auto_assign_eval_txn\n") );
979
980 DB( g_print("- eval every rules, and return the last that match\n") );
981
982 list = g_list_first(l_rul);
983 while (list != NULL)
984 {
985 Assign *rul = list->data;
986 gchar *text;
987
988 text = txn->wording;
989 if(rul->field == 1) //payee
990 {
991 Payee *pay = da_pay_get(txn->kpay);
992 if(pay)
993 text = pay->name;
994 }
995
996 if( !(rul->flags & ASGF_REGEX) )
997 {
998 if( misc_text_match(text, rul->text, rul->flags & ASGF_EXACT) )
999 rule = rul;
1000 }
1001 else
1002 {
1003 if( misc_regex_match(text, rul->text, rul->flags & ASGF_EXACT) )
1004 rule = rul;
1005 }
1006
1007 list = g_list_next(list);
1008 }
1009
1010 return rule;
1011 }
1012
1013
1014 static Assign *transaction_auto_assign_eval(GList *l_rul, gchar *text)
1015 {
1016 Assign *rule = NULL;
1017 GList *list;
1018
1019 DB( g_print("\n[transaction] auto_assign_eval\n") );
1020
1021 DB( g_print("- eval every rules, and return the last that match\n") );
1022
1023 list = g_list_first(l_rul);
1024 while (list != NULL)
1025 {
1026 Assign *rul = list->data;
1027
1028 if( rul->field == 0 ) //memo
1029 {
1030 if( !(rul->flags & ASGF_REGEX) )
1031 {
1032 if( misc_text_match(text, rul->text, rul->flags & ASGF_EXACT) )
1033 rule = rul;
1034 }
1035 else
1036 {
1037 if( misc_regex_match(text, rul->text, rul->flags & ASGF_EXACT) )
1038 rule = rul;
1039 }
1040 }
1041 list = g_list_next(list);
1042 }
1043
1044 return rule;
1045 }
1046
1047
1048 gint transaction_auto_assign(GList *ope_list, guint32 kacc)
1049 {
1050 GList *l_ope;
1051 GList *l_rul;
1052 gint changes = 0;
1053
1054 DB( g_print("\n[transaction] auto_assign\n") );
1055
1056 l_rul = g_hash_table_get_values(GLOBALS->h_rul);
1057
1058 l_ope = g_list_first(ope_list);
1059 while (l_ope != NULL)
1060 {
1061 Transaction *ope = l_ope->data;
1062 gboolean changed = FALSE;
1063
1064 DB( g_print("- eval ope '%s' : acc=%d, pay=%d, cat=%d\n", ope->wording, ope->kacc, ope->kpay, ope->kcat) );
1065
1066 //#1215521: added kacc == 0
1067 if( (kacc == ope->kacc || kacc == 0) )
1068 {
1069 Assign *rul;
1070
1071 rul = transaction_auto_assign_eval_txn(l_rul, ope);
1072 if( rul != NULL )
1073 {
1074 if( (ope->kpay == 0 && (rul->flags & ASGF_DOPAY)) || (rul->flags & ASGF_OVWPAY) )
1075 {
1076 if(ope->kpay != rul->kpay) { changed = TRUE; }
1077 ope->kpay = rul->kpay;
1078 }
1079
1080 if( !(ope->flags & OF_SPLIT) )
1081 {
1082 if( (ope->kcat == 0 && (rul->flags & ASGF_DOCAT)) || (rul->flags & ASGF_OVWCAT) )
1083 {
1084 if(ope->kcat != rul->kcat) { changed = TRUE; }
1085 ope->kcat = rul->kcat;
1086 }
1087 }
1088
1089 if( (ope->paymode == 0 && (rul->flags & ASGF_DOMOD)) || (rul->flags & ASGF_OVWMOD) )
1090 {
1091 //ugly hack - don't allow modify intxfer
1092 if(ope->paymode != PAYMODE_INTXFER && rul->paymode != PAYMODE_INTXFER)
1093 {
1094 if(ope->paymode != rul->paymode) { changed = TRUE; }
1095 ope->paymode = rul->paymode;
1096 }
1097 }
1098
1099 }
1100
1101 if( ope->flags & OF_SPLIT )
1102 {
1103 guint i, nbsplit = da_splits_count(ope->splits);
1104
1105 for(i=0;i<nbsplit;i++)
1106 {
1107 Split *split = ope->splits[i];
1108
1109 DB( g_print("- eval split '%s'\n", split->memo) );
1110
1111 rul = transaction_auto_assign_eval(l_rul, split->memo);
1112 if( rul != NULL )
1113 {
1114 //#1501144: check if user wants to set category in rule
1115 if( (split->kcat == 0 || (rul->flags & ASGF_OVWCAT)) && (rul->flags & ASGF_DOCAT) )
1116 {
1117 if(split->kcat != rul->kcat) { changed = TRUE; }
1118 split->kcat = rul->kcat;
1119 }
1120 }
1121 }
1122 }
1123
1124 if(changed == TRUE)
1125 {
1126 ope->flags |= OF_CHANGED;
1127 changes++;
1128 }
1129 }
1130
1131 l_ope = g_list_next(l_ope);
1132 }
1133
1134 g_list_free(l_rul);
1135
1136 return changes;
1137 }
1138
1139
1140 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1141
1142
1143 guint
1144 transaction_tags_count(Transaction *ope)
1145 {
1146 guint count = 0;
1147 guint32 *ptr = ope->tags;
1148
1149 //DB( g_print("\n[transaction] tags_count\n") );
1150
1151 if( ope->tags == NULL )
1152 return 0;
1153
1154 while(*ptr++ != 0 && count < 32)
1155 count++;
1156
1157 return count;
1158 }
1159
1160
1161 void transaction_tags_clone(Transaction *src_txn, Transaction *dst_txn)
1162 {
1163 guint count;
1164
1165 dst_txn->tags = NULL;
1166 count = transaction_tags_count(src_txn);
1167 if(count > 0)
1168 {
1169 //1501962: we must also copy the final 0
1170 dst_txn->tags = g_memdup(src_txn->tags, (count+1)*sizeof(guint32));
1171 }
1172 }
1173
1174 guint
1175 transaction_tags_parse(Transaction *ope, const gchar *tagstring)
1176 {
1177 gchar **str_array;
1178 guint count, i;
1179 Tag *tag;
1180
1181 DB( g_print("\n[transaction] tags_parse\n") );
1182
1183 DB( g_print(" - tagstring='%s'\n", tagstring) );
1184
1185 str_array = g_strsplit (tagstring, " ", 0);
1186 count = g_strv_length( str_array );
1187
1188 g_free(ope->tags);
1189 ope->tags = NULL;
1190
1191 DB( g_print(" -> reset storage %p\n", ope->tags) );
1192
1193
1194 if( count > 0 )
1195 {
1196
1197 ope->tags = g_new0(guint32, count + 1);
1198
1199 DB( g_print(" -> storage %p\n", ope->tags) );
1200
1201 for(i=0;i<count;i++)
1202 {
1203 tag = da_tag_get_by_name(str_array[i]);
1204 if(tag == NULL)
1205 {
1206 Tag *newtag = da_tag_malloc();
1207
1208 newtag->name = g_strdup(str_array[i]);
1209 da_tag_append(newtag);
1210 tag = da_tag_get_by_name(str_array[i]);
1211 }
1212
1213 DB( g_print(" -> storing %d=>%s at tags pos %d\n", tag->key, tag->name, i) );
1214
1215 ope->tags[i] = tag->key;
1216 }
1217 ope->tags[i] = 0;
1218 }
1219
1220 //hex_dump(ope->tags, sizeof(guint32*)*count+1);
1221
1222 g_strfreev (str_array);
1223
1224 return count;
1225 }
1226
1227 gchar *
1228 transaction_tags_tostring(Transaction *ope)
1229 {
1230 guint count, i;
1231 gchar **str_array;
1232 gchar *tagstring;
1233 Tag *tag;
1234
1235 DB( g_print("\n[transaction] tags_tostring\n") );
1236
1237 DB( g_print(" -> tags at=%p\n", ope->tags) );
1238
1239 if( ope->tags == NULL )
1240 {
1241
1242 return NULL;
1243 }
1244 else
1245 {
1246 count = transaction_tags_count(ope);
1247
1248 DB( g_print(" -> tags at=%p, nbtags=%d\n", ope->tags, count) );
1249
1250 str_array = g_new0(gchar*, count+1);
1251
1252 DB( g_print(" -> str_array at %p\n", str_array) );
1253
1254 //hex_dump(ope->tags, sizeof(guint32*)*(count+1));
1255
1256 for(i=0;i<count;i++)
1257 {
1258 DB( g_print(" -> try to get tag %d\n", ope->tags[i]) );
1259
1260 tag = da_tag_get(ope->tags[i]);
1261 if( tag )
1262 {
1263 DB( g_print(" -> get %s at %d\n", tag->name, i) );
1264 str_array[i] = tag->name;
1265 }
1266 else
1267 str_array[i] = NULL;
1268
1269
1270 }
1271
1272 tagstring = g_strjoinv(" ", str_array);
1273
1274 g_free (str_array);
1275
1276 }
1277
1278 return tagstring;
1279 }
1280
This page took 0.087607 seconds and 4 git commands to generate.