]> Dogcows Code - chaz/homebank/blob - src/hb-transaction.c
import homebank-5.1.2
[chaz/homebank] / src / hb-transaction.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2016 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] 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\n", child) );
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 if(stxn == dtxn)
481 return FALSE;
482
483 /*g_print("test\n");
484
485 g_print(" %d %d %d %f %d\n",
486 stxn->kcur, stxn->date, stxn->kacc, ABS(stxn->amount), stxn->kxfer );
487
488
489 g_print(" %d %d %d %f %d\n",
490 dtxn->kcur, dtxn->date, dtxn->kacc, ABS(dtxn->amount), dtxn->kxfer );
491 */
492
493 if( stxn->kcur == dtxn->kcur &&
494 stxn->date == dtxn->date &&
495 //v5.1 make no sense: stxn->kxferacc == dtxn->kacc &&
496 stxn->kacc != dtxn->kacc &&
497 ABS(stxn->amount) == ABS(dtxn->amount) &&
498 dtxn->kxfer == 0)
499 {
500 retval = TRUE;
501 }
502
503 //g_print(" return %d\n", retval);
504 return retval;
505 }
506
507
508 static GList *transaction_xfer_child_might_list_get(Transaction *ope)
509 {
510 GList *lst_acc, *lnk_acc;
511 GList *list, *matchlist = NULL;
512
513 DB( g_print("\n[transaction]xfer_get_potential_child\n") );
514
515 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
516 lnk_acc = g_list_first(lst_acc);
517 while (lnk_acc != NULL)
518 {
519 Account *acc = lnk_acc->data;
520
521 if( !(acc->flags & AF_CLOSED) && (acc->key != ope->kacc) )
522 {
523 list = g_queue_peek_tail_link(acc->txn_queue);
524 while (list != NULL)
525 {
526 Transaction *item = list->data;
527
528 // no need to go higher than src txn date
529 if(item->date < ope->date)
530 break;
531
532 if( transaction_xfer_child_might(ope, item, 0) == TRUE )
533 {
534 //DB( g_print(" - match : %d %s %f %d=>%d\n", item->date, item->wording, item->amount, item->account, item->kxferacc) );
535 matchlist = g_list_append(matchlist, item);
536 }
537 list = g_list_previous(list);
538 }
539 }
540
541 lnk_acc = g_list_next(lnk_acc);
542 }
543 g_list_free(lst_acc);
544
545 return matchlist;
546 }
547
548
549 void transaction_xfer_search_or_add_child(GtkWindow *parentwindow, Transaction *ope, gboolean manual)
550 {
551 GList *matchlist;
552 gint count;
553
554 DB( g_print("\n[transaction] transaction_xfer_search_or_add_child\n") );
555
556 matchlist = transaction_xfer_child_might_list_get(ope);
557 count = g_list_length(matchlist);
558
559 DB( g_print(" - found result is %d, switching\n", count) );
560
561 switch(count)
562 {
563 case 0: //we should create the child
564 transaction_xfer_create_child(ope);
565 break;
566
567 //todo: maybe with just 1 match the user must choose ?
568 //#942346: bad idea so to no let the user confirm, so let hil confirm
569 /*
570 case 1: //transform the transaction to a child transfer
571 {
572 GList *list = g_list_first(matchlist);
573 transaction_xfer_change_to_child(ope, list->data);
574 break;
575 }
576 */
577
578 default: //the user must choose himself
579 {
580 Transaction *child;
581
582 child = ui_dialog_transaction_xfer_select_child(ope, matchlist);
583 if(child == NULL)
584 transaction_xfer_create_child(ope);
585 else
586 transaction_xfer_change_to_child(ope, child);
587 }
588 }
589
590 g_list_free(matchlist);
591 }
592
593
594 Transaction *transaction_xfer_child_strong_get(Transaction *src)
595 {
596 Account *dstacc;
597 GList *list;
598
599 DB( g_print("\n[transaction] transaction_xfer_child_strong_get\n") );
600
601 dstacc = da_acc_get(src->kxferacc);
602 if( !dstacc || src->kxfer <= 0 )
603 return NULL;
604
605 DB( g_print(" - search: %d %s %f %d=>%d - %d\n", src->date, src->wording, src->amount, src->kacc, src->kxferacc, src->kxfer) );
606
607 list = g_queue_peek_tail_link(dstacc->txn_queue);
608 while (list != NULL)
609 {
610 Transaction *item = list->data;
611
612 //#1252230
613 //if( item->paymode == PAYMODE_INTXFER
614 // && item->kacc == src->kxferacc
615 // && item->kxfer == src->kxfer )
616 if( item->paymode == PAYMODE_INTXFER
617 && item->kxfer == src->kxfer
618 && item != src )
619 {
620 DB( g_print(" - found : %d %s %f %d=>%d - %d\n", item->date, item->wording, item->amount, item->kacc, item->kxferacc, src->kxfer) );
621 return item;
622 }
623 list = g_list_previous(list);
624 }
625
626 DB( g_print(" - not found...\n") );
627 return NULL;
628 }
629
630
631
632
633 void transaction_xfer_change_to_child(Transaction *ope, Transaction *child)
634 {
635 Account *dstacc;
636
637 DB( g_print("\n[transaction] transaction_xfer_change_to_child\n") );
638
639 if(ope->kcur != child->kcur)
640 return;
641
642 ope->flags |= OF_CHANGED;
643 child->flags |= OF_CHANGED;
644
645 child->paymode = PAYMODE_INTXFER;
646
647 ope->kxferacc = child->kacc;
648 child->kxferacc = ope->kacc;
649
650 /* update acc flags */
651 dstacc = da_acc_get( child->kacc);
652 if(dstacc != NULL)
653 dstacc->flags |= AF_CHANGED;
654
655 //strong link
656 guint maxkey = da_transaction_get_max_kxfer();
657 ope->kxfer = maxkey+1;
658 child->kxfer = maxkey+1;
659
660 }
661
662
663 void transaction_xfer_sync_child(Transaction *s_txn, Transaction *child)
664 {
665
666 DB( g_print("\n[transaction] transaction_xfer_sync_child\n") );
667
668 account_balances_sub (child);
669
670 child->date = s_txn->date;
671 child->amount = -s_txn->amount;
672 child->flags = child->flags | OF_CHANGED;
673 //#1295877
674 child->flags &= ~(OF_INCOME);
675 if( child->amount > 0)
676 child->flags |= (OF_INCOME);
677 child->kpay = s_txn->kpay;
678 child->kcat = s_txn->kcat;
679 if(child->wording)
680 g_free(child->wording);
681 child->wording = g_strdup(s_txn->wording);
682 if(child->info)
683 g_free(child->info);
684 child->info = g_strdup(s_txn->info);
685
686 //#1252230 sync account also
687 child->kacc = s_txn->kxferacc;
688 child->kxferacc = s_txn->kacc;
689
690 account_balances_add (child);
691
692 //synchronise tags since 5.1
693 if(child->tags)
694 g_free(child->tags);
695 transaction_tags_clone (s_txn, child);
696
697 }
698
699
700 void transaction_xfer_remove_child(Transaction *src)
701 {
702 Transaction *dst;
703
704 DB( g_print("\n[transaction] transaction_xfer_remove_child\n") );
705
706 dst = transaction_xfer_child_strong_get( src );
707
708 DB( g_print(" -> return is %s, %p\n", dst->wording, dst) );
709
710 if( dst != NULL )
711 {
712 Account *acc = da_acc_get(dst->kacc);
713
714 DB( g_print("deleting...") );
715 src->kxfer = 0;
716 src->kxferacc = 0;
717 account_balances_sub(dst);
718 g_queue_remove(acc->txn_queue, dst);
719 da_transaction_free (dst);
720 }
721 }
722
723
724 // still useful for upgrade from < file v0.6 (hb v4.4 kxfer)
725 Transaction *transaction_old_get_child_transfer(Transaction *src)
726 {
727 Account *acc;
728 GList *list;
729
730 DB( g_print("\n[transaction] transaction_get_child_transfer\n") );
731
732 //DB( g_print(" search: %d %s %f %d=>%d\n", src->date, src->wording, src->amount, src->account, src->kxferacc) );
733 acc = da_acc_get(src->kxferacc);
734
735 list = g_queue_peek_head_link(acc->txn_queue);
736 while (list != NULL)
737 {
738 Transaction *item = list->data;
739
740 // no need to go higher than src txn date
741 if(item->date > src->date)
742 break;
743
744 if( item->paymode == PAYMODE_INTXFER)
745 {
746 if( src->date == item->date &&
747 src->kacc == item->kxferacc &&
748 src->kxferacc == item->kacc &&
749 ABS(src->amount) == ABS(item->amount) )
750 {
751 //DB( g_print(" found : %d %s %f %d=>%d\n", item->date, item->wording, item->amount, item->account, item->kxferacc) );
752
753 return item;
754 }
755 }
756 list = g_list_next(list);
757 }
758
759 DB( g_print(" not found...\n") );
760
761 return NULL;
762 }
763
764
765 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
766
767
768 void transaction_add(Transaction *ope, GtkWidget *treeview, guint32 accnum)
769 {
770 Transaction *newope;
771 Account *acc;
772
773 DB( g_print("\n[transaction] transaction add\n") );
774
775 //controls accounts valid (archive scheduled maybe bad)
776 acc = da_acc_get(ope->kacc);
777 if(acc == NULL) return;
778
779 ope->kcur = acc->kcur;
780
781 if(ope->paymode == PAYMODE_INTXFER)
782 {
783 acc = da_acc_get(ope->kxferacc);
784 if(acc == NULL) return;
785
786 // delete any splits
787 da_splits_free(ope->splits);
788 ope->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
789 }
790
791 //allocate a new entry and copy from our edited structure
792 newope = da_transaction_clone(ope);
793
794 //init flag and keep remind status
795 // already done in deftransaction_get
796 //ope->flags |= (OF_ADDED);
797 //remind = (ope->flags & OF_REMIND) ? TRUE : FALSE;
798 //ope->flags &= (~OF_REMIND);
799
800 /* cheque number is already stored in deftransaction_get */
801 /* todo:move this to transaction add
802 store a new cheque number into account ? */
803
804 if( (newope->paymode == PAYMODE_CHECK) && (newope->info) && !(newope->flags & OF_INCOME) )
805 {
806 guint cheque;
807
808 /* get the active account and the corresponding cheque number */
809 acc = da_acc_get( newope->kacc);
810 cheque = atol(newope->info);
811
812 //DB( g_print(" -> should store cheque number %d to %d", cheque, newope->account) );
813 if( newope->flags & OF_CHEQ2 )
814 {
815 acc->cheque2 = MAX(acc->cheque2, cheque);
816 }
817 else
818 {
819 acc->cheque1 = MAX(acc->cheque1, cheque);
820 }
821 }
822
823 /* add normal transaction */
824 acc = da_acc_get( newope->kacc);
825 if(acc != NULL)
826 {
827 acc->flags |= AF_ADDED;
828
829 DB( g_print(" + add normal %p\n", newope) );
830 //da_transaction_append(newope);
831 da_transaction_insert_sorted(newope);
832
833 if(treeview != NULL)
834 transaction_add_treeview(newope, treeview, accnum);
835
836 account_balances_add(newope);
837
838 if(newope->paymode == PAYMODE_INTXFER)
839 {
840 transaction_xfer_search_or_add_child(NULL, newope, FALSE);
841 }
842 }
843 }
844
845
846 void transaction_add_treeview(Transaction *ope, GtkWidget *treeview, guint32 accnum)
847 {
848 GtkTreeModel *model;
849 GtkTreeIter iter;
850 //GtkTreePath *path;
851 //GtkTreeSelection *sel;
852
853 DB( g_print("\n[transaction] transaction add treeview\n") );
854
855 if(ope->kacc == accnum)
856 {
857 model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
858 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
859
860 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
861 LST_DSPOPE_DATAS, ope,
862 -1);
863
864 //activate that new line
865 //path = gtk_tree_model_get_path(model, &iter);
866 //gtk_tree_view_expand_to_path(GTK_TREE_VIEW(treeview), path);
867
868 //sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
869 //gtk_tree_selection_select_iter(sel, &iter);
870
871 //gtk_tree_path_free(path);
872
873 }
874 }
875
876
877 gboolean transaction_acc_move(Transaction *txn, guint32 okacc, guint32 nkacc)
878 {
879 Account *oacc, *nacc;
880
881 oacc = da_acc_get(okacc);
882 nacc = da_acc_get(nkacc);
883 if( oacc && nacc )
884 {
885 if( g_queue_remove(oacc->txn_queue, txn) )
886 {
887 g_queue_push_tail(nacc->txn_queue, txn);
888 txn->kacc = nacc->key;
889 txn->kcur = nacc->kcur;
890 nacc->flags |= AF_CHANGED;
891 return TRUE;
892 }
893 else
894 //ensure to keep txn into current account
895 txn->kacc = okacc;
896 }
897 return FALSE;
898 }
899
900
901 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
902
903
904 static gboolean misc_text_match(gchar *text, gchar *searchtext, gboolean exact)
905 {
906 gboolean match = FALSE;
907
908 if(text == NULL)
909 return FALSE;
910
911 //DB( g_print("search %s in %s\n", rul->name, ope->wording) );
912 if( searchtext != NULL )
913 {
914 if( exact == TRUE )
915 {
916 if( g_strrstr(text, searchtext) != NULL )
917 {
918 DB( g_print(" found case '%s'\n", searchtext) );
919 match = TRUE;
920 }
921 }
922 else
923 {
924 gchar *word = g_utf8_casefold(text, -1);
925 gchar *needle = g_utf8_casefold(searchtext, -1);
926
927 if( g_strrstr(word, needle) != NULL )
928 {
929 DB( g_print(" found nocase '%s'\n", searchtext) );
930 match = TRUE;
931 }
932 g_free(word);
933 g_free(needle);
934 }
935 }
936
937 return match;
938 }
939
940 static gboolean misc_regex_match(gchar *text, gchar *searchtext, gboolean exact)
941 {
942 gboolean match = FALSE;
943
944 if(text == NULL)
945 return FALSE;
946
947 DB( g_print("match RE %s in %s\n", searchtext, text) );
948 if( searchtext != NULL )
949 {
950 match = g_regex_match_simple(searchtext, text, ((exact == TRUE)?0:G_REGEX_CASELESS) | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY );
951 if (match == TRUE) { DB( g_print(" found pattern '%s'\n", searchtext) ); }
952 }
953 return match;
954 }
955
956
957 static Assign *transaction_auto_assign_eval_txn(GList *l_rul, Transaction *txn)
958 {
959 Assign *rule = NULL;
960 GList *list;
961
962 DB( g_print("- eval every rules, and return the last that match\n") );
963
964 list = g_list_first(l_rul);
965 while (list != NULL)
966 {
967 Assign *rul = list->data;
968 gchar *text;
969
970 text = txn->wording;
971 if(rul->field == 1) //payee
972 {
973 Payee *pay = da_pay_get(txn->kpay);
974 if(pay)
975 text = pay->name;
976 }
977
978 if( !(rul->flags & ASGF_REGEX) )
979 {
980 if( misc_text_match(text, rul->text, rul->flags & ASGF_EXACT) )
981 rule = rul;
982 }
983 else
984 {
985 if( misc_regex_match(text, rul->text, rul->flags & ASGF_EXACT) )
986 rule = rul;
987 }
988
989 list = g_list_next(list);
990 }
991
992 return rule;
993 }
994
995
996 static Assign *transaction_auto_assign_eval(GList *l_rul, gchar *text)
997 {
998 Assign *rule = NULL;
999 GList *list;
1000
1001 DB( g_print("- eval every rules, and return the last that match\n") );
1002
1003 list = g_list_first(l_rul);
1004 while (list != NULL)
1005 {
1006 Assign *rul = list->data;
1007
1008 if( rul->field == 0 ) //memo
1009 {
1010 if( !(rul->flags & ASGF_REGEX) )
1011 {
1012 if( misc_text_match(text, rul->text, rul->flags & ASGF_EXACT) )
1013 rule = rul;
1014 }
1015 else
1016 {
1017 if( misc_regex_match(text, rul->text, rul->flags & ASGF_EXACT) )
1018 rule = rul;
1019 }
1020 }
1021 list = g_list_next(list);
1022 }
1023
1024 return rule;
1025 }
1026
1027
1028 gint transaction_auto_assign(GList *ope_list, guint32 kacc)
1029 {
1030 GList *l_ope;
1031 GList *l_rul;
1032 gint changes = 0;
1033
1034 DB( g_print("\n[transaction] transaction_auto_assign\n") );
1035
1036 l_rul = g_hash_table_get_values(GLOBALS->h_rul);
1037
1038 l_ope = g_list_first(ope_list);
1039 while (l_ope != NULL)
1040 {
1041 Transaction *ope = l_ope->data;
1042 gboolean changed = FALSE;
1043
1044 DB( g_print("- eval ope '%s' : acc=%d, pay=%d, cat=%d\n", ope->wording, ope->kacc, ope->kpay, ope->kcat) );
1045
1046 //#1215521: added kacc == 0
1047 if( (kacc == ope->kacc || kacc == 0) )
1048 {
1049 Assign *rul;
1050
1051 rul = transaction_auto_assign_eval_txn(l_rul, ope);
1052 if( rul != NULL )
1053 {
1054 if( (ope->kpay == 0 && (rul->flags & ASGF_DOPAY)) || (rul->flags & ASGF_OVWPAY) )
1055 {
1056 if(ope->kpay != rul->kpay) { changed = TRUE; }
1057 ope->kpay = rul->kpay;
1058 }
1059
1060 if( !(ope->flags & OF_SPLIT) )
1061 {
1062 if( (ope->kcat == 0 && (rul->flags & ASGF_DOCAT)) || (rul->flags & ASGF_OVWCAT) )
1063 {
1064 if(ope->kcat != rul->kcat) { changed = TRUE; }
1065 ope->kcat = rul->kcat;
1066 }
1067 }
1068
1069 if( (ope->paymode == 0 && (rul->flags & ASGF_DOMOD)) || (rul->flags & ASGF_OVWMOD) )
1070 {
1071 //ugly hack - don't allow modify intxfer
1072 if(ope->paymode != PAYMODE_INTXFER && rul->paymode != PAYMODE_INTXFER)
1073 {
1074 if(ope->paymode != rul->paymode) { changed = TRUE; }
1075 ope->paymode = rul->paymode;
1076 }
1077 }
1078
1079 }
1080
1081 if( ope->flags & OF_SPLIT )
1082 {
1083 guint i, nbsplit = da_splits_count(ope->splits);
1084
1085 for(i=0;i<nbsplit;i++)
1086 {
1087 Split *split = ope->splits[i];
1088
1089 DB( g_print("- eval split '%s'\n", split->memo) );
1090
1091 rul = transaction_auto_assign_eval(l_rul, split->memo);
1092 if( rul != NULL )
1093 {
1094 //#1501144: check if user wants to set category in rule
1095 if( (split->kcat == 0 || (rul->flags & ASGF_OVWCAT)) && (rul->flags & ASGF_DOCAT) )
1096 {
1097 if(split->kcat != rul->kcat) { changed = TRUE; }
1098 split->kcat = rul->kcat;
1099 }
1100 }
1101 }
1102 }
1103
1104 if(changed == TRUE)
1105 {
1106 ope->flags |= OF_CHANGED;
1107 changes++;
1108 }
1109 }
1110
1111 l_ope = g_list_next(l_ope);
1112 }
1113
1114 g_list_free(l_rul);
1115
1116 return changes;
1117 }
1118
1119
1120 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1121
1122
1123 guint
1124 transaction_tags_count(Transaction *ope)
1125 {
1126 guint count = 0;
1127 guint32 *ptr = ope->tags;
1128
1129 DB( g_print("(transaction_tags_count)\n") );
1130
1131 if( ope->tags == NULL )
1132 return 0;
1133
1134 while(*ptr++ != 0 && count < 32)
1135 count++;
1136
1137 return count;
1138 }
1139
1140
1141 void transaction_tags_clone(Transaction *src_txn, Transaction *dst_txn)
1142 {
1143 guint count;
1144
1145 dst_txn->tags = NULL;
1146 count = transaction_tags_count(src_txn);
1147 if(count > 0)
1148 {
1149 //1501962: we must also copy the final 0
1150 dst_txn->tags = g_memdup(src_txn->tags, (count+1)*sizeof(guint32));
1151 }
1152 }
1153
1154 guint
1155 transaction_tags_parse(Transaction *ope, const gchar *tagstring)
1156 {
1157 gchar **str_array;
1158 guint count, i;
1159 Tag *tag;
1160
1161 DB( g_print("(transaction_tags_parse)\n") );
1162
1163 DB( g_print(" - tagstring='%s'\n", tagstring) );
1164
1165 str_array = g_strsplit (tagstring, " ", 0);
1166 count = g_strv_length( str_array );
1167
1168 g_free(ope->tags);
1169 ope->tags = NULL;
1170
1171 DB( g_print(" -> reset storage %p\n", ope->tags) );
1172
1173
1174 if( count > 0 )
1175 {
1176
1177 ope->tags = g_new0(guint32, count + 1);
1178
1179 DB( g_print(" -> storage %p\n", ope->tags) );
1180
1181 for(i=0;i<count;i++)
1182 {
1183 tag = da_tag_get_by_name(str_array[i]);
1184 if(tag == NULL)
1185 {
1186 Tag *newtag = da_tag_malloc();
1187
1188 newtag->name = g_strdup(str_array[i]);
1189 da_tag_append(newtag);
1190 tag = da_tag_get_by_name(str_array[i]);
1191 }
1192
1193 DB( g_print(" -> storing %d=>%s at tags pos %d\n", tag->key, tag->name, i) );
1194
1195 ope->tags[i] = tag->key;
1196 }
1197 ope->tags[i] = 0;
1198 }
1199
1200 //hex_dump(ope->tags, sizeof(guint32*)*count+1);
1201
1202 g_strfreev (str_array);
1203
1204 return count;
1205 }
1206
1207 gchar *
1208 transaction_tags_tostring(Transaction *ope)
1209 {
1210 guint count, i;
1211 gchar **str_array;
1212 gchar *tagstring;
1213 Tag *tag;
1214
1215 DB( g_print("transaction_tags_tostring\n") );
1216
1217 DB( g_print(" -> tags at=%p\n", ope->tags) );
1218
1219 if( ope->tags == NULL )
1220 {
1221
1222 return NULL;
1223 }
1224 else
1225 {
1226 count = transaction_tags_count(ope);
1227
1228 DB( g_print(" -> tags at=%p, nbtags=%d\n", ope->tags, count) );
1229
1230 str_array = g_new0(gchar*, count+1);
1231
1232 DB( g_print(" -> str_array at %p\n", str_array) );
1233
1234 //hex_dump(ope->tags, sizeof(guint32*)*(count+1));
1235
1236 for(i=0;i<count;i++)
1237 {
1238 DB( g_print(" -> try to get tag %d\n", ope->tags[i]) );
1239
1240 tag = da_tag_get(ope->tags[i]);
1241 if( tag )
1242 {
1243 DB( g_print(" -> get %s at %d\n", tag->name, i) );
1244 str_array[i] = tag->name;
1245 }
1246 else
1247 str_array[i] = NULL;
1248
1249
1250 }
1251
1252 tagstring = g_strjoinv(" ", str_array);
1253
1254 g_free (str_array);
1255
1256 }
1257
1258 return tagstring;
1259 }
1260
This page took 0.08364 seconds and 4 git commands to generate.