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