X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fhomebank;a=blobdiff_plain;f=src%2Fhb-import.c;h=7520c03a77a76cb6c78947f906f5dcb554d1b08a;hp=8f6655d341b19d7c6ad55781f4389ee13d0ece72;hb=a6c6b0df5492c2160ed97e3a376bdb2fe7c5ebc4;hpb=cd13d9691c46c2b2d6d459e9e6a76bed1c21b7a6 diff --git a/src/hb-import.c b/src/hb-import.c index 8f6655d..7520c03 100644 --- a/src/hb-import.c +++ b/src/hb-import.c @@ -1,5 +1,5 @@ /* HomeBank -- Free, easy, personal accounting for everyone. - * Copyright (C) 1995-2018 Maxime DOYEN + * Copyright (C) 1995-2019 Maxime DOYEN * * This file is part of HomeBank. * @@ -37,51 +37,1332 @@ extern struct HomeBank *GLOBALS; extern struct Preferences *PREFS; +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +static void +da_import_context_gen_txn_destroy(ImportContext *context) +{ +GList *list; + + DB( g_print("\n[import] free gen txn list\n") ); + list = g_list_first(context->gen_lst_txn); + while (list != NULL) + { + GenTxn *gentxn = list->data; + da_gen_txn_free(gentxn); + list = g_list_next(list); + } + g_list_free(context->gen_lst_txn); + context->gen_lst_txn = NULL; +} + + +static void +da_import_context_gen_acc_destroy(ImportContext *context) +{ +GList *list; + + DB( g_print("\n[import] free gen acc list\n") ); + list = g_list_first(context->gen_lst_acc); + while (list != NULL) + { + GenAcc *genacc = list->data; + da_gen_acc_free(genacc); + list = g_list_next(list); + } + g_list_free(context->gen_lst_acc); + context->gen_lst_acc = NULL; + +} + +static void +da_import_context_clear(ImportContext *context) +{ + DB( g_print("\n[import] context clear\n") ); + + da_import_context_gen_txn_destroy(context); + da_import_context_gen_acc_destroy(context); + context->gen_next_acckey = 1; +} -Account *import_create_account(gchar *name, gchar *number) + +void +da_import_context_destroy(ImportContext *context) { -Account *accitem, *existitem; +GList *list; + + DB( g_print("\n[import] context destroy\n") ); - //first check we do not have already this imported account - existitem = da_acc_get_by_imp_name(name); - if(existitem != NULL) - return existitem; + da_import_context_gen_txn_destroy(context); + da_import_context_gen_acc_destroy(context); + + DB( g_print(" free gen file list\n") ); + list = g_list_first(context->gen_lst_file); + while (list != NULL) + { + GenFile *genfile = list->data; + da_gen_file_free(genfile); + list = g_list_next(list); + } + g_list_free(context->gen_lst_file); + context->gen_lst_file = NULL; +} + + +void +da_import_context_new(ImportContext *context) +{ + context->gen_lst_file = NULL; - DB( g_print(" ** create acc: '%s' '%s'\n", name, number) ); + context->gen_lst_acc = NULL; + context->gen_lst_txn = NULL; + context->gen_next_acckey = 1; +} + + +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +GenFile * +da_gen_file_malloc(void) +{ + return g_malloc0(sizeof(GenFile)); +} + +void +da_gen_file_free(GenFile *genfile) +{ + if(genfile != NULL) + { + if(genfile->filepath != NULL) + g_free(genfile->filepath); + + g_free(genfile); + } +} + + +GenFile * +da_gen_file_get(GList *lst_file, guint32 key) +{ +GenFile *existfile = NULL; +GList *list; + + list = g_list_first(lst_file); + while (list != NULL) + { + GenFile *genfile = list->data; + + if( key == genfile->key ) + { + existfile = genfile; + break; + } + list = g_list_next(list); + } + return existfile; +} + + +static GenFile * +da_gen_file_get_by_name(GList *lst_file, gchar *filepath) +{ +GenFile *existfile = NULL; +GList *list; - accitem = da_acc_malloc(); - accitem->key = da_acc_get_max_key() + 1; - accitem->pos = da_acc_length() + 1; + DB( g_print("da_gen_file_get_by_name\n") ); - // existing named account ? - existitem = da_acc_get_by_name(name); - if(existitem != NULL) - accitem->imp_key = existitem->key; + list = g_list_first(lst_file); + while (list != NULL) + { + GenFile *genfile = list->data; + + DB( g_print(" strcasecmp '%s' '%s'\n", filepath, genfile->filepath) ); + + if(!strcasecmp(filepath, genfile->filepath)) + { + existfile = genfile; + DB( g_print(" found\n") ); + break; + } + list = g_list_next(list); + } + + return existfile; +} + + +GenFile * +da_gen_file_append_from_filename(ImportContext *ictx, gchar *filename) +{ +GenFile *genfile = NULL; +gint filetype; + + //todo: should check if its a file !! + + filetype = homebank_alienfile_recognize(filename); + + DB( g_print(" - filename '%s', type is %d\n", filename, filetype ) ); + + // we keep everything here + //if( (filetype == FILETYPE_OFX) || (filetype == FILETYPE_QIF) || (filetype == FILETYPE_CSV_HB) ) + //{ + GenFile *existgenfile; + + existgenfile = da_gen_file_get_by_name(ictx->gen_lst_file, filename); + if(existgenfile == NULL) + { + genfile = da_gen_file_malloc(); + genfile->filepath = g_strdup(filename); + genfile->filetype = filetype; + + //append to list + genfile->key = g_list_length (ictx->gen_lst_file) + 1; + ictx->gen_lst_file = g_list_append(ictx->gen_lst_file, genfile); + + } + //} + + return genfile; +} + + +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +GenAcc * +da_gen_acc_malloc(void) +{ + return g_malloc0(sizeof(GenAcc)); +} + +void +da_gen_acc_free(GenAcc *genacc) +{ + if(genacc != NULL) + { + if(genacc->name != NULL) + g_free(genacc->name); + if(genacc->number != NULL) + g_free(genacc->number); + + g_free(genacc); + } +} + + +GenAcc * +da_gen_acc_get_by_key(GList *lst_acc, guint32 key) +{ +GenAcc *existacc = NULL; +GList *list; + + list = g_list_first(lst_acc); + while (list != NULL) + { + GenAcc *genacc = list->data; + + if( key == genacc->key ) + { + existacc = genacc; + break; + } + list = g_list_next(list); + } + return existacc; +} + + +static GenAcc * +da_gen_acc_get_by_name(GList *lst_acc, gchar *name) +{ +GenAcc *existacc = NULL; +GList *list; + + //DB( g_print("da_gen_acc_get_by_name\n") ); + + list = g_list_first(lst_acc); + while (list != NULL) + { + GenAcc *genacc = list->data; + + //DB( g_print(" strcasecmp '%s' '%s'\n", name, genacc->name) ); + + if(!strcasecmp(name, genacc->name)) + { + existacc = genacc; + //DB( g_print(" found\n") ); + break; + } + list = g_list_next(list); + } + + return existacc; +} - if(!existitem && *name != 0) - accitem->name = g_strdup(name); + +Account * +hb_import_acc_find_existing(gchar *name, gchar *number) +{ +Account *retacc = NULL; +GList *lacc, *list; + + DB( g_print("\n[import] acc_find_existing\n") ); + + DB( g_print(" - search number '%s'\n", number) ); + lacc = list = g_hash_table_get_values(GLOBALS->h_acc); + while (list != NULL) + { + Account *acc = list->data; + + //DB( g_print(" - eval acc '%s' or '%s'\n", acc->name, acc->number) ); + if(number != NULL && acc->number && strlen(acc->number) ) + { + //prefer identifying with number & search number into acc->number + if(g_strstr_len(number, -1, acc->number) != NULL) + { + DB( g_print(" - match number '%s'\n", acc->number) ); + retacc = acc; + break; + } + } + list = g_list_next(list); + } + + //# 1815964 only test name if all number test failed + //if not found try with name + if(retacc == NULL) + { + DB( g_print(" - search name '%s'\n", name) ); + list = g_list_first(lacc); + while (list != NULL) + { + Account *acc = list->data; + + //DB( g_print(" - eval acc '%s' or '%s'\n", acc->name, acc->number) ); + if(retacc == NULL && name != NULL) + { + if(g_strstr_len(name, -1, acc->name) != NULL) + { + DB( g_print(" - match name '%s'\n", acc->name) ); + retacc = acc; + break; + } + } + list = g_list_next(list); + } + } + + g_list_free(lacc); + + return retacc; +} + + +GenAcc * +hb_import_gen_acc_get_next(ImportContext *ictx, gint filetype, gchar *name, gchar *number) +{ +GenAcc *newacc; + + DB( g_print("\n[import] acc_get_next\n") ); + + DB( g_print(" - type='%d', name='%s', number='%s'\n", filetype, name, number) ); + + // try to find a same name account + if( name != NULL ) + { + newacc = da_gen_acc_get_by_name(ictx->gen_lst_acc, name); + if(newacc != NULL) + { + DB( g_print(" - found existing '%s'\n", name) ); + goto end; + } + } + + newacc = da_gen_acc_malloc(); + if(newacc) + { + newacc->kfile = ictx->curr_kfile; + newacc->key = ictx->gen_next_acckey++; + newacc->kacc = DST_ACC_GLOBAL; + + if(name != NULL) + { + newacc->is_unamed = FALSE; + newacc->name = g_strdup(name); + } + else + { + GenFile *genfile; + gchar *basename; + + newacc->is_unamed = TRUE; + + genfile = da_gen_file_get (ictx->gen_lst_file, newacc->kfile); + basename = g_path_get_basename(genfile->filepath); + + newacc->name = g_strdup_printf("%s %d", basename, newacc->key); + g_free(basename); + } + + if(number != NULL) + newacc->number = g_strdup(number); + + ictx->gen_lst_acc = g_list_append(ictx->gen_lst_acc, newacc); + } + + DB( g_print(" - create new '%s'\n", newacc->name) ); + +end: + newacc->filetype = filetype; + ictx->curr_kacc = newacc->key; + + return newacc; +} + + +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +GenTxn * +da_gen_txn_malloc(void) +{ + return g_malloc0(sizeof(GenTxn)); +} + + +void +da_gen_txn_free(GenTxn *gentxn) +{ +gint i; + + if(gentxn != NULL) + { + if(gentxn->account != NULL) + g_free(gentxn->account); + + if(gentxn->rawinfo != NULL) + g_free(gentxn->rawinfo); + if(gentxn->rawpayee != NULL) + g_free(gentxn->rawpayee); + if(gentxn->rawmemo != NULL) + g_free(gentxn->rawmemo); + + if(gentxn->date != NULL) + g_free(gentxn->date); + if(gentxn->info != NULL) + g_free(gentxn->info); + if(gentxn->payee != NULL) + g_free(gentxn->payee); + if(gentxn->memo != NULL) + g_free(gentxn->memo); + if(gentxn->category != NULL) + g_free(gentxn->category); + if(gentxn->tags != NULL) + g_free(gentxn->tags); + + for(i=0;isplits[i]; + + if(s->memo != NULL) + g_free(s->memo); + if(s->category != NULL) + g_free(s->category); + } + + if(gentxn->lst_existing != NULL) + { + g_list_free(gentxn->lst_existing); + gentxn->lst_existing = NULL; + } + + g_free(gentxn); + } +} + +static gint +da_gen_txn_compare_func(GenTxn *a, GenTxn *b) +{ +gint retval = (gint)(a->julian - b->julian); + + if(!retval) + retval = (ABS(a->amount) - ABS(b->amount)) > 0 ? 1 : -1; + return (retval); +} + + +GList * +da_gen_txn_sort(GList *list) +{ + return( g_list_sort(list, (GCompareFunc)da_gen_txn_compare_func)); +} + + +void +da_gen_txn_move(GenTxn *sgentxn, GenTxn *dgentxn) +{ + if(sgentxn != NULL && dgentxn != NULL) + { + memcpy(dgentxn, sgentxn, sizeof(GenTxn)); + memset(sgentxn, 0, sizeof(GenTxn)); + } +} + + +void +da_gen_txn_append(ImportContext *ctx, GenTxn *gentxn) +{ + gentxn->kfile = ctx->curr_kfile; + gentxn->kacc = ctx->curr_kacc; + gentxn->to_import = TRUE; + ctx->gen_lst_txn = g_list_append(ctx->gen_lst_txn, gentxn); +} + + +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ + + +static void _string_utf8_ucfirst(gchar **str) +{ +gint str_len; +gchar *first, *lc; + + if( *str == NULL ) + return; + + str_len = strlen(*str); + if( str_len <= 1 ) + return; + + first = g_utf8_strup(*str, 1); + lc = g_utf8_strdown( g_utf8_next_char(*str), -1 ); + g_free(*str); + *str = g_strjoin(NULL, first, lc, NULL); + g_free(first); + g_free(lc); +} + + +static gchar * +_string_concat(gchar *str, gchar *addon) +{ +gchar *retval; + + DB( g_print(" - concat '%s' + '%s'\n", str, addon) ); + + if(str == NULL) + retval = g_strdup(addon); else - accitem->name = g_strdup_printf(_("(account %d)"), accitem->key); + { + retval = g_strjoin(" ", str, addon, NULL); + g_free(str); + } - accitem->imp_name = g_strdup(name); + DB( g_print(" - retval='%s'\n", retval) ); + return retval; +} - if(number) - accitem->number = g_strdup(number); - //fixed 5.1.2 - accitem->kcur = GLOBALS->kcur; +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ - accitem->imported = TRUE; - da_acc_insert(accitem); +gchar *hb_import_filetype_char_get(GenAcc *genacc) +{ +gchar *retval = ""; + + switch(genacc->filetype) + { +#ifndef NOOFX + case FILETYPE_OFX: + retval = "OFX/QFX"; + break; +#endif + case FILETYPE_QIF: + retval = "QIF"; + break; - return accitem; + case FILETYPE_CSV_HB: + retval = "CSV"; + break; + } + + return retval; } +void +hb_import_load_all(ImportContext *ictx) +{ +GList *list; + + DB( g_print("\n[import] load all\n") ); + + da_import_context_clear (ictx); + + list = g_list_first(ictx->gen_lst_file); + while (list != NULL) + { + GenFile *genfile = list->data; + + if(genfile->filetype != FILETYPE_UNKNOWN) + { + //todo: move this to alien analysis + genfile->encoding = homebank_file_getencoding(genfile->filepath); + + ictx->curr_kfile = genfile->key; + + DB( g_print(" -> key = '%d'\n", genfile->key) ); + DB( g_print(" -> filepath = '%s'\n", genfile->filepath) ); + DB( g_print(" -> encoding = '%s'\n", genfile->encoding) ); + + genfile->loaded = FALSE; + genfile->invaliddatefmt = FALSE; + + switch(genfile->filetype) + { + #ifndef NOOFX + case FILETYPE_OFX: + homebank_ofx_import(ictx, genfile); + break; + #endif + case FILETYPE_QIF: + homebank_qif_import(ictx, genfile); + break; + case FILETYPE_CSV_HB: + homebank_csv_import(ictx, genfile); + break; + } + genfile->loaded = TRUE; + } + list = g_list_next(list); + } + + // sort by date + ictx->gen_lst_txn = da_gen_txn_sort(ictx->gen_lst_txn); +} + + +gint +hb_import_gen_acc_count_txn(ImportContext *ictx, GenAcc *genacc) +{ +GList *list; +gint count = 0; + + DB( g_print("\n[import] gen_acc_count_txn\n") ); + + genacc->n_txnall = 0; + genacc->n_txnimp = 0; + + list = g_list_first(ictx->gen_lst_txn); + while (list != NULL) + { + GenTxn *gentxn = list->data; + + if(gentxn->kacc == genacc->key) + { + genacc->n_txnall++; + count++; + + DB( g_print(" count %03d: gentxn in=%d dup=%d '%s'\n", count, gentxn->to_import, gentxn->is_dst_similar, gentxn->memo) ); + + if(gentxn->to_import) + genacc->n_txnimp++; + } + list = g_list_next(list); + } + return count; +} + + +/** + * uncheck duplicate within the import context files + */ +gint +hb_import_gen_txn_check_duplicate(ImportContext *ictx, GenAcc *genacc) +{ +GList *list1, *list2; +gint count = 0; + + DB( g_print("\n[import] gen_txn_check_duplicate\n") ); + + + list1 = g_list_first(ictx->gen_lst_txn); + while (list1 != NULL) + { + GenTxn *gentxn1 = list1->data; + + if( (genacc->key == gentxn1->kacc) && (gentxn1->julian != 0) ) //same account, valid date + { + list2 = g_list_next(list1); + while (list2 != NULL) + { + GenTxn *gentxn2 = list2->data; + + if( (gentxn2->julian > gentxn1->julian) ) + break; + + //todo: maybe reinforce controls here + if( (gentxn2->kacc == gentxn1->kacc) + && (gentxn2->julian == gentxn1->julian) + && (gentxn2->amount == gentxn1->amount) + && (hb_string_compare(gentxn2->memo, gentxn1->memo) == 0) + && (hb_string_compare(gentxn2->payee, gentxn1->payee) == 0) + ) + { + gentxn1->to_import = FALSE; + gentxn1->is_imp_similar = TRUE; + count++; + + 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) ); + + } + list2 = g_list_next(list2); + } + } + list1 = g_list_next(list1); + } + return count; +} + + +/** + * uncheck existing txn into target account + * + */ +gint +hb_import_gen_txn_check_target_similar(ImportContext *ictx, GenAcc *genacc) +{ +GList *list1, *list2; +gint count = 0; + + DB( g_print("\n[import] gen_txn_check_target_similar\n") ); + + list1 = g_list_first(ictx->gen_lst_txn); + while (list1 != NULL) + { + GenTxn *gentxn = list1->data; + + if(genacc->key == gentxn->kacc) + { + gentxn->to_import = TRUE; + gentxn->is_dst_similar = FALSE; + + if(genacc->kacc == DST_ACC_SKIP) + { + gentxn->to_import = FALSE; + } + else + { + Account *acc = da_acc_get(genacc->kacc); + + if(acc != NULL) + { + //clear previous existing + if(gentxn->lst_existing != NULL) + { + g_list_free(gentxn->lst_existing); + gentxn->lst_existing = NULL; + } + + // try to find existing transaction + list2 = g_queue_peek_tail_link(acc->txn_queue); + while (list2 != NULL) + { + Transaction *txn = list2->data; + + //break if the date goes below the gentxn date + gap + if( txn->date < (gentxn->julian - ictx->opt_daygap) ) + break; + + //#1586211 add of date tolerance + //todo: maybe reinforce controls here + if( ( txn->kacc == genacc->kacc ) + && ( gentxn->julian <= (txn->date + ictx->opt_daygap) ) + && ( gentxn->julian >= (txn->date - ictx->opt_daygap) ) + && ( txn->amount == gentxn->amount ) + ) + { + gentxn->lst_existing = g_list_append(gentxn->lst_existing, txn); + gentxn->to_import = FALSE; + gentxn->is_dst_similar = TRUE; + count++; + + 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) ); + } + + list2 = g_list_previous(list2); + } + } + + } + } + + list1 = g_list_next(list1); + } + + return count; +} + + +/** + * try to indentify xfer for OFX + * + */ +static gint +hb_import_gen_xfer_eval(ImportContext *ictx, GList *list) +{ +GList *root, *list1, *list2; +GList *match = NULL; +gint count = 0; + + DB( g_print("\n[import] gen xfer eval\n") ); + + root = list1 = g_list_first(list); + while (list1 != NULL) + { + Transaction *txn1 = list1->data; + GenAcc *acc; + + acc = da_gen_acc_get_by_key(ictx->gen_lst_acc, txn1->kacc); + + DB( g_print(" src: kacc:%d dat:%d amt:%.2f %s kfxacc:%d\n", txn1->kacc, txn1->date, txn1->amount, txn1->memo, txn1->kxferacc) ); + + if( (acc != NULL) && (acc->filetype == FILETYPE_OFX) ) + { + match = NULL; + count = 0; + list2 = g_list_next(root); + while (list2 != NULL) + { + Transaction *txn2 = list2->data; + + //DB( g_print(" -- chk: kacc:%d dat:%d amt:%.2f %s\n", txn2->kacc, txn2->date, txn2->amount, txn2->memo) ); + if( (txn2->date > txn1->date) ) + break; + + if( (txn2 == txn1) || (txn2->paymode == PAYMODE_INTXFER) ) + goto next; + + //todo: maybe reinforce controls here + if( (txn2->kacc != txn1->kacc) + && (txn2->date == txn1->date) + && (txn2->amount == -txn1->amount) + && (hb_string_compare(txn2->memo, txn1->memo) == 0) + ) + { + DB( g_print(" match: kacc:%d dat:%d amt:%.2f %s kfxacc:%d\n", txn2->kacc, txn2->date, txn2->amount, txn2->memo, txn2->kxferacc) ); + match = g_list_append(match, txn2); + count++; + } + next: + list2 = g_list_next(list2); + } + + if(count == 1) //we found a single potential xfer, transform it + { + Transaction *txn2 ; + + DB( g_print(" single found => convert both\n") ); + + list2 = g_list_first(match); + txn2 = list2->data; + + + txn1->paymode = PAYMODE_INTXFER; + transaction_xfer_change_to_child(txn1, txn2); + + /*list2 = g_list_first(match); + txn2 = list2->data; + + txn1->paymode = PAYMODE_INTXFER; + txn1->kxferacc = txn2->kacc; + + txn2->paymode = PAYMODE_INTXFER; + txn2->kxferacc = txn1->kacc; + */ + } + // if more than one, we cannot be sure + g_list_free(match); + } + + list1 = g_list_next(list1); + } + + return count; +} + + +/** + * apply the user option: date format, payee/memo/info mapping + * + */ +gboolean +hb_import_option_apply(ImportContext *ictx, GenAcc *genacc) +{ +GList *list; + + DB( g_print("\n[import] option apply\n") ); + + DB( g_print(" - type=%d\n", genacc->filetype) ); + + genacc->n_txnbaddate = 0; + + list = g_list_first(ictx->gen_lst_txn); + while (list != NULL) + { + GenTxn *gentxn = list->data; + + if(gentxn->kacc == genacc->key) + { + if(genacc->filetype != FILETYPE_OFX) + { + gentxn->julian = hb_date_get_julian(gentxn->date, ictx->opt_dateorder); + if( gentxn->julian == 0 ) + { + genacc->n_txnbaddate++; + } + } + + if(genacc->filetype == FILETYPE_OFX) + { + DB( g_print(" - ofx option apply\n") ); + + g_free(gentxn->payee); + g_free(gentxn->memo); + g_free(gentxn->info); + gentxn->payee = NULL; + gentxn->memo = NULL; + gentxn->info = NULL; + + // OFX:check_number + gentxn->info = g_strdup(gentxn->rawinfo); + + //#1791482 map name to info (concat only) + switch(ictx->opt_ofxname) + { + //ofxname is stored into rawpayee + case 1: + gentxn->memo = g_strdup(gentxn->rawpayee); + break; + case 2: + gentxn->payee = g_strdup(gentxn->rawpayee); + break; + case 3: + g_free(gentxn->info); + gentxn->info = _string_concat(gentxn->rawinfo, gentxn->rawpayee); + break; + } + + if(gentxn->rawmemo != NULL) + { + switch(ictx->opt_ofxmemo) + { + //case 0: ignore + case 1: //add to info + gentxn->info = _string_concat(gentxn->info, gentxn->rawmemo); + break; + + case 2: //add to memo + gentxn->memo = _string_concat(gentxn->memo, gentxn->rawmemo); + break; + + case 3: //add to payee + gentxn->payee = _string_concat(gentxn->payee, gentxn->rawmemo); + break; + } + } + + DB( g_print(" - payee is '%s'\n", gentxn->payee) ); + DB( g_print(" - memo is '%s'\n", gentxn->memo) ); + DB( g_print(" - info is '%s'\n", gentxn->info) ); + DB( g_print("\n") ); + + } + else + if(genacc->filetype == FILETYPE_QIF) + { + DB( g_print(" - qif option apply\n") ); + + g_free(gentxn->payee); + g_free(gentxn->memo); + gentxn->payee = NULL; + gentxn->memo = NULL; + + if(!ictx->opt_qifswap) + { + gentxn->payee = g_strdup(gentxn->rawpayee); + if(ictx->opt_qifmemo) + gentxn->memo = g_strdup(gentxn->rawmemo); + } + else + { + gentxn->payee = g_strdup(gentxn->rawmemo); + if(ictx->opt_qifmemo) + gentxn->memo = g_strdup(gentxn->rawpayee); + } + + DB( g_print(" - payee is '%s'\n", gentxn->payee) ); + DB( g_print(" - memo is '%s'\n", gentxn->memo) ); + + } + else + if(genacc->filetype == FILETYPE_CSV_HB) + { + DB( g_print(" - csv option apply\n") ); + + //#1791656 missing: info, payee and tagsg_freg_free(gentxn->payee); + g_free(gentxn->payee); + g_free(gentxn->memo); + g_free(gentxn->info); + + gentxn->payee = g_strdup(gentxn->rawpayee); + gentxn->memo = g_strdup(gentxn->rawmemo); + gentxn->info = g_strdup(gentxn->rawinfo); + } + + //at last do ucfirst + if( (ictx->opt_ucfirst == TRUE) ) + { + _string_utf8_ucfirst(&gentxn->memo); + _string_utf8_ucfirst(&gentxn->payee); + //category ? + } + + } + list = g_list_next(list); + } + + DB( g_print(" - nb_err=%d\n", genacc->n_txnbaddate) ); + + return genacc->n_txnbaddate == 0 ? TRUE : FALSE; +} + + +/** + * convert a GenTxn to a Transaction + * + */ +Transaction * +hb_import_convert_txn(GenAcc *genacc, GenTxn *gentxn) +{ +Transaction *newope; +Account *accitem; +Payee *payitem; +Category *catitem; +gint nsplit; + + DB( g_print("\n[import] convert txn\n") ); + + newope = NULL; + + DB( g_print(" - gentxt %s %s %s\n", gentxn->account, gentxn->date, gentxn->memo) ); + DB( g_print(" - genacc '%s' '%p'\n", gentxn->account, genacc) ); + + if( genacc != NULL) + { + newope = da_transaction_malloc(); + + newope->kacc = genacc->kacc; + newope->date = gentxn->julian; + newope->paymode = gentxn->paymode; + newope->info = g_strdup(gentxn->info); + newope->memo = g_strdup(gentxn->memo); + newope->amount = gentxn->amount; + + if(newope->amount > 0) + newope->flags |= OF_INCOME; + + //#773282 invert amount for ccard accounts + //todo: manage this (qif), it is not set to true anywhere + //if(ictx->is_ccard) + // gentxn->amount *= -1; + + // payee + append + if( gentxn->payee != NULL ) + { + payitem = da_pay_get_by_name(gentxn->payee); + if(payitem == NULL) + { + //DB( g_print(" -> append pay: '%s'\n", item->payee ) ); + + payitem = da_pay_malloc(); + payitem->name = g_strdup(gentxn->payee); + //payitem->imported = TRUE; + da_pay_append(payitem); + + //ictx->cnt_new_pay += 1; + } + newope->kpay = payitem->key; + } + + // LCategory of transaction + // L[Transfer account name] + // LCategory of transaction/Class of transaction + // L[Transfer account]/Class of transaction + if( gentxn->category != NULL ) + { + if(g_str_has_prefix(gentxn->category, "[")) // this is a transfer account name + { + gchar *accname; + + //DB ( g_print(" -> transfer to: '%s'\n", item->category) ); + + //remove brackets + accname = hb_strdup_nobrackets(gentxn->category); + + // search target account + append if not exixts + accitem = da_acc_get_by_name(accname); + if(accitem == NULL) + { + DB( g_print(" -> append int xfer dest acc: '%s'\n", accname ) ); + + accitem = da_acc_malloc(); + accitem->name = g_strdup(accname); + //accitem->imported = TRUE; + //accitem->imp_name = g_strdup(accname); + da_acc_append(accitem); + } + + newope->kxferacc = accitem->key; + newope->paymode = PAYMODE_INTXFER; + + g_free(accname); + } + else + { + //DB ( g_print(" -> append cat: '%s'\n", item->category) ); + + catitem = da_cat_append_ifnew_by_fullname(gentxn->category); + if( catitem != NULL ) + { + //ictx->cnt_new_cat += 1; + newope->kcat = catitem->key; + } + } + } + + //#1791656 miss tags also... + if( gentxn->tags != NULL ) + { + g_free(newope->tags); + newope->tags = tags_parse(gentxn->tags); + } + + // splits, if not a xfer + if( gentxn->paymode != PAYMODE_INTXFER ) + { + if( gentxn->nb_splits > 0 ) + { + newope->splits = da_split_new(); + for(nsplit=0;nsplitnb_splits;nsplit++) + { + GenSplit *s = &gentxn->splits[nsplit]; + Split *hbs; + guint32 kcat = 0; + + DB( g_print(" -> append split %d: '%s' '%.2f' '%s'\n", nsplit, s->category, s->amount, s->memo) ); + + if( s->category != NULL ) + { + catitem = da_cat_append_ifnew_by_fullname(s->category); + if( catitem != NULL ) + { + kcat = catitem->key; + } + } + + //todo: remove this when no more use || + hb_string_replace_char('|', s->memo); + hbs = da_split_malloc (); + hbs->kcat = kcat; + hbs->memo = g_strdup(s->memo); + hbs->amount = s->amount; + da_splits_append(newope->splits, hbs); + hbs = NULL; + } + } + } + + newope->flags |= OF_ADDED; + if( newope->amount > 0 ) + newope->flags |= OF_INCOME; + + if( gentxn->reconciled ) + newope->status = TXN_STATUS_RECONCILED; + else + if( gentxn->cleared ) + newope->status = TXN_STATUS_CLEARED; + } + return newope; +} + + +void +hb_import_apply(ImportContext *ictx) +{ +GList *list, *lacc; +GList *txnlist; +guint32 kcommon = 0; +guint changes = 0; + + DB( g_print("\n[import] apply\n") ); + + //create accounts + list = g_list_first(ictx->gen_lst_acc); + while (list != NULL) + { + GenAcc *genacc = list->data; + + DB( g_print(" #1 genacc: %d %s %s => %d\n", genacc->key, genacc->name, genacc->number, genacc->kacc) ); + + //we do create the common account once + if( (genacc->kacc == DST_ACC_GLOBAL) ) + { + if( kcommon == 0 ) + { + Account *acc = da_acc_malloc (); + + acc->name = g_strdup(_("imported account")); + if( da_acc_append(acc) ) + { + kcommon = acc->key; + changes++; + } + } + + genacc->kacc = kcommon; + } + else + if( (genacc->kacc == DST_ACC_NEW) ) + { + Account *acc = da_acc_malloc (); + + acc->name = g_strdup(genacc->name); + if( da_acc_append(acc) ) + { + acc->number = g_strdup(genacc->number); + acc->initial = genacc->initial; + + //store the target acc key + genacc->kacc = acc->key; + changes++; + } + } + + list = g_list_next(list); + } + + // insert every transactions into a temporary list + // we do this to keep a finished real txn list for detect xfer below + DB( g_print(" #2 insert txn\n") ); + + txnlist = NULL; + lacc = g_list_first(ictx->gen_lst_acc); + while (lacc != NULL) + { + GenAcc *genacc = lacc->data; + + if(genacc->kacc != DST_ACC_SKIP) + { + list = g_list_first(ictx->gen_lst_txn); + while (list != NULL) + { + GenTxn *gentxn = list->data; + + if(gentxn->kacc == genacc->key && gentxn->to_import == TRUE) + { + Transaction *txn, *dtxn; + + txn = hb_import_convert_txn(genacc, gentxn); + if( txn ) + { + dtxn = transaction_add(NULL, txn); + txnlist = g_list_append(txnlist, dtxn); + da_transaction_free(txn); + //#1820618 forgot to report changes count + changes++; + } + } + list = g_list_next(list); + } + } + lacc = g_list_next(lacc); + } + + //auto assign + DB( g_print(" call auto assign\n") ); + transaction_auto_assign(txnlist, 0); + + //check for ofx internal xfer + DB( g_print(" call hb_import_gen_xfer_eval\n") ); + hb_import_gen_xfer_eval(ictx, txnlist); + + g_list_free(txnlist); + + GLOBALS->changes_count += changes; + +} + + +#if MYDEBUG +void _import_context_debug_file_list(ImportContext *ctx) +{ +GList *list; + + g_print("\n--debug-- file list %d\n", g_list_length(ctx->gen_lst_file) ); + + list = g_list_first(ctx->gen_lst_file); + while (list != NULL) + { + GenFile *item = list->data; + + g_print(" genfile: %d '%s' '%s'\ndf=%d invalid=%d\n", item->key, item->filepath, item->encoding, item->datefmt, item->invaliddatefmt); + + list = g_list_next(list); + } + +} + +void _import_context_debug_acc_list(ImportContext *ctx) +{ +GList *list; + + g_print("\n--debug-- acc list %d\n", g_list_length(ctx->gen_lst_acc) ); + + list = g_list_first(ctx->gen_lst_acc); + while (list != NULL) + { + GenAcc *item = list->data; + + g_print(" genacc: %d %s %s => %d\n", item->key, item->name, item->number, item->kacc); + + list = g_list_next(list); + } + +} + + +void _import_context_debug_txn_list(ImportContext *ctx) +{ +GList *list; + + g_print("\n--debug-- txn list %d\n", g_list_length(ctx->gen_lst_txn) ); + + list = g_list_first(ctx->gen_lst_txn); + while (list != NULL) + { + GenTxn *item = list->data; + + g_print(" gentxn: (%d) %s %s (%d) %s %.2f\n", item->kfile, item->account, item->date, item->julian, item->memo, item->amount); + + list = g_list_next(list); + } + +} + +#endif