]> Dogcows Code - chaz/homebank/blob - src/hb-import.c
import homebank-5.2.6
[chaz/homebank] / src / hb-import.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 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 #include "hb-import.h"
22
23
24 /****************************************************************************/
25 /* Debug macros */
26 /****************************************************************************/
27 #define MYDEBUG 0
28
29 #if MYDEBUG
30 #define DB(x) (x);
31 #else
32 #define DB(x);
33 #endif
34
35 /* our global datas */
36 extern struct HomeBank *GLOBALS;
37 extern struct Preferences *PREFS;
38
39
40 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
41
42
43 static gint homebank_alienfile_recognize(gchar *filename)
44 {
45 GIOChannel *io;
46 gint i, retval = FILETYPE_UNKNOWN;
47 gchar *tmpstr;
48 gint io_stat;
49 GError *err = NULL;
50
51 DB( g_print("\n[homebank] alienfile_recognize\n") );
52
53 io = g_io_channel_new_file(filename, "r", NULL);
54 if(io != NULL)
55 {
56 g_io_channel_set_encoding(io, NULL, NULL); /* set to binary mode */
57
58 for(i=0;i<25;i++)
59 {
60 if( retval != FILETYPE_UNKNOWN )
61 break;
62
63 io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, &err);
64 if( io_stat == G_IO_STATUS_EOF)
65 break;
66 if( io_stat == G_IO_STATUS_ERROR )
67 {
68 DB (g_print(" + ERROR %s\n",err->message));
69 break;
70 }
71 if( io_stat == G_IO_STATUS_NORMAL)
72 {
73 if( *tmpstr != '\0' )
74 {
75 DB( g_print(" line %d: '%s' retval=%d\n", i, tmpstr, retval) );
76
77 // OFX/QFX file ?
78 if( g_strstr_len(tmpstr, -1, "<OFX>") != NULL
79 || g_strstr_len(tmpstr, -1, "<ofx>") != NULL
80 /*|| strcasestr(tmpstr, "<OFC>") != NULL*/
81 )
82 {
83 DB( g_print(" type is OFX\n") );
84 retval = FILETYPE_OFX;
85 }
86 else
87
88 // QIF file ?
89 if( g_str_has_prefix(tmpstr, "!Type") ||
90 g_str_has_prefix(tmpstr, "!type") ||
91 g_str_has_prefix(tmpstr, "!Option") ||
92 g_str_has_prefix(tmpstr, "!option") ||
93 g_str_has_prefix(tmpstr, "!Account") ||
94 g_str_has_prefix(tmpstr, "!account")
95 )
96 {
97 DB( g_print(" type is QIF\n") );
98 retval = FILETYPE_QIF;
99 }
100 else
101
102 // CSV homebank format ?
103 if( hb_csv_test_line(tmpstr) )
104 {
105 DB( g_print(" type is CSV homebank\n") );
106 retval = FILETYPE_CSV_HB;
107 }
108 else
109
110 // native homebank file ?
111 if( g_str_has_prefix(tmpstr, "<homebank v="))
112 {
113 DB( g_print(" type is HomeBank\n") );
114 retval = FILETYPE_HOMEBANK;
115 }
116
117
118 g_free(tmpstr);
119 }
120 }
121 }
122 g_io_channel_unref (io);
123 }
124
125 return retval;
126 }
127
128
129
130
131
132 static void
133 da_import_context_gen_txn_destroy(ImportContext *context)
134 {
135 GList *list;
136
137 DB( g_print("\n[import] free gen txn list\n") );
138 list = g_list_first(context->gen_lst_txn);
139 while (list != NULL)
140 {
141 GenTxn *gentxn = list->data;
142 da_gen_txn_free(gentxn);
143 list = g_list_next(list);
144 }
145 g_list_free(context->gen_lst_txn);
146 context->gen_lst_txn = NULL;
147 }
148
149
150 static void
151 da_import_context_gen_acc_destroy(ImportContext *context)
152 {
153 GList *list;
154
155 DB( g_print("\n[import] free gen acc list\n") );
156 list = g_list_first(context->gen_lst_acc);
157 while (list != NULL)
158 {
159 GenAcc *genacc = list->data;
160 da_gen_acc_free(genacc);
161 list = g_list_next(list);
162 }
163 g_list_free(context->gen_lst_acc);
164 context->gen_lst_acc = NULL;
165
166 }
167
168
169 static void
170 da_import_context_clear(ImportContext *context)
171 {
172 DB( g_print("\n[import] context clear\n") );
173
174 da_import_context_gen_txn_destroy(context);
175 da_import_context_gen_acc_destroy(context);
176 context->gen_next_acckey = 1;
177
178 }
179
180
181 void
182 da_import_context_destroy(ImportContext *context)
183 {
184 GList *list;
185
186 DB( g_print("\n[import] context destroy\n") );
187
188 da_import_context_gen_txn_destroy(context);
189 da_import_context_gen_acc_destroy(context);
190
191 DB( g_print(" free gen file list\n") );
192 list = g_list_first(context->gen_lst_file);
193 while (list != NULL)
194 {
195 GenFile *genfile = list->data;
196 da_gen_file_free(genfile);
197 list = g_list_next(list);
198 }
199 g_list_free(context->gen_lst_file);
200 context->gen_lst_file = NULL;
201 }
202
203
204 void
205 da_import_context_new(ImportContext *context)
206 {
207 context->gen_lst_file = NULL;
208
209 context->gen_lst_acc = NULL;
210 context->gen_lst_txn = NULL;
211 context->gen_next_acckey = 1;
212 }
213
214
215 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
216
217 GenFile *
218 da_gen_file_malloc(void)
219 {
220 return g_malloc0(sizeof(GenFile));
221 }
222
223 void
224 da_gen_file_free(GenFile *genfile)
225 {
226 if(genfile != NULL)
227 {
228 if(genfile->filepath != NULL)
229 g_free(genfile->filepath);
230
231 g_free(genfile);
232 }
233 }
234
235
236 GenFile *
237 da_gen_file_get(GList *lst_file, guint32 key)
238 {
239 GenFile *existfile = NULL;
240 GList *list;
241
242 list = g_list_first(lst_file);
243 while (list != NULL)
244 {
245 GenFile *genfile = list->data;
246
247 if( key == genfile->key )
248 {
249 existfile = genfile;
250 break;
251 }
252 list = g_list_next(list);
253 }
254 return existfile;
255 }
256
257
258 static GenFile *
259 da_gen_file_get_by_name(GList *lst_file, gchar *filepath)
260 {
261 GenFile *existfile = NULL;
262 GList *list;
263
264 DB( g_print("da_gen_file_get_by_name\n") );
265
266 list = g_list_first(lst_file);
267 while (list != NULL)
268 {
269 GenFile *genfile = list->data;
270
271 DB( g_print(" strcasecmp '%s' '%s'\n", filepath, genfile->filepath) );
272
273 if(!strcasecmp(filepath, genfile->filepath))
274 {
275 existfile = genfile;
276 DB( g_print(" found\n") );
277 break;
278 }
279 list = g_list_next(list);
280 }
281
282 return existfile;
283 }
284
285
286 GenFile *
287 da_gen_file_append_from_filename(ImportContext *ictx, gchar *filename)
288 {
289 GenFile *genfile = NULL;
290 gint filetype;
291
292 //todo: should check if its a file !!
293
294 filetype = homebank_alienfile_recognize(filename);
295
296 DB( g_print(" - filename '%s', type is %d\n", filename, filetype ) );
297
298 // we keep everything here
299 //if( (filetype == FILETYPE_OFX) || (filetype == FILETYPE_QIF) || (filetype == FILETYPE_CSV_HB) )
300 //{
301 GenFile *existgenfile;
302
303 existgenfile = da_gen_file_get_by_name(ictx->gen_lst_file, filename);
304 if(existgenfile == NULL)
305 {
306 genfile = da_gen_file_malloc();
307 genfile->filepath = g_strdup(filename);
308 genfile->filetype = filetype;
309
310 //append to list
311 genfile->key = g_list_length (ictx->gen_lst_file) + 1;
312 ictx->gen_lst_file = g_list_append(ictx->gen_lst_file, genfile);
313
314 }
315 //}
316
317 return genfile;
318 }
319
320
321 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
322
323
324 GenAcc *
325 da_gen_acc_malloc(void)
326 {
327 return g_malloc0(sizeof(GenAcc));
328 }
329
330 void
331 da_gen_acc_free(GenAcc *genacc)
332 {
333 if(genacc != NULL)
334 {
335 if(genacc->name != NULL)
336 g_free(genacc->name);
337 if(genacc->number != NULL)
338 g_free(genacc->number);
339
340 g_free(genacc);
341 }
342 }
343
344
345 GenAcc *
346 da_gen_acc_get_by_key(GList *lst_acc, guint32 key)
347 {
348 GenAcc *existacc = NULL;
349 GList *list;
350
351 list = g_list_first(lst_acc);
352 while (list != NULL)
353 {
354 GenAcc *genacc = list->data;
355
356 if( key == genacc->key )
357 {
358 existacc = genacc;
359 break;
360 }
361 list = g_list_next(list);
362 }
363 return existacc;
364 }
365
366
367 static GenAcc *
368 da_gen_acc_get_by_name(GList *lst_acc, gchar *name)
369 {
370 GenAcc *existacc = NULL;
371 GList *list;
372
373 //DB( g_print("da_gen_acc_get_by_name\n") );
374
375 list = g_list_first(lst_acc);
376 while (list != NULL)
377 {
378 GenAcc *genacc = list->data;
379
380 //DB( g_print(" strcasecmp '%s' '%s'\n", name, genacc->name) );
381
382 if(!strcasecmp(name, genacc->name))
383 {
384 existacc = genacc;
385 //DB( g_print(" found\n") );
386 break;
387 }
388 list = g_list_next(list);
389 }
390
391 return existacc;
392 }
393
394
395 Account *
396 hb_import_acc_find_existing(gchar *name, gchar *number)
397 {
398 Account *retacc = NULL;
399 GList *lacc, *list;
400
401 DB( g_print("\n[import] acc_find_existing\n") );
402
403 DB( g_print(" - search number '%s'\n", number) );
404 lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
405 while (list != NULL)
406 {
407 Account *acc = list->data;
408
409 //DB( g_print(" - eval acc '%s' or '%s'\n", acc->name, acc->number) );
410 if(number != NULL && acc->number && strlen(acc->number) )
411 {
412 //prefer identifying with number & search number into acc->number
413 if(g_strstr_len(number, -1, acc->number) != NULL)
414 {
415 DB( g_print(" - match number '%s'\n", acc->number) );
416 retacc = acc;
417 break;
418 }
419 }
420 list = g_list_next(list);
421 }
422
423 //# 1815964 only test name if all number test failed
424 //if not found try with name
425 if(retacc == NULL)
426 {
427 DB( g_print(" - search name '%s'\n", name) );
428 list = g_list_first(lacc);
429 while (list != NULL)
430 {
431 Account *acc = list->data;
432
433 //DB( g_print(" - eval acc '%s' or '%s'\n", acc->name, acc->number) );
434 if(retacc == NULL && name != NULL)
435 {
436 if(g_strstr_len(name, -1, acc->name) != NULL)
437 {
438 DB( g_print(" - match name '%s'\n", acc->name) );
439 retacc = acc;
440 break;
441 }
442 }
443 list = g_list_next(list);
444 }
445 }
446
447 g_list_free(lacc);
448
449 return retacc;
450 }
451
452
453 GenAcc *
454 hb_import_gen_acc_get_next(ImportContext *ictx, gint filetype, gchar *name, gchar *number)
455 {
456 GenAcc *newacc;
457
458 DB( g_print("\n[import] acc_get_next\n") );
459
460 DB( g_print(" - type='%d', name='%s', number='%s'\n", filetype, name, number) );
461
462 // try to find a same name account
463 if( name != NULL )
464 {
465 newacc = da_gen_acc_get_by_name(ictx->gen_lst_acc, name);
466 if(newacc != NULL)
467 {
468 DB( g_print(" - found existing '%s'\n", name) );
469 goto end;
470 }
471 }
472
473 newacc = da_gen_acc_malloc();
474 if(newacc)
475 {
476 newacc->kfile = ictx->curr_kfile;
477 newacc->key = ictx->gen_next_acckey++;
478 newacc->kacc = DST_ACC_GLOBAL;
479
480 if(name != NULL)
481 {
482 newacc->is_unamed = FALSE;
483 newacc->name = g_strdup(name);
484 }
485 else
486 {
487 GenFile *genfile;
488 gchar *basename;
489
490 newacc->is_unamed = TRUE;
491
492 genfile = da_gen_file_get (ictx->gen_lst_file, newacc->kfile);
493 basename = g_path_get_basename(genfile->filepath);
494
495 newacc->name = g_strdup_printf("%s %d", basename, newacc->key);
496 g_free(basename);
497 }
498
499 if(number != NULL)
500 newacc->number = g_strdup(number);
501
502 ictx->gen_lst_acc = g_list_append(ictx->gen_lst_acc, newacc);
503 }
504
505 DB( g_print(" - create new '%s'\n", newacc->name) );
506
507 end:
508 newacc->filetype = filetype;
509 ictx->curr_kacc = newacc->key;
510
511 return newacc;
512 }
513
514
515 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
516
517
518 GenTxn *
519 da_gen_txn_malloc(void)
520 {
521 return g_malloc0(sizeof(GenTxn));
522 }
523
524
525 void
526 da_gen_txn_free(GenTxn *gentxn)
527 {
528 gint i;
529
530 if(gentxn != NULL)
531 {
532 if(gentxn->account != NULL)
533 g_free(gentxn->account);
534
535 if(gentxn->rawinfo != NULL)
536 g_free(gentxn->rawinfo);
537 if(gentxn->rawpayee != NULL)
538 g_free(gentxn->rawpayee);
539 if(gentxn->rawmemo != NULL)
540 g_free(gentxn->rawmemo);
541
542 if(gentxn->date != NULL)
543 g_free(gentxn->date);
544 if(gentxn->info != NULL)
545 g_free(gentxn->info);
546 if(gentxn->payee != NULL)
547 g_free(gentxn->payee);
548 if(gentxn->memo != NULL)
549 g_free(gentxn->memo);
550 if(gentxn->category != NULL)
551 g_free(gentxn->category);
552 if(gentxn->tags != NULL)
553 g_free(gentxn->tags);
554
555 for(i=0;i<TXN_MAX_SPLIT;i++)
556 {
557 GenSplit *s = &gentxn->splits[i];
558
559 if(s->memo != NULL)
560 g_free(s->memo);
561 if(s->category != NULL)
562 g_free(s->category);
563 }
564
565 if(gentxn->lst_existing != NULL)
566 {
567 g_list_free(gentxn->lst_existing);
568 gentxn->lst_existing = NULL;
569 }
570
571 g_free(gentxn);
572 }
573 }
574
575 static gint
576 da_gen_txn_compare_func(GenTxn *a, GenTxn *b)
577 {
578 gint retval = (gint)(a->julian - b->julian);
579
580 if(!retval)
581 retval = (ABS(a->amount) - ABS(b->amount)) > 0 ? 1 : -1;
582 return (retval);
583 }
584
585
586 GList *
587 da_gen_txn_sort(GList *list)
588 {
589 return( g_list_sort(list, (GCompareFunc)da_gen_txn_compare_func));
590 }
591
592
593 void
594 da_gen_txn_move(GenTxn *sgentxn, GenTxn *dgentxn)
595 {
596 if(sgentxn != NULL && dgentxn != NULL)
597 {
598 memcpy(dgentxn, sgentxn, sizeof(GenTxn));
599 memset(sgentxn, 0, sizeof(GenTxn));
600 }
601 }
602
603
604 void
605 da_gen_txn_append(ImportContext *ctx, GenTxn *gentxn)
606 {
607 gentxn->kfile = ctx->curr_kfile;
608 gentxn->kacc = ctx->curr_kacc;
609 gentxn->to_import = TRUE;
610 ctx->gen_lst_txn = g_list_append(ctx->gen_lst_txn, gentxn);
611 }
612
613
614 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
615
616
617 static void _string_utf8_ucfirst(gchar **str)
618 {
619 gint str_len;
620 gchar *first, *lc;
621
622 if( *str == NULL )
623 return;
624
625 str_len = strlen(*str);
626 if( str_len <= 1 )
627 return;
628
629 first = g_utf8_strup(*str, 1);
630 lc = g_utf8_strdown( g_utf8_next_char(*str), -1 );
631 g_free(*str);
632 *str = g_strjoin(NULL, first, lc, NULL);
633 g_free(first);
634 g_free(lc);
635 }
636
637
638 static gchar *
639 _string_concat(gchar *str, gchar *addon)
640 {
641 gchar *retval;
642
643 DB( g_print(" - concat '%s' + '%s'\n", str, addon) );
644
645 if(str == NULL)
646 retval = g_strdup(addon);
647 else
648 {
649 retval = g_strjoin(" ", str, addon, NULL);
650 g_free(str);
651 }
652
653 DB( g_print(" - retval='%s'\n", retval) );
654 return retval;
655 }
656
657
658 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
659
660 gchar *hb_import_filetype_char_get(GenAcc *genacc)
661 {
662 gchar *retval = "";
663
664 switch(genacc->filetype)
665 {
666 #ifndef NOOFX
667 case FILETYPE_OFX:
668 retval = "OFX/QFX";
669 break;
670 #endif
671 case FILETYPE_QIF:
672 retval = "QIF";
673 break;
674
675 case FILETYPE_CSV_HB:
676 retval = "CSV";
677 break;
678 }
679
680 return retval;
681 }
682
683
684 void
685 hb_import_load_all(ImportContext *ictx)
686 {
687 GList *list;
688
689 DB( g_print("\n[import] load all\n") );
690
691 da_import_context_clear (ictx);
692
693 list = g_list_first(ictx->gen_lst_file);
694 while (list != NULL)
695 {
696 GenFile *genfile = list->data;
697
698 if(genfile->filetype != FILETYPE_UNKNOWN)
699 {
700 //todo: move this to alien analysis
701 genfile->encoding = homebank_file_getencoding(genfile->filepath);
702
703 ictx->curr_kfile = genfile->key;
704
705 DB( g_print(" -> key = '%d'\n", genfile->key) );
706 DB( g_print(" -> filepath = '%s'\n", genfile->filepath) );
707 DB( g_print(" -> encoding = '%s'\n", genfile->encoding) );
708
709 genfile->loaded = FALSE;
710 genfile->invaliddatefmt = FALSE;
711
712 switch(genfile->filetype)
713 {
714 #ifndef NOOFX
715 case FILETYPE_OFX:
716 homebank_ofx_import(ictx, genfile);
717 break;
718 #endif
719 case FILETYPE_QIF:
720 homebank_qif_import(ictx, genfile);
721 break;
722
723 case FILETYPE_CSV_HB:
724 homebank_csv_import(ictx, genfile);
725 break;
726 }
727
728 genfile->loaded = TRUE;
729 }
730
731 list = g_list_next(list);
732 }
733
734 // sort by date
735 ictx->gen_lst_txn = da_gen_txn_sort(ictx->gen_lst_txn);
736
737 }
738
739
740 gint
741 hb_import_gen_acc_count_txn(ImportContext *ictx, GenAcc *genacc)
742 {
743 GList *list;
744 gint count = 0;
745
746 DB( g_print("\n[import] gen_acc_count_txn\n") );
747
748 genacc->n_txnall = 0;
749 genacc->n_txnimp = 0;
750
751 list = g_list_first(ictx->gen_lst_txn);
752 while (list != NULL)
753 {
754 GenTxn *gentxn = list->data;
755
756 if(gentxn->kacc == genacc->key)
757 {
758 genacc->n_txnall++;
759 count++;
760
761 DB( g_print(" count %03d: gentxn in=%d dup=%d '%s'\n", count, gentxn->to_import, gentxn->is_dst_similar, gentxn->memo) );
762
763 if(gentxn->to_import)
764 genacc->n_txnimp++;
765 }
766 list = g_list_next(list);
767 }
768 return count;
769 }
770
771
772 /**
773 * uncheck duplicate within the import context files
774 */
775 gint
776 hb_import_gen_txn_check_duplicate(ImportContext *ictx, GenAcc *genacc)
777 {
778 GList *list1, *list2;
779 gint count = 0;
780
781 DB( g_print("\n[import] gen_txn_check_duplicate\n") );
782
783
784 list1 = g_list_first(ictx->gen_lst_txn);
785 while (list1 != NULL)
786 {
787 GenTxn *gentxn1 = list1->data;
788
789 if( (genacc->key == gentxn1->kacc) && (gentxn1->julian != 0) ) //same account, valid date
790 {
791 list2 = g_list_next(list1);
792 while (list2 != NULL)
793 {
794 GenTxn *gentxn2 = list2->data;
795
796 if( (gentxn2->julian > gentxn1->julian) )
797 break;
798
799 //todo: maybe reinforce controls here
800 if( (gentxn2->kacc == gentxn1->kacc)
801 && (gentxn2->julian == gentxn1->julian)
802 && (gentxn2->amount == gentxn1->amount)
803 && (hb_string_compare(gentxn2->memo, gentxn1->memo) == 0)
804 && (hb_string_compare(gentxn2->payee, gentxn1->payee) == 0)
805 )
806 {
807 gentxn1->to_import = FALSE;
808 gentxn1->is_imp_similar = TRUE;
809 count++;
810
811 DB( g_print(" found import dup %d=%d %.2f %.2f in=%d dup=%d\n", gentxn1->julian, gentxn2->julian, gentxn2->amount, gentxn1->amount, gentxn1->to_import, gentxn1->is_imp_similar) );
812
813 }
814 list2 = g_list_next(list2);
815 }
816 }
817 list1 = g_list_next(list1);
818 }
819 return count;
820 }
821
822
823 /**
824 * uncheck existing txn into target account
825 *
826 */
827 gint
828 hb_import_gen_txn_check_target_similar(ImportContext *ictx, GenAcc *genacc)
829 {
830 GList *list1, *list2;
831 gint count = 0;
832
833 DB( g_print("\n[import] gen_txn_check_target_similar\n") );
834
835 list1 = g_list_first(ictx->gen_lst_txn);
836 while (list1 != NULL)
837 {
838 GenTxn *gentxn = list1->data;
839
840 if(genacc->key == gentxn->kacc)
841 {
842 gentxn->to_import = TRUE;
843 gentxn->is_dst_similar = FALSE;
844
845 if(genacc->kacc == DST_ACC_SKIP)
846 {
847 gentxn->to_import = FALSE;
848 }
849 else
850 {
851 Account *acc = da_acc_get(genacc->kacc);
852
853 if(acc != NULL)
854 {
855 //clear previous existing
856 if(gentxn->lst_existing != NULL)
857 {
858 g_list_free(gentxn->lst_existing);
859 gentxn->lst_existing = NULL;
860 }
861
862 // try to find existing transaction
863 list2 = g_queue_peek_tail_link(acc->txn_queue);
864 while (list2 != NULL)
865 {
866 Transaction *txn = list2->data;
867
868 //break if the date goes below the gentxn date + gap
869 if( txn->date < (gentxn->julian - ictx->opt_daygap) )
870 break;
871
872 //#1586211 add of date tolerance
873 //todo: maybe reinforce controls here
874 if( ( txn->kacc == genacc->kacc )
875 && ( gentxn->julian <= (txn->date + ictx->opt_daygap) )
876 && ( gentxn->julian >= (txn->date - ictx->opt_daygap) )
877 && ( txn->amount == gentxn->amount )
878 )
879 {
880 gentxn->lst_existing = g_list_append(gentxn->lst_existing, txn);
881 gentxn->to_import = FALSE;
882 gentxn->is_dst_similar = TRUE;
883 count++;
884
885 DB( g_print(" found dst acc dup %d %.2f '%s' in=%d, dup=%d\n", gentxn->julian, gentxn->amount, gentxn->memo, gentxn->to_import, gentxn->is_dst_similar) );
886 }
887
888 list2 = g_list_previous(list2);
889 }
890 }
891
892 }
893 }
894
895 list1 = g_list_next(list1);
896 }
897
898 return count;
899 }
900
901
902 /**
903 * try to indentify xfer for OFX
904 *
905 */
906 static gint
907 hb_import_gen_xfer_eval(ImportContext *ictx, GList *list)
908 {
909 GList *root, *list1, *list2;
910 GList *match = NULL;
911 gint count = 0;
912
913 DB( g_print("\n[import] gen xfer eval\n") );
914
915 root = list1 = g_list_first(list);
916 while (list1 != NULL)
917 {
918 Transaction *txn1 = list1->data;
919 GenAcc *acc;
920
921 acc = da_gen_acc_get_by_key(ictx->gen_lst_acc, txn1->kacc);
922
923 DB( g_print(" src: kacc:%d dat:%d amt:%.2f %s kfxacc:%d\n", txn1->kacc, txn1->date, txn1->amount, txn1->memo, txn1->kxferacc) );
924
925 if( (acc != NULL) && (acc->filetype == FILETYPE_OFX) )
926 {
927 match = NULL;
928 count = 0;
929 list2 = g_list_next(root);
930 while (list2 != NULL)
931 {
932 Transaction *txn2 = list2->data;
933
934 //DB( g_print(" -- chk: kacc:%d dat:%d amt:%.2f %s\n", txn2->kacc, txn2->date, txn2->amount, txn2->memo) );
935 if( (txn2->date > txn1->date) )
936 break;
937
938 if( (txn2 == txn1) || (txn2->paymode == PAYMODE_INTXFER) )
939 goto next;
940
941 //todo: maybe reinforce controls here
942 if( (txn2->kacc != txn1->kacc)
943 && (txn2->date == txn1->date)
944 && (txn2->amount == -txn1->amount)
945 && (hb_string_compare(txn2->memo, txn1->memo) == 0)
946 )
947 {
948 DB( g_print(" match: kacc:%d dat:%d amt:%.2f %s kfxacc:%d\n", txn2->kacc, txn2->date, txn2->amount, txn2->memo, txn2->kxferacc) );
949 match = g_list_append(match, txn2);
950 count++;
951 }
952 next:
953 list2 = g_list_next(list2);
954 }
955
956 if(count == 1) //we found a single potential xfer, transform it
957 {
958 Transaction *txn2 ;
959
960 DB( g_print(" single found => convert both\n") );
961
962 list2 = g_list_first(match);
963 txn2 = list2->data;
964
965
966 txn1->paymode = PAYMODE_INTXFER;
967 transaction_xfer_change_to_child(txn1, txn2);
968
969 /*list2 = g_list_first(match);
970 txn2 = list2->data;
971
972 txn1->paymode = PAYMODE_INTXFER;
973 txn1->kxferacc = txn2->kacc;
974
975 txn2->paymode = PAYMODE_INTXFER;
976 txn2->kxferacc = txn1->kacc;
977 */
978 }
979 // if more than one, we cannot be sure
980 g_list_free(match);
981 }
982
983 list1 = g_list_next(list1);
984 }
985
986 return count;
987 }
988
989
990 /**
991 * apply the user option: date format, payee/memo/info mapping
992 *
993 */
994 gboolean
995 hb_import_option_apply(ImportContext *ictx, GenAcc *genacc)
996 {
997 GList *list;
998
999 DB( g_print("\n[import] option apply\n") );
1000
1001 DB( g_print(" - type=%d\n", genacc->filetype) );
1002
1003 genacc->n_txnbaddate = 0;
1004
1005 list = g_list_first(ictx->gen_lst_txn);
1006 while (list != NULL)
1007 {
1008 GenTxn *gentxn = list->data;
1009
1010 if(gentxn->kacc == genacc->key)
1011 {
1012 if(genacc->filetype != FILETYPE_OFX)
1013 {
1014 gentxn->julian = hb_date_get_julian(gentxn->date, ictx->opt_dateorder);
1015 if( gentxn->julian == 0 )
1016 {
1017 genacc->n_txnbaddate++;
1018 }
1019 }
1020
1021 if(genacc->filetype == FILETYPE_OFX)
1022 {
1023 DB( g_print(" - ofx option apply\n") );
1024
1025 g_free(gentxn->payee);
1026 g_free(gentxn->memo);
1027 g_free(gentxn->info);
1028 gentxn->payee = NULL;
1029 gentxn->memo = NULL;
1030 gentxn->info = NULL;
1031
1032 // OFX:check_number
1033 gentxn->info = g_strdup(gentxn->rawinfo);
1034
1035 //#1791482 map name to info (concat only)
1036 switch(ictx->opt_ofxname)
1037 {
1038 //ofxname is stored into rawpayee
1039 case 1:
1040 gentxn->memo = g_strdup(gentxn->rawpayee);
1041 break;
1042 case 2:
1043 gentxn->payee = g_strdup(gentxn->rawpayee);
1044 break;
1045 case 3:
1046 g_free(gentxn->info);
1047 gentxn->info = _string_concat(gentxn->rawinfo, gentxn->rawpayee);
1048 break;
1049 }
1050
1051 if(gentxn->rawmemo != NULL)
1052 {
1053 switch(ictx->opt_ofxmemo)
1054 {
1055 //case 0: ignore
1056 case 1: //add to info
1057 gentxn->info = _string_concat(gentxn->info, gentxn->rawmemo);
1058 break;
1059
1060 case 2: //add to memo
1061 gentxn->memo = _string_concat(gentxn->memo, gentxn->rawmemo);
1062 break;
1063
1064 case 3: //add to payee
1065 gentxn->payee = _string_concat(gentxn->payee, gentxn->rawmemo);
1066 break;
1067 }
1068 }
1069
1070 DB( g_print(" - payee is '%s'\n", gentxn->payee) );
1071 DB( g_print(" - memo is '%s'\n", gentxn->memo) );
1072 DB( g_print(" - info is '%s'\n", gentxn->info) );
1073 DB( g_print("\n") );
1074
1075 }
1076 else
1077 if(genacc->filetype == FILETYPE_QIF)
1078 {
1079 DB( g_print(" - qif option apply\n") );
1080
1081 g_free(gentxn->payee);
1082 g_free(gentxn->memo);
1083 gentxn->payee = NULL;
1084 gentxn->memo = NULL;
1085
1086 if(!ictx->opt_qifswap)
1087 {
1088 gentxn->payee = g_strdup(gentxn->rawpayee);
1089 if(ictx->opt_qifmemo)
1090 gentxn->memo = g_strdup(gentxn->rawmemo);
1091 }
1092 else
1093 {
1094 gentxn->payee = g_strdup(gentxn->rawmemo);
1095 if(ictx->opt_qifmemo)
1096 gentxn->memo = g_strdup(gentxn->rawpayee);
1097 }
1098
1099 DB( g_print(" - payee is '%s'\n", gentxn->payee) );
1100 DB( g_print(" - memo is '%s'\n", gentxn->memo) );
1101
1102 }
1103 else
1104 if(genacc->filetype == FILETYPE_CSV_HB)
1105 {
1106 DB( g_print(" - csv option apply\n") );
1107
1108 //#1791656 missing: info, payee and tagsg_freg_free(gentxn->payee);
1109 g_free(gentxn->payee);
1110 g_free(gentxn->memo);
1111 g_free(gentxn->info);
1112
1113 gentxn->payee = g_strdup(gentxn->rawpayee);
1114 gentxn->memo = g_strdup(gentxn->rawmemo);
1115 gentxn->info = g_strdup(gentxn->rawinfo);
1116 }
1117
1118 //at last do ucfirst
1119 if( (ictx->opt_ucfirst == TRUE) )
1120 {
1121 _string_utf8_ucfirst(&gentxn->memo);
1122 _string_utf8_ucfirst(&gentxn->payee);
1123 //category ?
1124 }
1125
1126 }
1127 list = g_list_next(list);
1128 }
1129
1130 DB( g_print(" - nb_err=%d\n", genacc->n_txnbaddate) );
1131
1132 return genacc->n_txnbaddate == 0 ? TRUE : FALSE;
1133 }
1134
1135
1136 /**
1137 * convert a GenTxn to a Transaction
1138 *
1139 */
1140 Transaction *
1141 hb_import_convert_txn(GenAcc *genacc, GenTxn *gentxn)
1142 {
1143 Transaction *newope;
1144 Account *accitem;
1145 Payee *payitem;
1146 Category *catitem;
1147 gint nsplit;
1148
1149 DB( g_print("\n[import] convert txn\n") );
1150
1151 newope = NULL;
1152
1153 DB( g_print(" - gentxt %s %s %s\n", gentxn->account, gentxn->date, gentxn->memo) );
1154 DB( g_print(" - genacc '%s' '%p'\n", gentxn->account, genacc) );
1155
1156 if( genacc != NULL)
1157 {
1158 newope = da_transaction_malloc();
1159
1160 newope->kacc = genacc->kacc;
1161 newope->date = gentxn->julian;
1162 newope->paymode = gentxn->paymode;
1163 newope->info = g_strdup(gentxn->info);
1164 newope->memo = g_strdup(gentxn->memo);
1165 newope->amount = gentxn->amount;
1166
1167 if(newope->amount > 0)
1168 newope->flags |= OF_INCOME;
1169
1170 //#773282 invert amount for ccard accounts
1171 //todo: manage this (qif), it is not set to true anywhere
1172 //if(ictx->is_ccard)
1173 // gentxn->amount *= -1;
1174
1175 // payee + append
1176 if( gentxn->payee != NULL )
1177 {
1178 payitem = da_pay_get_by_name(gentxn->payee);
1179 if(payitem == NULL)
1180 {
1181 //DB( g_print(" -> append pay: '%s'\n", item->payee ) );
1182
1183 payitem = da_pay_malloc();
1184 payitem->name = g_strdup(gentxn->payee);
1185 //payitem->imported = TRUE;
1186 da_pay_append(payitem);
1187
1188 //ictx->cnt_new_pay += 1;
1189 }
1190 newope->kpay = payitem->key;
1191 }
1192
1193 // LCategory of transaction
1194 // L[Transfer account name]
1195 // LCategory of transaction/Class of transaction
1196 // L[Transfer account]/Class of transaction
1197 if( gentxn->category != NULL )
1198 {
1199 if(g_str_has_prefix(gentxn->category, "[")) // this is a transfer account name
1200 {
1201 gchar *accname;
1202
1203 //DB ( g_print(" -> transfer to: '%s'\n", item->category) );
1204
1205 //remove brackets
1206 accname = hb_strdup_nobrackets(gentxn->category);
1207
1208 // search target account + append if not exixts
1209 accitem = da_acc_get_by_name(accname);
1210 if(accitem == NULL)
1211 {
1212 DB( g_print(" -> append int xfer dest acc: '%s'\n", accname ) );
1213
1214 accitem = da_acc_malloc();
1215 accitem->name = g_strdup(accname);
1216 //accitem->imported = TRUE;
1217 //accitem->imp_name = g_strdup(accname);
1218 da_acc_append(accitem);
1219 }
1220
1221 newope->kxferacc = accitem->key;
1222 newope->paymode = PAYMODE_INTXFER;
1223
1224 g_free(accname);
1225 }
1226 else
1227 {
1228 //DB ( g_print(" -> append cat: '%s'\n", item->category) );
1229
1230 catitem = da_cat_append_ifnew_by_fullname(gentxn->category);
1231 if( catitem != NULL )
1232 {
1233 //ictx->cnt_new_cat += 1;
1234 newope->kcat = catitem->key;
1235 }
1236 }
1237 }
1238
1239 //#1791656 miss tags also...
1240 if( gentxn->tags != NULL )
1241 {
1242 g_free(newope->tags);
1243 newope->tags = tags_parse(gentxn->tags);
1244 }
1245
1246 // splits, if not a xfer
1247 //TODO: it seems this never happen
1248 if( gentxn->paymode != PAYMODE_INTXFER )
1249 {
1250 if( gentxn->nb_splits > 0 )
1251 {
1252 newope->splits = da_split_new();
1253 for(nsplit=0;nsplit<gentxn->nb_splits;nsplit++)
1254 {
1255 GenSplit *s = &gentxn->splits[nsplit];
1256 Split *hbs;
1257 guint32 kcat = 0;
1258
1259 DB( g_print(" -> append split %d: '%s' '%.2f' '%s'\n", nsplit, s->category, s->amount, s->memo) );
1260
1261 if( s->category != NULL )
1262 {
1263 catitem = da_cat_append_ifnew_by_fullname(s->category);
1264 if( catitem != NULL )
1265 {
1266 kcat = catitem->key;
1267 }
1268 }
1269
1270 //todo: remove this when no more use ||
1271 hb_string_replace_char('|', s->memo);
1272 hbs = da_split_malloc ();
1273 hbs->kcat = kcat;
1274 hbs->memo = g_strdup(s->memo);
1275 hbs->amount = s->amount;
1276 da_splits_append(newope->splits, hbs);
1277 hbs = NULL;
1278 }
1279 }
1280 }
1281
1282 newope->flags |= OF_ADDED;
1283 if( newope->amount > 0 )
1284 newope->flags |= OF_INCOME;
1285
1286 if( gentxn->reconciled )
1287 newope->status = TXN_STATUS_RECONCILED;
1288 else
1289 if( gentxn->cleared )
1290 newope->status = TXN_STATUS_CLEARED;
1291 }
1292 return newope;
1293 }
1294
1295
1296 void
1297 hb_import_apply(ImportContext *ictx)
1298 {
1299 GList *list, *lacc;
1300 GList *txnlist;
1301 guint32 kcommon = 0;
1302 guint changes = 0;
1303
1304 DB( g_print("\n[import] apply\n") );
1305
1306 //create accounts
1307 list = g_list_first(ictx->gen_lst_acc);
1308 while (list != NULL)
1309 {
1310 GenAcc *genacc = list->data;
1311
1312 DB( g_print(" #1 genacc: %d %s %s => %d\n", genacc->key, genacc->name, genacc->number, genacc->kacc) );
1313
1314 //we do create the common account once
1315 if( (genacc->kacc == DST_ACC_GLOBAL) )
1316 {
1317 if( kcommon == 0 )
1318 {
1319 Account *acc = da_acc_malloc ();
1320
1321 acc->name = g_strdup(_("imported account"));
1322 if( da_acc_append(acc) )
1323 {
1324 kcommon = acc->key;
1325 changes++;
1326 }
1327 }
1328
1329 genacc->kacc = kcommon;
1330 }
1331 else
1332 if( (genacc->kacc == DST_ACC_NEW) )
1333 {
1334 Account *acc = da_acc_malloc ();
1335
1336 acc->name = g_strdup(genacc->name);
1337 if( da_acc_append(acc) )
1338 {
1339 acc->number = g_strdup(genacc->number);
1340 acc->initial = genacc->initial;
1341
1342 //store the target acc key
1343 genacc->kacc = acc->key;
1344 changes++;
1345 }
1346 }
1347
1348 list = g_list_next(list);
1349 }
1350
1351 // insert every transactions into a temporary list
1352 // we do this to keep a finished real txn list for detect xfer below
1353 DB( g_print(" #2 insert txn\n") );
1354
1355 txnlist = NULL;
1356 lacc = g_list_first(ictx->gen_lst_acc);
1357 while (lacc != NULL)
1358 {
1359 GenAcc *genacc = lacc->data;
1360
1361 if(genacc->kacc != DST_ACC_SKIP)
1362 {
1363 list = g_list_first(ictx->gen_lst_txn);
1364 while (list != NULL)
1365 {
1366 GenTxn *gentxn = list->data;
1367
1368 if(gentxn->kacc == genacc->key && gentxn->to_import == TRUE)
1369 {
1370 Transaction *txn, *dtxn;
1371
1372 txn = hb_import_convert_txn(genacc, gentxn);
1373 if( txn )
1374 {
1375 dtxn = transaction_add(NULL, txn);
1376 txnlist = g_list_append(txnlist, dtxn);
1377 da_transaction_free(txn);
1378 //#1820618 forgot to report changes count
1379 changes++;
1380 }
1381 }
1382 list = g_list_next(list);
1383 }
1384 }
1385 lacc = g_list_next(lacc);
1386 }
1387
1388 //auto assign
1389 DB( g_print(" call auto assign\n") );
1390 transaction_auto_assign(txnlist, 0);
1391
1392 //check for ofx internal xfer
1393 DB( g_print(" call hb_import_gen_xfer_eval\n") );
1394 hb_import_gen_xfer_eval(ictx, txnlist);
1395
1396 g_list_free(txnlist);
1397
1398 DB( g_print(" adding %d changes\n", changes) );
1399 GLOBALS->changes_count += changes;
1400
1401 }
1402
1403
1404 #if MYDEBUG
1405 void _import_context_debug_file_list(ImportContext *ctx)
1406 {
1407 GList *list;
1408
1409 g_print("\n--debug-- file list %d\n", g_list_length(ctx->gen_lst_file) );
1410
1411 list = g_list_first(ctx->gen_lst_file);
1412 while (list != NULL)
1413 {
1414 GenFile *item = list->data;
1415
1416 g_print(" genfile: %d '%s' '%s'\ndf=%d invalid=%d\n", item->key, item->filepath, item->encoding, item->datefmt, item->invaliddatefmt);
1417
1418 list = g_list_next(list);
1419 }
1420
1421 }
1422
1423 void _import_context_debug_acc_list(ImportContext *ctx)
1424 {
1425 GList *list;
1426
1427 g_print("\n--debug-- acc list %d\n", g_list_length(ctx->gen_lst_acc) );
1428
1429 list = g_list_first(ctx->gen_lst_acc);
1430 while (list != NULL)
1431 {
1432 GenAcc *item = list->data;
1433
1434 g_print(" genacc: %d %s %s => %d\n", item->key, item->name, item->number, item->kacc);
1435
1436 list = g_list_next(list);
1437 }
1438
1439 }
1440
1441
1442 void _import_context_debug_txn_list(ImportContext *ctx)
1443 {
1444 GList *list;
1445
1446 g_print("\n--debug-- txn list %d\n", g_list_length(ctx->gen_lst_txn) );
1447
1448 list = g_list_first(ctx->gen_lst_txn);
1449 while (list != NULL)
1450 {
1451 GenTxn *item = list->data;
1452
1453 g_print(" gentxn: (%d) %s %s (%d) %s %.2f\n", item->kfile, item->account, item->date, item->julian, item->memo, item->amount);
1454
1455 list = g_list_next(list);
1456 }
1457
1458 }
1459
1460 #endif
1461
This page took 0.103511 seconds and 4 git commands to generate.