+}
+
+
+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;nsplit<gentxn->nb_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