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