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