GnuCash  5.6-150-g038405b370+
import-backend.cpp
1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or *
3  * modify it under the terms of the GNU General Public License as *
4  * published by the Free Software Foundation; either version 2 of *
5  * the License, or (at your option) any later version. *
6  * *
7  * This program is distributed in the hope that it will be useful, *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
10  * GNU General Public License for more details. *
11  * *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact: *
14  * *
15  * Free Software Foundation Voice: +1-617-542-5942 *
16  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
17  * Boston, MA 02110-1301, USA gnu@gnu.org *
18 \********************************************************************/
29 #include <config.h>
30 
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 #include <stdlib.h>
34 #include <math.h>
35 
36 #include <errno.h>
37 
38 #include "import-backend.h"
39 #include "import-utilities.h"
40 #include "Account.h"
41 #include "Account.hpp"
42 #include "Query.h"
43 #include "gnc-engine.h"
44 #include "engine-helpers.h"
45 #include "gnc-prefs.h"
46 #include "gnc-ui-util.h"
47 
48 #include <algorithm>
49 #include <string>
50 
51 #define GNCIMPORT_DESC "desc"
52 #define GNCIMPORT_MEMO "memo"
53 #define GNCIMPORT_PAYEE "payee"
54 
55 /********************************************************************\
56  * Constants *
57 \********************************************************************/
58 
59 static QofLogModule log_module = GNC_MOD_IMPORT;
60 
61 /********************************************************************\
62  * Forward declared prototypes *
63 \********************************************************************/
64 
65 static void matchmap_store_destination(Account* base_acc,
66  GNCImportTransInfo* trans_info,
67  gboolean use_match);
68 
69 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info);
70 
71 
72 /********************************************************************\
73  * Structures passed between the functions *
74 \********************************************************************/
75 
77 {
78  GNCImportMatchInfo *selected_match;
79  gboolean selected_manually;
80 };
81 
83 {
84  Transaction * trans;
85  Split * first_split;
86 
87  /* GList of GNCImportMatchInfo's, one for each possible duplicate match. */
88  GList * match_list;
89  GNCImportSelectedMatchInfo selected_match_info;
90 
91  GNCImportAction action;
92  GNCImportAction previous_action;
93 
94  /* A list of tokenized strings to use for bayesian matching purposes */
95  GList * match_tokens;
96 
97  /* In case of a single destination account it is stored here. */
98  Account *dest_acc;
99  gboolean dest_acc_selected_manually;
100 
101  /* Reference id to link gnc transaction to external object. E.g. aqbanking job id. */
102  guint32 ref_id;
103 
104  /* When updating a matched transaction, append Description and Notes instead of replacing */
105  gboolean append_text;
106 
107  /* Extra data we can use to build the balancing split. It may be passed on by the
108  * code that calls the generic importer */
109  gnc_numeric lsplit_price;
110  char *lsplit_action;
111  char *lsplit_memo;
112  char lsplit_rec_state;
113  time64 lsplit_rec_date;
114 
115  gnc_numeric lsplit_value;
116  /* Amount for the balancing split. This may be passed by the import front-
117  * ends or calculated. The latter is only possible when
118  * the destination account is known and may require an exchange rate
119  * if that account is not in the same commodity as the transaction. */
120  gnc_numeric lsplit_amount;
121  gboolean lsplit_amount_selected_manually;
122 };
123 
124 /* Some simple getters and setters for the above data types. */
125 
126 GList *
127 gnc_import_TransInfo_get_match_list (const GNCImportTransInfo *info)
128 {
129  g_assert (info);
130  return info->match_list;
131 }
132 
133 void
134 gnc_import_TransInfo_remove_top_match (GNCImportTransInfo *info)
135 {
136  g_return_if_fail (info);
137  info->match_list = g_list_remove (info->match_list, static_cast<gpointer>(info->match_list->data));
138  if (info->match_list)
139  info->selected_match_info.selected_match = static_cast<GNCImportMatchInfo*>(info->match_list->data);
140  else
141  {
142  info->selected_match_info.selected_match = nullptr;
143  gnc_import_TransInfo_set_action (info, GNCImport_ADD);
144  };
145 }
146 
147 Transaction *
148 gnc_import_TransInfo_get_trans (const GNCImportTransInfo *info)
149 {
150  g_assert (info);
151  return info->trans;
152 }
153 
154 gboolean
155 gnc_import_TransInfo_is_balanced (const GNCImportTransInfo *info)
156 {
157  g_assert (info);
159 }
160 
161 Split *
162 gnc_import_TransInfo_get_fsplit (const GNCImportTransInfo *info)
163 {
164  g_assert (info);
165  return info->first_split;
166 }
167 
169 gnc_import_TransInfo_get_selected_match (const GNCImportTransInfo *info)
170 {
171  g_assert (info);
172  return info->selected_match_info.selected_match;
173 }
174 
175 void
177  GNCImportMatchInfo *match,
178  gboolean selected_manually)
179 {
180  g_assert (info);
181  info->selected_match_info.selected_match = match;
182  info->selected_match_info.selected_manually = selected_manually;
183 }
184 
185 gboolean
186 gnc_import_TransInfo_get_match_selected_manually (const GNCImportTransInfo *info)
187 {
188  g_assert (info);
189  return info->selected_match_info.selected_manually;
190 }
191 
192 GNCImportAction
193 gnc_import_TransInfo_get_action (const GNCImportTransInfo *info)
194 {
195  g_assert (info);
196  return info->action;
197 }
198 
199 void
200 gnc_import_TransInfo_set_action (GNCImportTransInfo *info,
201  GNCImportAction action)
202 {
203  g_assert (info);
204  if (action != info->action)
205  {
206  info->previous_action = info->action;
207  info->action = action;
208  }
209 }
210 
211 Account *
212 gnc_import_TransInfo_get_destacc (const GNCImportTransInfo *info)
213 {
214  g_assert (info);
215  return info->dest_acc;
216 }
217 void gnc_import_TransInfo_set_destacc (GNCImportTransInfo *info,
218  Account *acc,
219  gboolean selected_manually)
220 {
221  g_assert (info);
222  info->dest_acc = acc;
223  info->dest_acc_selected_manually = selected_manually;
224 
225  /* Store the mapping to the other account in the MatchMap. */
226  if (selected_manually)
227  matchmap_store_destination (nullptr, info, false);
228 
229  trans_info_calculate_dest_amount (info);
230 }
231 
232 gboolean
234 {
235  g_assert (info);
236  return info->dest_acc_selected_manually;
237 }
238 
239 guint32
240 gnc_import_TransInfo_get_ref_id (const GNCImportTransInfo *info)
241 {
242  g_assert (info);
243  return info->ref_id;
244 }
245 
246 void
247 gnc_import_TransInfo_set_ref_id (GNCImportTransInfo *info,
248  guint32 ref_id)
249 {
250  g_assert (info);
251  info->ref_id = ref_id;
252 }
253 
254 gnc_numeric
255 gnc_import_TransInfo_get_price (const GNCImportTransInfo *info)
256 {
257  g_assert (info);
258  return info->lsplit_price;
259 }
260 
261 void
262 gnc_import_TransInfo_set_price (GNCImportTransInfo *info,
263  gnc_numeric lprice)
264 {
265  g_assert (info);
266  info->lsplit_price = lprice;
267  /* if a valid price is explicitly set, assume the user wants to
268  * use it to calculate balance split amount.
269  * Ensure this gets recalculated */
270  if (gnc_numeric_check (lprice) == 0)
271  {
272  info->lsplit_amount_selected_manually = false;
273  trans_info_calculate_dest_amount(info);
274  }
275 }
276 
277 gnc_numeric
278 gnc_import_TransInfo_get_dest_amount (const GNCImportTransInfo *info)
279 {
280  g_assert (info);
281  return info->lsplit_amount;
282 }
283 
284 gnc_numeric
285 gnc_import_TransInfo_get_dest_value (const GNCImportTransInfo *info)
286 {
287  g_assert (info);
288  return info->lsplit_value;
289 }
290 
291 void
293  GNCImportLastSplitInfo *lsplit)
294 {
295  g_assert (info);
296  if (lsplit)
297  {
298  info->lsplit_price = lsplit->price;
299  info->lsplit_action = g_strdup(lsplit->action);
300  info->lsplit_memo = g_strdup(lsplit->memo);
301  if (gnc_numeric_check (lsplit->amount) == 0)
302  {
303  info->lsplit_amount = lsplit->amount;
304  info->lsplit_amount_selected_manually = true;
305  }
306  /* Bayesian matching may have already set a candidate destination
307  * account. However if the csv data also provides one, the one from the
308  * csv data is preferred. */
309  if (lsplit->account)
310  info->dest_acc = lsplit->account;
311  info->lsplit_rec_state = lsplit->rec_state;
312  info->lsplit_rec_date = lsplit->rec_date;
313  }
314 }
315 
316 void
317 gnc_import_TransInfo_set_append_text (GNCImportTransInfo *info,
318  gboolean append_text)
319 {
320  g_assert (info);
321  info->append_text = append_text;
322 }
323 
324 
325 Split *
327 {
328  g_assert (info);
329  return info->split;
330 }
331 
332 gint
334 {
335  if (info)
336  return info->probability;
337  else
338  return 0;
339 }
340 
341 void gnc_import_TransInfo_delete (GNCImportTransInfo *info)
342 {
343  if (info)
344  {
345  g_list_free_full (info->match_list, g_free);
346  /*If the transaction exists and is still open, it must be destroyed*/
347  if (xaccTransIsOpen(info->trans))
348  {
349  xaccTransDestroy(info->trans);
350  xaccTransCommitEdit(info->trans);
351  }
352  g_list_free_full (info->match_tokens, g_free);
353  g_free(info->lsplit_action);
354  g_free(info->lsplit_memo);
355 
356  g_free(info);
357  }
358 }
359 
360 GdkPixbuf* gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget * widget)
361 {
362  constexpr gint height = 15;
363  constexpr gint width_each_bar = 7;
364  constexpr gint width_first_bar = 1;
365  constexpr gint num_colors = 5;
366  constexpr size_t xpm_size = 2 + num_colors + height;
367  gchar * xpm[xpm_size];
368 
369  g_assert(settings);
370  g_assert(widget);
371 
372  auto score = std::max (0, score_original);
373 
374  /* Add size definition to xpm */
375  xpm[0] = g_strdup_printf("%d%s%d%s%d%s", (width_each_bar * score) + width_first_bar/*width*/, " ", height, " ", num_colors, " 1"/*characters per pixel*/);
376 
377  /* Define used colors */
378  xpm[1] = g_strdup(" c None");
379  xpm[2] = g_strdup("g c green");
380  xpm[3] = g_strdup("y c yellow");
381  xpm[4] = g_strdup("r c red");
382  xpm[5] = g_strdup("b c black");
383 
384  auto add_threshold = gnc_import_Settings_get_add_threshold(settings);
385  auto clear_threshold = gnc_import_Settings_get_clear_threshold(settings);
386  for (int i = 0; i < height; i++)
387  {
388  xpm[num_colors+1+i] = g_new0(char, (width_each_bar * score) + width_first_bar + 1);
389  for (int j = 0; j <= score; j++)
390  {
391  if (j == 0)
392  strcat(xpm[num_colors+1+i], "b");
393  else if (i == 0 || i == height - 1)
394  strcat(xpm[num_colors+1+i], "bbbbbb ");
395  else if (j <= add_threshold)
396  strcat(xpm[num_colors+1+i], "brrrrb ");
397  else if (j >= clear_threshold)
398  strcat(xpm[num_colors+1+i], "bggggb ");
399  else
400  strcat(xpm[num_colors+1+i], "byyyyb ");
401  }
402  }
403  GError *err = nullptr;
404  std::string xpm_str = "/* XPM */\nstatic char * XFACE[] = {\n";
405 
406  for (auto i = 0UL; i < xpm_size - 1; i++)
407  {
408  xpm_str += "\"";
409  xpm_str += xpm[i];
410  xpm_str += "\",\n";
411  g_free(xpm[i]);
412  }
413  xpm_str += "};";
414 
415  auto gstream = g_memory_input_stream_new_from_data(xpm_str.c_str(), -1,
416  nullptr);
417  auto retval =
418  gdk_pixbuf_new_from_stream(G_INPUT_STREAM(gstream), nullptr, &err);
419  g_object_unref(gstream);
420 
421  if (!retval && err)
422  PERR("Failed to create pixbuf from XPM data: %s", err->message);
423 
424  return retval;
425 }
426 
427 /*************************************************************************
428  * MatchMap related functions (storing and retrieving)
429  */
430 
431 /* Tokenize a string and append the tokens to an existing GList
432  * (or an empty GList)
433  */
434 static GList*
435 tokenize_string(GList* existing_tokens, const char *string)
436 {
437  char **tokenized_strings = g_strsplit(string, " ", 0);
438  char **stringpos = tokenized_strings;
439 
440  /* add each unique non-empty token to the token GList */
441  while (stringpos && *stringpos)
442  {
443  if ((strlen(*stringpos) > 0) &&
444  (!g_list_find_custom (existing_tokens, *stringpos, (GCompareFunc)g_strcmp0)))
445  existing_tokens = g_list_prepend(existing_tokens, g_strdup(*stringpos));
446 
447  stringpos++;
448  }
449 
450  /* free up the strings that g_strsplit() created */
451  g_strfreev(tokenized_strings);
452 
453  return existing_tokens;
454 }
455 
456 /* create and return a list of tokens for a given transaction info. */
457 static GList*
458 TransactionGetTokens(GNCImportTransInfo *info)
459 {
460 
461  g_return_val_if_fail (info, nullptr);
462  if (info->match_tokens) return info->match_tokens;
463 
464  auto transaction = gnc_import_TransInfo_get_trans(info);
465  g_assert(transaction);
466 
467  /* make tokens from the transaction description */
468  auto text = xaccTransGetDescription(transaction);
469  GList *tokens = nullptr;
470  tokens = tokenize_string(tokens, text);
471 
472  /* The day of week the transaction occurred is a good indicator of
473  * what account this transaction belongs in. Get the date and convert
474  * it to day of week as a token
475  */
476  auto transtime = xaccTransGetDate(transaction);
477  auto tm_struct = gnc_gmtime(&transtime);
478  char local_day_of_week[16];
479  if (!qof_strftime(local_day_of_week, sizeof(local_day_of_week), "%A", tm_struct))
480  PERR("TransactionGetTokens: error, strftime failed\n");
481  gnc_tm_free (tm_struct);
482  /* we cannot add a locally allocated string to this array, dup it so
483  * it frees the same way the rest do
484  */
485  tokens = g_list_prepend(tokens, g_strdup(local_day_of_week));
486 
487  /* make tokens from the memo of each split of this transaction */
488  for (GList *node=xaccTransGetSplitList (transaction); node; node=node->next)
489  {
490  text = xaccSplitGetMemo(static_cast<Split*>(node->data));
491  tokens = tokenize_string(tokens, text);
492  }
493 
494  info->match_tokens = tokens;
495  return tokens;
496 }
497 
498 /* searches using the GNCImportTransInfo through all existing transactions
499  * if there is an exact match of the description and memo
500  */
501 static Account *
502 matchmap_find_destination (Account *base_acc, GNCImportTransInfo *info)
503 {
504  g_assert (info);
505  auto orig_acc = (base_acc ? base_acc : xaccSplitGetAccount
507 
508  Account *result = nullptr;
509  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_USE_BAYES))
510  {
511  /* get the tokens for this transaction* */
512  GList* tokens = TransactionGetTokens(info);
513 
514  /* try to find the destination account for this transaction from its tokens */
515  result = gnc_account_imap_find_account_bayes(orig_acc, tokens);
516 
517  }
518  else
519  result = gnc_account_imap_find_account
520  (orig_acc, GNCIMPORT_DESC,
522 
523  return result;
524 }
525 
530 static void
531 matchmap_store_destination (Account *base_acc,
532  GNCImportTransInfo *trans_info,
533  gboolean use_match)
534 {
535  g_assert (trans_info);
536 
537  /* This will store the destination account of the selected match if
538  the reconcile match selected has only two splits. */
539  Account *dest = nullptr;
540  if (use_match)
541  dest = xaccSplitGetAccount
545  else
546  dest = gnc_import_TransInfo_get_destacc (trans_info);
547  if (!dest)
548  return;
549 
550  auto orig_acc = (base_acc ? base_acc : xaccSplitGetAccount
551  (gnc_import_TransInfo_get_fsplit (trans_info)));
552 
553  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_USE_BAYES))
554  {
555  /* tokenize this transaction */
556  auto tokens = TransactionGetTokens(trans_info);
557 
558  /* add the tokens to the imap with the given destination account */
559  gnc_account_imap_add_account_bayes(orig_acc, tokens, dest);
560  }
561  else
562  {
563  auto desc = xaccTransGetDescription
564  (gnc_import_TransInfo_get_trans (trans_info));
565  auto memo = xaccSplitGetMemo
566  (gnc_import_TransInfo_get_fsplit (trans_info));
567 
568  if (desc && *desc)
569  gnc_account_imap_add_account (orig_acc, GNCIMPORT_DESC, desc, dest);
570  if (memo && *memo)
571  gnc_account_imap_add_account (orig_acc, GNCIMPORT_MEMO, memo, dest);
572  }
573 }
574 
575 
576 
579 void split_find_match (GNCImportTransInfo * trans_info,
580  Split * split,
581  gint display_threshold,
582  gint date_threshold,
583  gint date_not_threshold,
584  double fuzzy_amount_difference)
585 {
586  gint prob = 0;
587 
588  auto new_trans = gnc_import_TransInfo_get_trans (trans_info);
589  auto new_trans_fsplit = gnc_import_TransInfo_get_fsplit (trans_info);
590 
591  /* Matching heuristics */
592 
593  /* Amount heuristics */
594  auto downloaded_split_amount =
595  gnc_numeric_to_double (xaccSplitGetAmount(new_trans_fsplit));
596  /*DEBUG(" downloaded_split_amount=%f", downloaded_split_amount);*/
597  auto match_split_amount = gnc_numeric_to_double(xaccSplitGetAmount(split));
598  /*DEBUG(" match_split_amount=%f", match_split_amount);*/
599  if (fabs(downloaded_split_amount - match_split_amount) < 1e-6)
600  /* bug#347791: Double type shouldn't be compared for exact
601  equality, so we're using fabs() instead. */
602  /*if (gnc_numeric_equal(xaccSplitGetAmount
603  (new_trans_fsplit),
604  xaccSplitGetAmount(split)))
605  -- gnc_numeric_equal is an expensive function call */
606  {
607  prob = prob + 3;
608  /*DEBUG("heuristics: probability + 3 (amount)");*/
609  }
610  else if (fabs (downloaded_split_amount - match_split_amount) <=
611  fuzzy_amount_difference)
612  {
613  /* ATM fees are sometimes added directly in the transaction.
614  So you withdraw 100$ and get charged 101,25$ in the same
615  transaction */
616  prob = prob + 2;
617  /*DEBUG("heuristics: probability + 2 (amount)");*/
618  }
619  else
620  {
621  /* If a transaction's amount doesn't match within the
622  threshold, it's very unlikely to be the same transaction
623  so we give it an extra -5 penalty */
624  prob = prob - 5;
625  /* DEBUG("heuristics: probability - 1 (amount)"); */
626  }
627 
628  /* Date heuristics */
629  auto match_time = xaccTransGetDate (xaccSplitGetParent (split));
630  auto download_time = xaccTransGetDate (new_trans);
631  auto datediff_day = llabs(match_time - download_time) / 86400;
632  /* Sorry, there are not really functions around at all that
633  provide for less hacky calculation of days of date
634  differences. Whatever. On the other hand, the difference
635  calculation itself will work regardless of month/year
636  turnarounds. */
637  /*DEBUG("diff day %d", datediff_day);*/
638  if (datediff_day == 0)
639  {
640  prob = prob + 3;
641  /*DEBUG("heuristics: probability + 3 (date)");*/
642  }
643  else if (datediff_day <= date_threshold)
644  {
645  prob = prob + 2;
646  /*DEBUG("heuristics: probability + 2 (date)");*/
647  }
648  else if (datediff_day > date_not_threshold)
649  {
650  /* Extra penalty if that split lies awfully far away from
651  the given one. */
652  prob = prob - 5;
653  /*DEBUG("heuristics: probability - 5 (date)"); */
654  /* Changed 2005-02-21: Revert the hard-limiting behaviour
655  back to the previous large penalty. (Changed 2004-11-27:
656  The penalty is so high that we can forget about this
657  split anyway and skip the rest of the tests.) */
658  }
659 
660  /* Check if date and amount are identical */
661  auto update_proposed = (prob < 6);
662 
663  /* Check number heuristics */
664  auto new_trans_str = gnc_get_num_action(new_trans, new_trans_fsplit);
665  if (new_trans_str && *new_trans_str)
666  {
667  char *endptr;
668  auto conversion_ok = true;
669 
670  /* To distinguish success/failure after strtol call */
671  errno = 0;
672  auto new_trans_number = strtol(new_trans_str, &endptr, 10);
673  /* Possible addressed problems: over/underflow, only non
674  numbers on string and string empty */
675  conversion_ok = !(errno || endptr == new_trans_str);
676 
677  auto split_str = gnc_get_num_action (xaccSplitGetParent (split), split);
678  errno = 0;
679  auto split_number = strtol(split_str, &endptr, 10);
680  conversion_ok = !(errno || endptr == split_str);
681 
682  if ( (conversion_ok && (split_number == new_trans_number)) ||
683  (g_strcmp0(new_trans_str, split_str) == 0) )
684  {
685  /* An exact match of the Check number gives a +4 */
686  prob += 4;
687  /*DEBUG("heuristics: probability + 4 (Check number)");*/
688  }
689  else if (strlen(new_trans_str) > 0 && strlen(split_str) > 0)
690  {
691  /* If both number are not empty yet do not match, add a
692  little extra penalty */
693  prob -= 2;
694  }
695  }
696 
697  /* Memo heuristics */
698  auto memo = xaccSplitGetMemo(new_trans_fsplit);
699  if (memo && *memo)
700  {
701  if (safe_strcasecmp(memo, xaccSplitGetMemo(split)) == 0)
702  {
703  /* An exact match of memo gives a +2 */
704  prob = prob + 2;
705  /* DEBUG("heuristics: probability + 2 (memo)"); */
706  }
707  else if ((strncasecmp(memo, xaccSplitGetMemo(split),
708  strlen(xaccSplitGetMemo(split)) / 2) == 0))
709  {
710  /* Very primitive fuzzy match worth +1. This matches the
711  first 50% of the strings to skip annoying transaction
712  number some banks seem to include in the memo but someone
713  should write something more sophisticated */
714  prob = prob + 1;
715  /*DEBUG("heuristics: probability + 1 (memo)"); */
716  }
717  }
718 
719  /* Description heuristics */
720  auto descr = xaccTransGetDescription(new_trans);
721  if (descr && *descr)
722  {
723  if (safe_strcasecmp(descr,
725  {
726  /*An exact match of Description gives a +2 */
727  prob = prob + 2;
728  /*DEBUG("heuristics: probability + 2 (description)");*/
729  }
730  else if ((strncasecmp(descr,
732  strlen(xaccTransGetDescription (new_trans)) / 2) == 0))
733  {
734  /* Very primitive fuzzy match worth +1. This matches the
735  first 50% of the strings to skip annoying transaction
736  number some banks seem to include in the description but someone
737  should write something more sophisticated */
738  prob = prob + 1;
739  /*DEBUG("heuristics: probability + 1 (description)"); */
740  }
741  }
742 
743  /* Is the probability high enough? Otherwise do nothing and return. */
744  if (prob < display_threshold)
745  return;
746 
747  /* The probability is high enough, so allocate an object
748  here. Allocating it only when it's actually being used is
749  probably quite some performance gain. */
750  auto match_info = g_new0(GNCImportMatchInfo, 1);
751 
752  match_info->probability = prob;
753  match_info->update_proposed = update_proposed;
754  match_info->split = split;
755  match_info->trans = xaccSplitGetParent(split);
756 
757 
758  /* Append that to the list. Do not use g_list_append because
759  it is slow. The list is sorted afterwards anyway. */
760  trans_info->match_list = g_list_prepend(trans_info->match_list, match_info);
761 }
762 
763 /***********************************************************************
764  */
765 
766 static char*
767 maybe_append_string (const char* match_string, const char* imp_string)
768 {
769  if (!(match_string && *match_string))
770  return g_strdup(imp_string);
771 
772  if (!(imp_string && *imp_string))
773  return nullptr;
774 
775  auto norm_match_string = g_utf8_normalize (match_string, -1, G_NORMALIZE_NFC);
776  auto norm_imp_string = g_utf8_normalize (imp_string, -1, G_NORMALIZE_NFC);
777 
778  char *retval = nullptr;
779  if (g_utf8_strlen (norm_imp_string, -1) > g_utf8_strlen (norm_match_string, -1) ||
780  !strstr (norm_match_string, norm_imp_string))
781  retval = g_strconcat(match_string, "|", imp_string, nullptr);
782 
783  g_free (norm_match_string);
784  g_free (norm_imp_string);
785  return retval;
786 
787 }
788 
789 /* Append or replace transaction description and notes
790  * depending on the Append checkbox
791  */
792 static void
793 update_desc_and_notes (const GNCImportTransInfo* trans_info)
794 {
795  auto selected_match = gnc_import_TransInfo_get_selected_match (trans_info);
796  auto imp_trans = gnc_import_TransInfo_get_trans (trans_info);
797 
798  if (trans_info->append_text)
799  {
800  auto match_trans = selected_match->trans;
801  auto repl_str =
802  maybe_append_string (xaccTransGetDescription(match_trans),
803  xaccTransGetDescription(imp_trans));
804  if (repl_str)
805  xaccTransSetDescription(match_trans, repl_str);
806  g_free (repl_str);
807 
808  repl_str =
809  maybe_append_string (xaccTransGetNotes(match_trans),
810  xaccTransGetNotes(imp_trans));
811  if (repl_str)
812  xaccTransSetNotes (match_trans, repl_str);
813  g_free (repl_str);
814  }
815  else
816  {
817  xaccTransSetDescription (selected_match->trans,
818  xaccTransGetDescription (imp_trans));
819  xaccTransSetNotes (selected_match->trans,
820  xaccTransGetNotes (imp_trans));
821  }
822 }
823 
824 static void
825 process_reconcile(Account *base_acc,
826  GNCImportTransInfo *trans_info,
827  GNCImportMatchInfo *selected_match)
828 {
829  /* Reconcile the matching transaction */
830  /*DEBUG("BeginEdit selected_match")*/
831  xaccTransBeginEdit(selected_match->trans);
832 
833  if (xaccSplitGetReconcile(selected_match->split) == NREC)
834  xaccSplitSetReconcile(selected_match->split, CREC);
835 
836  /* Set reconcile date to today */
837  xaccSplitSetDateReconciledSecs(selected_match->split, gnc_time (nullptr));
838 
839  /* Copy the online id to the reconciled transaction, so
840  * the match will be remembered */
841  auto online_id = gnc_import_get_split_online_id(trans_info->first_split);
842  if (online_id && *online_id)
843  gnc_import_set_split_online_id(selected_match->split, online_id);
844 
845  g_free (online_id);
846 
847  /* Done editing. */
848  /*DEBUG("CommitEdit selected_match")*/
849  xaccTransCommitEdit(selected_match->trans);
850 
851  /* Store the mapping to the other account in the MatchMap. */
852  matchmap_store_destination(base_acc, trans_info, true);
853 
854  /* Erase the downloaded transaction */
855  xaccTransDestroy(trans_info->trans);
856  /*DEBUG("CommitEdit trans")*/
857  xaccTransCommitEdit(trans_info->trans);
858  /* Very important: Make sure the freed transaction is not freed again! */
859  trans_info->trans = nullptr;
860 }
861 
864 gboolean
866  GNCImportTransInfo *trans_info)
867 {
868  g_assert (trans_info);
869  /*DEBUG("Iteration %d, action %d, split %s", i,
870  trans_info->action,
871  xaccTransGetDescription (gnc_import_TransInfo_get_trans
872  (trans_info)))*/
873  switch (gnc_import_TransInfo_get_action (trans_info))
874  {
875  case GNCImport_SKIP:
876  return false;
877  case GNCImport_ADD:
878  /* Transaction gets imported. */
879  if (!gnc_import_TransInfo_is_balanced(trans_info)
880  && gnc_import_TransInfo_get_destacc(trans_info))
881  {
882  /* Create the 'other' split. */
883  auto trans = gnc_import_TransInfo_get_trans (trans_info);
884  auto acct = gnc_import_TransInfo_get_destacc (trans_info);
885  auto split = xaccMallocSplit (gnc_account_get_book (acct));
886  xaccTransAppendSplit (trans, split);
887  xaccAccountInsertSplit (acct, split);
888  xaccSplitSetValue (split, trans_info->lsplit_value);
889  if (!gnc_numeric_zero_p (trans_info->lsplit_amount))
890  xaccSplitSetAmount (split, trans_info->lsplit_amount);
891  else
892  {
893  /* Bad! user asked to create a balancing split in an account with
894  * different currency/commodit than the transaction but didn't provide
895  * an exchange rate.
896  * Continue anyway pretenting split is in transaction currency. */
897  xaccSplitSetAmount (split, trans_info->lsplit_value);
898  PWARN("Missing exchange rate while adding transaction '%s', will assume rate of 1",
900  }
901  }
902 
904  /*Set reconcile date to today*/
906  gnc_time (nullptr));
907  /* Done editing. */
908  xaccTransCommitEdit(trans_info->trans);
909  xaccTransRecordPrice(trans_info->trans, PRICE_SOURCE_SPLIT_IMPORT);
910  return true;
911  case GNCImport_UPDATE:
912  {
913  auto selected_match = gnc_import_TransInfo_get_selected_match(trans_info);
914 
915  /* If there is no selection, ignore this transaction. */
916  if (!selected_match)
917  {
918  PWARN("No matching transaction to be cleared was chosen. Imported transaction will be ignored.");
919  break;
920  }
921 
922  /* Transaction gets not imported but the matching one gets
923  updated and reconciled. */
924  if (!gnc_import_MatchInfo_get_split(selected_match))
925  PERR("The split I am trying to update and reconcile is nullptr, shouldn't happen!");
926  else
927  {
928  /* Update and reconcile the matching transaction */
929  /*DEBUG("BeginEdit selected_match")*/
930  xaccTransBeginEdit(selected_match->trans);
931 
932  auto fsplit = gnc_import_TransInfo_get_fsplit(trans_info);
933  xaccTransSetDatePostedSecsNormalized(selected_match->trans,
935 
936  auto match_split_amount = xaccSplitGetAmount(selected_match->split);
937  xaccSplitSetAmount(selected_match->split, xaccSplitGetAmount(fsplit));
938  xaccSplitSetValue(selected_match->split, xaccSplitGetValue(fsplit));
939 
940  auto imbalance_value = gnc_import_TransInfo_get_dest_value(trans_info);
941  auto other_split = xaccSplitGetOtherSplit(selected_match->split);
942  if (!gnc_numeric_zero_p(imbalance_value) && other_split)
943  {
944  if (xaccSplitGetReconcile(other_split) == NREC)
945  {
946  xaccSplitSetValue(other_split, imbalance_value);
947  auto new_amt = gnc_import_TransInfo_get_dest_value(trans_info);
948  if (gnc_numeric_zero_p(new_amt))
949  {
950  auto other_split_amount = xaccSplitGetAmount(other_split);
951  auto price = gnc_numeric_div(match_split_amount, other_split_amount,
954 
955  new_amt = gnc_numeric_mul(xaccSplitGetAmount(fsplit), price,
958  }
959  xaccSplitSetAmount(other_split, new_amt);
960  }
961  else
962  {
963  /* else GC will automatically insert a split to equity
964  to balance the transaction */
965  PWARN("Updated transaction '%s', but not other split.",
966  xaccTransGetDescription(selected_match->trans));
967  }
968  }
969 
970  auto fs_memo = xaccSplitGetMemo (trans_info->first_split);
971  if (fs_memo && *fs_memo)
972  xaccSplitSetMemo(selected_match->split, fs_memo);
973 
974  update_desc_and_notes(trans_info);
975 
976  /*DEBUG("CommitEdit selected_match")*/
977  xaccTransCommitEdit(selected_match->trans);
978 
979  process_reconcile (base_acc, trans_info, selected_match);
980  }
981  }
982  return true;
983  case GNCImport_CLEAR:
984  {
985  auto selected_match = gnc_import_TransInfo_get_selected_match (trans_info);
986 
987  /* If there is no selection, ignore this transaction. */
988  if (!selected_match)
989  {
990  PWARN("No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
991  break;
992  }
993 
994  /* Transaction gets not imported but the matching one gets
995  reconciled. */
996  if (!gnc_import_MatchInfo_get_split (selected_match))
997  PERR("The split I am trying to reconcile is nullptr, shouldn't happen!");
998  else
999  {
1000  /* Reconcile the matching transaction */
1001  process_reconcile(base_acc, trans_info, selected_match);
1002  }
1003  }
1004  return true;
1005  default:
1006  DEBUG("Invalid GNCImportAction for this imported transaction.");
1007  break;
1008  }
1009  /*DEBUG("End");*/
1010  return false;
1011 }
1012 
1013 static GHashTable*
1014 hash_account_online_ids (Account *account)
1015 {
1016  auto acct_hash = g_hash_table_new_full
1017  (g_str_hash, g_str_equal, g_free, nullptr);
1018  for (auto split : xaccAccountGetSplits (account))
1019  {
1020  auto id = gnc_import_get_split_online_id (split);
1021  if (id && *id)
1022  g_hash_table_insert (acct_hash, (void*) id, GINT_TO_POINTER (1));
1023  }
1024  return acct_hash;
1025 }
1026 
1029 gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash)
1030 {
1031 
1032  /* Look for an online_id in the first split */
1033  auto source_split = xaccTransGetSplit(trans, 0);
1034  g_assert(source_split);
1035 
1036  auto source_online_id = gnc_import_get_split_online_id (source_split);
1037 
1038  // No online id, no point in continuing. We'd crash if we tried.
1039  if (!source_online_id)
1040  return false;
1041 
1042  // Create a hash per account of a hash of all split IDs. Then the
1043  // test below will be fast if we have many transactions to import.
1044  auto dest_acct = xaccSplitGetAccount (source_split);
1045 
1046  auto online_id_hash = static_cast<GHashTable*>(g_hash_table_lookup (acct_id_hash, dest_acct));
1047 
1048  if (!online_id_hash)
1049  {
1050  online_id_hash = hash_account_online_ids (dest_acct);
1051  g_hash_table_insert (acct_id_hash, dest_acct, online_id_hash);
1052  }
1053 
1054  auto online_id_exists = g_hash_table_contains (online_id_hash, source_online_id);
1055  g_free (source_online_id);
1056  return online_id_exists;
1057 }
1058 
1059 
1060 /* ******************************************************************
1061  */
1062 
1063 /* Calculate lsplit_amount based on knowledge gathered so far
1064  * If insufficient info is available (eg multi currency transaction with missing
1065  * exchange rate provided), set amount to 0 */
1066 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info)
1067 {
1068  info->lsplit_value = gnc_numeric_neg (xaccTransGetImbalanceValue (info->trans));
1069  if (!info->lsplit_amount_selected_manually)
1070  info->lsplit_amount = {0, 1};
1071 
1072  if (info->dest_acc)
1073  {
1074  auto tcurr = xaccTransGetCurrency(info->trans);
1075  auto dcurr = xaccAccountGetCommodity(info->dest_acc);
1076 
1077  if (gnc_numeric_zero_p(info->lsplit_value))
1078  return;
1079 
1080  if (gnc_commodity_equiv(tcurr, dcurr))
1081  info->lsplit_amount = info->lsplit_value;
1082  else if (info->lsplit_amount_selected_manually &&
1083  gnc_numeric_check(info->lsplit_amount) == 0)
1084  {
1085  /* Nothing to do, user has provided amount already */
1086  }
1087  else if (gnc_numeric_check(info->lsplit_price) == 0)
1088  {
1089  /* We are in a multi currency situation and have a valid price
1090  * Reminder: value = amount * price => amount = value / price */
1091  gnc_numeric inv_price = gnc_numeric_invert (info->lsplit_price);
1092  info->lsplit_amount = gnc_numeric_mul (info->lsplit_value,
1093  inv_price,
1096  }
1097  }
1098 }
1099 
1101 GNCImportTransInfo *
1102 gnc_import_TransInfo_new (Transaction *trans, Account *base_acc)
1103 {
1104  g_assert (trans);
1105 
1106  auto t_info = g_new0(GNCImportTransInfo, 1);
1107 
1108  t_info->trans = trans;
1109  /* Only use first split, the source split */
1110  auto split = xaccTransGetSplit(trans, 0);
1111  g_assert(split);
1112  t_info->first_split = split;
1113 
1114  /* Try to find a previously selected destination account
1115  string match for the ADD action */
1117  matchmap_find_destination (base_acc, t_info),
1118  false);
1119 
1120  return t_info;
1121 }
1122 
1123 
1125 static gint compare_probability (gconstpointer a,
1126  gconstpointer b)
1127 {
1128  return(((GNCImportMatchInfo *)b)->probability -
1129  ((GNCImportMatchInfo *)a)->probability);
1130 }
1131 
1136 void
1137 gnc_import_TransInfo_init_matches (GNCImportTransInfo *trans_info,
1138  GNCImportSettings *settings)
1139 {
1140  g_assert (trans_info);
1141 
1142  if (trans_info->match_list)
1143  {
1144  trans_info->match_list = g_list_sort(trans_info->match_list,
1145  compare_probability);
1146  auto best_match = static_cast<GNCImportMatchInfo*>(g_list_nth_data(trans_info->match_list, 0));
1147  gnc_import_TransInfo_set_selected_match_info (trans_info, best_match, false);
1148  if (best_match &&
1149  best_match->probability >= gnc_import_Settings_get_clear_threshold(settings))
1150  {
1152  best_match->update_proposed)
1153  trans_info->action = GNCImport_UPDATE;
1154  else
1155  trans_info->action = GNCImport_CLEAR;
1156  }
1157  else if (!best_match ||
1158  best_match->probability <= gnc_import_Settings_get_add_threshold(settings))
1159  trans_info->action = GNCImport_ADD;
1161  trans_info->action = GNCImport_SKIP;
1163  trans_info->action = GNCImport_UPDATE;
1164  else
1165  trans_info->action = GNCImport_ADD;
1166  }
1167  else
1168  trans_info->action = GNCImport_ADD;
1169 
1170 
1171  trans_info->previous_action = trans_info->action;
1172 }
1173 
1174 
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:92
gsize qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
qof_strftime calls qof_format_time to print a given time and afterwards tries to put the result into ...
Definition: gnc-date.cpp:1062
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:381
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
gnc_numeric gnc_import_TransInfo_get_dest_value(const GNCImportTransInfo *info)
Returns the destination split value for this TransInfo.
gint gnc_import_Settings_get_clear_threshold(GNCImportSettings *settings)
Return the selected threshold.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction&#39;s split list.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void gnc_import_TransInfo_set_selected_match_info(GNCImportTransInfo *info, GNCImportMatchInfo *match, gboolean selected_manually)
Sets the currently selected match in this TransInfo.
void split_find_match(GNCImportTransInfo *trans_info, Split *split, gint display_threshold, gint date_threshold, gint date_not_threshold, double fuzzy_amount_difference)
The transaction matching heuristics are here.
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing. ...
utility functions for the GnuCash UI
GdkPixbuf * gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget *widget)
This function generates a new pixmap representing a match score.
void gnc_import_TransInfo_delete(GNCImportTransInfo *info)
Destructor.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
STRUCTS.
void gnc_import_TransInfo_set_ref_id(GNCImportTransInfo *info, guint32 ref_id)
Set the reference id for this TransInfo.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
#define GNC_PREFS_GROUP_IMPORT
The preferences used by the importer.
Generic importer backend interface.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
GNCImportTransInfo * gnc_import_TransInfo_new(Transaction *trans, Account *base_acc)
Create a new object of GNCImportTransInfo here.
gint safe_strcasecmp(const gchar *da, const gchar *db)
case sensitive comparison of strings da and db - either may be NULL.
Definition: qofutil.cpp:100
Split * gnc_import_TransInfo_get_fsplit(const GNCImportTransInfo *info)
Returns the first split of the transaction of this TransInfo.
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
Transaction * gnc_import_TransInfo_get_trans(const GNCImportTransInfo *info)
Returns the transaction of this TransInfo.
void xaccTransRecordPrice(Transaction *trans, PriceSource source)
The xaccTransRecordPrice() method iterates through the splits and and record the non-currency equival...
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void gnc_import_TransInfo_set_destacc(GNCImportTransInfo *info, Account *acc, gboolean selected_manually)
Set the &#39;other account&#39; of this transaction (used for auto-balance if needed).
guint32 gnc_import_TransInfo_get_ref_id(const GNCImportTransInfo *info)
Returns the reference id for this TransInfo.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void gnc_account_imap_add_account_bayes(Account *acc, GList *tokens, Account *added_acc)
Updates the imap for a given account using a list of tokens.
Definition: Account.cpp:5521
void gnc_import_TransInfo_init_matches(GNCImportTransInfo *trans_info, GNCImportSettings *settings)
Iterates through all splits of trans_info&#39;s originating account match list.
GNCImportAction gnc_import_TransInfo_get_action(const GNCImportTransInfo *info)
Returns the currently selected action for this TransInfo.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void gnc_tm_free(struct tm *time)
free a struct tm* created with gnc_localtime() or gnc_gmtime()
Definition: gnc-date.cpp:95
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
Split * gnc_import_MatchInfo_get_split(const GNCImportMatchInfo *info)
Get the split (&#39;this-side split&#39;) of this MatchInfo.
gboolean gnc_import_process_trans_item(Account *base_acc, GNCImportTransInfo *trans_info)
/brief – Processes one match according to its selected action.
gdouble gnc_numeric_to_double(gnc_numeric n)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
gint gnc_import_MatchInfo_get_probability(const GNCImportMatchInfo *info)
Get the probability (confidence level) of this MatchInfo.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account&#39;s commodity that the split should have...
Definition: gmock-Split.cpp:77
Account handling public routines.
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
Account public routines (C++ api)
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
void gnc_import_TransInfo_set_action(GNCImportTransInfo *info, GNCImportAction action)
Set the action for this TransInfo.
gboolean gnc_import_exists_online_id(Transaction *trans, GHashTable *acct_id_hash)
Checks whether the given transaction&#39;s online_id already exists in its parent account.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gnc_numeric gnc_import_TransInfo_get_dest_amount(const GNCImportTransInfo *info)
Returns the destination split amount for this TransInfo.
void gnc_import_TransInfo_set_append_text(GNCImportTransInfo *info, gboolean append_text)
Set the append_text for this TransInfo.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
Division.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
All type declarations for the whole Gnucash engine.
#define CREC
The Split has been cleared.
Definition: Split.h:73
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
Generic api to store and retrieve preferences.
void gnc_import_TransInfo_set_price(GNCImportTransInfo *info, gnc_numeric lprice)
Set the exchange rate for this TransInfo.
void xaccSplitSetDateReconciledSecs(Split *split, time64 secs)
Set the date on which this split was reconciled by specifying the time as time64. ...
gboolean gnc_import_Settings_get_action_update_enabled(GNCImportSettings *settings)
Return the selected action is enable state.
gboolean gnc_import_TransInfo_get_match_selected_manually(const GNCImportTransInfo *info)
Returns if the currently selected match was selected by the user.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3371
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1052
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits...
Utility functions for writing import modules.
Account * gnc_import_TransInfo_get_destacc(const GNCImportTransInfo *info)
Returns the &#39;other account&#39; of this transaction.
struct tm * gnc_gmtime(const time64 *secs)
fill out a time struct from a 64-bit time value
Definition: gnc-date.cpp:176
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:260
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
Check for error signal in value.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
gnc_numeric gnc_import_TransInfo_get_price(const GNCImportTransInfo *info)
Returns the exchange rate for this TransInfo.
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
Returns the stored list of possible matches.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
Returns the currently selected match in this TransInfo.
void gnc_import_TransInfo_remove_top_match(GNCImportTransInfo *info)
Remove the first match in the list of possible matches.
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_import_Settings_get_action_skip_enabled(GNCImportSettings *settings)
Return the selected action is enable state.
gboolean gnc_import_TransInfo_is_balanced(const GNCImportTransInfo *info)
Returns if the transaction stored in the TransInfo is currently balanced.
gint gnc_import_Settings_get_add_threshold(GNCImportSettings *settings)
Return the selected threshold.
void gnc_import_TransInfo_set_last_split_info(GNCImportTransInfo *info, GNCImportLastSplitInfo *lsplit)
Sets additional parameters to be used to generate the closing split.
#define NREC
not reconciled or cleared
Definition: Split.h:76
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69
Account * gnc_account_imap_find_account_bayes(Account *acc, GList *tokens)
Look up an Account in the map.
Definition: Account.cpp:5475
gboolean gnc_import_TransInfo_get_destacc_selected_manually(const GNCImportTransInfo *info)
Returns if the currently selected destination account for auto-matching was selected by the user...