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  auto download_time_str = qof_print_date(download_time);
638  auto match_time_str = qof_print_date(match_time);
639  DEBUG("Date download: %s vs match: %s", download_time_str, match_time_str);
640  g_free (download_time_str);
641  g_free (match_time_str);
642  DEBUG("diff day %lld", datediff_day);
643  if (datediff_day == 0)
644  {
645  prob = prob + 3;
646  DEBUG("heuristics: probability + 3 (date)");
647  }
648  else if (datediff_day <= date_threshold)
649  {
650  prob = prob + 2;
651  DEBUG("heuristics: probability + 2 (date)");
652  }
653  else if (datediff_day > date_not_threshold)
654  {
655  /* Extra penalty if that split lies awfully far away from
656  the given one. */
657  prob = prob - 5;
658  DEBUG("heuristics: probability - 5 (date)");
659  /* Changed 2005-02-21: Revert the hard-limiting behaviour
660  back to the previous large penalty. (Changed 2004-11-27:
661  The penalty is so high that we can forget about this
662  split anyway and skip the rest of the tests.) */
663  }
664 
665  /* Check if date and amount are identical */
666  auto update_proposed = (prob < 6);
667 
668  /* Check number heuristics */
669  auto new_trans_str = gnc_get_num_action(new_trans, new_trans_fsplit);
670  auto split_str = gnc_get_num_action (xaccSplitGetParent (split), split);
671  DEBUG("number download: '%s' to match: '%s'", new_trans_str, split_str);
672  if (new_trans_str && *new_trans_str && split_str && *split_str)
673  {
674  char *endptr;
675  auto conversion_ok = true;
676 
677  /* To distinguish success/failure after strtol call */
678  errno = 0;
679  auto new_trans_number = strtol(new_trans_str, &endptr, 10);
680  /* Possible addressed problems: over/underflow, only non
681  numbers on string and string empty */
682  conversion_ok = !(errno || endptr == new_trans_str);
683 
684  errno = 0;
685  auto split_number = strtol(split_str, &endptr, 10);
686  conversion_ok = !(errno || endptr == split_str);
687 
688  if ( (conversion_ok && (split_number == new_trans_number)) ||
689  (g_strcmp0(new_trans_str, split_str) == 0) )
690  {
691  /* An exact match of the Check number gives a +4 */
692  prob += 4;
693  DEBUG("heuristics: probability + 4 (Check number)");
694  }
695  else if (strlen(new_trans_str) > 0 && strlen(split_str) > 0)
696  {
697  /* If both number are not empty yet do not match, add a
698  little extra penalty */
699  prob -= 2;
700  }
701  }
702 
703  /* Memo heuristics */
704  auto memo = xaccSplitGetMemo(new_trans_fsplit);
705  auto match_memo = xaccSplitGetMemo(split);
706  if (memo && *memo && match_memo && *match_memo)
707  {
708  DEBUG("memo download: '%s' to match: '%s'", memo, match_memo);
709  if (safe_strcasecmp(memo, match_memo) == 0)
710  {
711  /* An exact match of memo gives a +2 */
712  prob = prob + 2;
713  DEBUG("heuristics: probability + 2 (memo)");
714  }
715  else
716  {
717  const size_t match_memo_len = strlen(match_memo);
718  const size_t memo_len = strlen(memo);
719  const size_t max_memo_len = match_memo_len > memo_len ? match_memo_len : memo_len;
720  if (match_memo_len > 1 && memo_len > 1
721  && (strncasecmp(memo, match_memo, max_memo_len / 2) == 0))
722  {
723  /* Very primitive fuzzy match worth +1. This matches up to the
724  first 50% of the longest string to skip annoying transaction
725  number some banks seem to include in the memo but someone
726  should write something more sophisticated */
727  prob = prob + 1;
728  DEBUG("heuristics: probability + 1 (memo)");
729  }
730  }
731  }
732 
733  /* Description heuristics */
734  auto descr = xaccTransGetDescription(new_trans);
735  auto match_descr = xaccTransGetDescription(xaccSplitGetParent(split));
736  if (descr && *descr && match_descr && *match_descr)
737  {
738  DEBUG("description: download: '%s' to match: '%s'", descr, match_descr);
739  if (safe_strcasecmp(descr, match_descr) == 0)
740  {
741  /*An exact match of Description gives a +2 */
742  prob = prob + 2;
743  DEBUG("heuristics: probability + 2 (description)");
744  }
745  else
746  {
747  const size_t match_descr_len = strlen(match_descr);
748  const size_t descr_len = strlen(descr);
749  const size_t max_descr_len = match_descr_len > descr_len ? match_descr_len : descr_len;
750  if (match_descr_len > 1 && descr_len > 1
751  && (strncasecmp(descr, match_descr, max_descr_len / 2) == 0))
752  {
753  /* Very primitive fuzzy match worth +1. This matches up to the
754  first 50% of the longest string to skip annoying transaction
755  number some banks seem to include in the description but someone
756  should write something more sophisticated */
757  prob = prob + 1;
758  DEBUG("heuristics: probability + 1 (description)");
759  }
760  }
761  }
762 
763  /* Is the probability high enough? Otherwise do nothing and return. */
764  if (prob < display_threshold) {
765  DEBUG("below threshold: %d", prob);
766  return;
767  }
768 
769  /* The probability is high enough, so allocate an object
770  here. Allocating it only when it's actually being used is
771  probably quite some performance gain. */
772  auto match_info = g_new0(GNCImportMatchInfo, 1);
773 
774  match_info->probability = prob;
775  match_info->update_proposed = update_proposed;
776  match_info->split = split;
777  match_info->trans = xaccSplitGetParent(split);
778 
779 
780  /* Append that to the list. Do not use g_list_append because
781  it is slow. The list is sorted afterwards anyway. */
782  trans_info->match_list = g_list_prepend(trans_info->match_list, match_info);
783  DEBUG("Added to list of possible matches: %d", prob);
784 }
785 
786 /***********************************************************************
787  */
788 
789 static char*
790 maybe_append_string (const char* match_string, const char* imp_string)
791 {
792  if (!(match_string && *match_string))
793  return g_strdup(imp_string);
794 
795  if (!(imp_string && *imp_string))
796  return nullptr;
797 
798  auto norm_match_string = g_utf8_normalize (match_string, -1, G_NORMALIZE_NFC);
799  auto norm_imp_string = g_utf8_normalize (imp_string, -1, G_NORMALIZE_NFC);
800 
801  char *retval = nullptr;
802  if (g_utf8_strlen (norm_imp_string, -1) > g_utf8_strlen (norm_match_string, -1) ||
803  !strstr (norm_match_string, norm_imp_string))
804  retval = g_strconcat(match_string, "|", imp_string, nullptr);
805 
806  g_free (norm_match_string);
807  g_free (norm_imp_string);
808  return retval;
809 
810 }
811 
812 /* Append or replace transaction description and notes
813  * depending on the Append checkbox
814  */
815 static void
816 update_desc_and_notes (const GNCImportTransInfo* trans_info)
817 {
818  auto selected_match = gnc_import_TransInfo_get_selected_match (trans_info);
819  auto imp_trans = gnc_import_TransInfo_get_trans (trans_info);
820 
821  if (trans_info->append_text)
822  {
823  auto match_trans = selected_match->trans;
824  auto repl_str =
825  maybe_append_string (xaccTransGetDescription(match_trans),
826  xaccTransGetDescription(imp_trans));
827  if (repl_str)
828  xaccTransSetDescription(match_trans, repl_str);
829  g_free (repl_str);
830 
831  repl_str =
832  maybe_append_string (xaccTransGetNotes(match_trans),
833  xaccTransGetNotes(imp_trans));
834  if (repl_str)
835  xaccTransSetNotes (match_trans, repl_str);
836  g_free (repl_str);
837  }
838  else
839  {
840  xaccTransSetDescription (selected_match->trans,
841  xaccTransGetDescription (imp_trans));
842  xaccTransSetNotes (selected_match->trans,
843  xaccTransGetNotes (imp_trans));
844  }
845 }
846 
847 static void
848 process_reconcile(Account *base_acc,
849  GNCImportTransInfo *trans_info,
850  GNCImportMatchInfo *selected_match)
851 {
852  /* Reconcile the matching transaction */
853  /*DEBUG("BeginEdit selected_match")*/
854  xaccTransBeginEdit(selected_match->trans);
855 
856  if (xaccSplitGetReconcile(selected_match->split) == NREC)
857  xaccSplitSetReconcile(selected_match->split, CREC);
858 
859  /* Set reconcile date to today */
860  xaccSplitSetDateReconciledSecs(selected_match->split, gnc_time (nullptr));
861 
862  /* Copy the online id to the reconciled transaction, so
863  * the match will be remembered */
864  auto online_id = gnc_import_get_split_online_id(trans_info->first_split);
865  if (online_id && *online_id)
866  gnc_import_set_split_online_id(selected_match->split, online_id);
867 
868  g_free (online_id);
869 
870  /* Done editing. */
871  /*DEBUG("CommitEdit selected_match")*/
872  xaccTransCommitEdit(selected_match->trans);
873 
874  /* Store the mapping to the other account in the MatchMap. */
875  matchmap_store_destination(base_acc, trans_info, true);
876 
877  /* Erase the downloaded transaction */
878  xaccTransDestroy(trans_info->trans);
879  /*DEBUG("CommitEdit trans")*/
880  xaccTransCommitEdit(trans_info->trans);
881  /* Very important: Make sure the freed transaction is not freed again! */
882  trans_info->trans = nullptr;
883 }
884 
887 gboolean
889  GNCImportTransInfo *trans_info)
890 {
891  g_assert (trans_info);
892  /*DEBUG("Iteration %d, action %d, split %s", i,
893  trans_info->action,
894  xaccTransGetDescription (gnc_import_TransInfo_get_trans
895  (trans_info)))*/
896  switch (gnc_import_TransInfo_get_action (trans_info))
897  {
898  case GNCImport_SKIP:
899  return false;
900  case GNCImport_ADD:
901  /* Transaction gets imported. */
902  if (!gnc_import_TransInfo_is_balanced(trans_info)
903  && gnc_import_TransInfo_get_destacc(trans_info))
904  {
905  /* Create the 'other' split. */
906  auto trans = gnc_import_TransInfo_get_trans (trans_info);
907  auto acct = gnc_import_TransInfo_get_destacc (trans_info);
908  auto split = xaccMallocSplit (gnc_account_get_book (acct));
909  xaccTransAppendSplit (trans, split);
910  xaccAccountInsertSplit (acct, split);
911  xaccSplitSetValue (split, trans_info->lsplit_value);
912  if (!gnc_numeric_zero_p (trans_info->lsplit_amount))
913  xaccSplitSetAmount (split, trans_info->lsplit_amount);
914  else
915  {
916  /* Bad! user asked to create a balancing split in an account with
917  * different currency/commodit than the transaction but didn't provide
918  * an exchange rate.
919  * Continue anyway pretenting split is in transaction currency. */
920  xaccSplitSetAmount (split, trans_info->lsplit_value);
921  PWARN("Missing exchange rate while adding transaction '%s', will assume rate of 1",
923  }
924  }
925 
927  /*Set reconcile date to today*/
929  gnc_time (nullptr));
930  /* Done editing. */
931  xaccTransCommitEdit(trans_info->trans);
932  xaccTransRecordPrice(trans_info->trans, PRICE_SOURCE_SPLIT_IMPORT);
933  return true;
934  case GNCImport_UPDATE:
935  {
936  auto selected_match = gnc_import_TransInfo_get_selected_match(trans_info);
937 
938  /* If there is no selection, ignore this transaction. */
939  if (!selected_match)
940  {
941  PWARN("No matching transaction to be cleared was chosen. Imported transaction will be ignored.");
942  break;
943  }
944 
945  /* Transaction gets not imported but the matching one gets
946  updated and reconciled. */
947  if (!gnc_import_MatchInfo_get_split(selected_match))
948  PERR("The split I am trying to update and reconcile is nullptr, shouldn't happen!");
949  else
950  {
951  /* Update and reconcile the matching transaction */
952  /*DEBUG("BeginEdit selected_match")*/
953  xaccTransBeginEdit(selected_match->trans);
954 
955  auto fsplit = gnc_import_TransInfo_get_fsplit(trans_info);
956  xaccTransSetDatePostedSecsNormalized(selected_match->trans,
958 
959  auto match_split_amount = xaccSplitGetAmount(selected_match->split);
960  xaccSplitSetAmount(selected_match->split, xaccSplitGetAmount(fsplit));
961  xaccSplitSetValue(selected_match->split, xaccSplitGetValue(fsplit));
962 
963  auto imbalance_value = gnc_import_TransInfo_get_dest_value(trans_info);
964  auto other_split = xaccSplitGetOtherSplit(selected_match->split);
965  if (!gnc_numeric_zero_p(imbalance_value) && other_split)
966  {
967  if (xaccSplitGetReconcile(other_split) == NREC)
968  {
969  xaccSplitSetValue(other_split, imbalance_value);
970  auto new_amt = gnc_import_TransInfo_get_dest_value(trans_info);
971  if (gnc_numeric_zero_p(new_amt))
972  {
973  auto other_split_amount = xaccSplitGetAmount(other_split);
974  auto price = gnc_numeric_div(match_split_amount, other_split_amount,
977 
978  new_amt = gnc_numeric_mul(xaccSplitGetAmount(fsplit), price,
981  }
982  xaccSplitSetAmount(other_split, new_amt);
983  }
984  else
985  {
986  /* else GC will automatically insert a split to equity
987  to balance the transaction */
988  PWARN("Updated transaction '%s', but not other split.",
989  xaccTransGetDescription(selected_match->trans));
990  }
991  }
992 
993  auto fs_memo = xaccSplitGetMemo (trans_info->first_split);
994  if (fs_memo && *fs_memo)
995  xaccSplitSetMemo(selected_match->split, fs_memo);
996 
997  update_desc_and_notes(trans_info);
998 
999  /*DEBUG("CommitEdit selected_match")*/
1000  xaccTransCommitEdit(selected_match->trans);
1001 
1002  process_reconcile (base_acc, trans_info, selected_match);
1003  }
1004  }
1005  return true;
1006  case GNCImport_CLEAR:
1007  {
1008  auto selected_match = gnc_import_TransInfo_get_selected_match (trans_info);
1009 
1010  /* If there is no selection, ignore this transaction. */
1011  if (!selected_match)
1012  {
1013  PWARN("No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1014  break;
1015  }
1016 
1017  /* Transaction gets not imported but the matching one gets
1018  reconciled. */
1019  if (!gnc_import_MatchInfo_get_split (selected_match))
1020  PERR("The split I am trying to reconcile is nullptr, shouldn't happen!");
1021  else
1022  {
1023  /* Reconcile the matching transaction */
1024  process_reconcile(base_acc, trans_info, selected_match);
1025  }
1026  }
1027  return true;
1028  default:
1029  DEBUG("Invalid GNCImportAction for this imported transaction.");
1030  break;
1031  }
1032  /*DEBUG("End");*/
1033  return false;
1034 }
1035 
1036 static GHashTable*
1037 hash_account_online_ids (Account *account)
1038 {
1039  auto acct_hash = g_hash_table_new_full
1040  (g_str_hash, g_str_equal, g_free, nullptr);
1041  for (auto split : xaccAccountGetSplits (account))
1042  {
1043  auto id = gnc_import_get_split_online_id (split);
1044  if (id && *id)
1045  g_hash_table_insert (acct_hash, (void*) id, GINT_TO_POINTER (1));
1046  }
1047  return acct_hash;
1048 }
1049 
1052 gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash)
1053 {
1054 
1055  /* Look for an online_id in the first split */
1056  auto source_split = xaccTransGetSplit(trans, 0);
1057  g_assert(source_split);
1058 
1059  auto source_online_id = gnc_import_get_split_online_id (source_split);
1060 
1061  // No online id, no point in continuing. We'd crash if we tried.
1062  if (!source_online_id)
1063  return false;
1064 
1065  // Create a hash per account of a hash of all split IDs. Then the
1066  // test below will be fast if we have many transactions to import.
1067  auto dest_acct = xaccSplitGetAccount (source_split);
1068 
1069  auto online_id_hash = static_cast<GHashTable*>(g_hash_table_lookup (acct_id_hash, dest_acct));
1070 
1071  if (!online_id_hash)
1072  {
1073  online_id_hash = hash_account_online_ids (dest_acct);
1074  g_hash_table_insert (acct_id_hash, dest_acct, online_id_hash);
1075  }
1076 
1077  auto online_id_exists = g_hash_table_contains (online_id_hash, source_online_id);
1078  if (online_id_exists)
1079  {
1080  auto date_str = qof_print_date(xaccTransGetDate(trans));
1081  DEBUG("Transaction with online ID %s already exists, date: %s", source_online_id, date_str);
1082  g_free (date_str);
1083  }
1084  g_free (source_online_id);
1085  return online_id_exists;
1086 }
1087 
1088 
1089 /* ******************************************************************
1090  */
1091 
1092 /* Calculate lsplit_amount based on knowledge gathered so far
1093  * If insufficient info is available (eg multi currency transaction with missing
1094  * exchange rate provided), set amount to 0 */
1095 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info)
1096 {
1097  info->lsplit_value = gnc_numeric_neg (xaccTransGetImbalanceValue (info->trans));
1098  if (!info->lsplit_amount_selected_manually)
1099  info->lsplit_amount = {0, 1};
1100 
1101  if (info->dest_acc)
1102  {
1103  auto tcurr = xaccTransGetCurrency(info->trans);
1104  auto dcurr = xaccAccountGetCommodity(info->dest_acc);
1105 
1106  if (gnc_numeric_zero_p(info->lsplit_value))
1107  return;
1108 
1109  if (gnc_commodity_equiv(tcurr, dcurr))
1110  info->lsplit_amount = info->lsplit_value;
1111  else if (info->lsplit_amount_selected_manually &&
1112  gnc_numeric_check(info->lsplit_amount) == 0)
1113  {
1114  /* Nothing to do, user has provided amount already */
1115  }
1116  else if (gnc_numeric_check(info->lsplit_price) == 0)
1117  {
1118  /* We are in a multi currency situation and have a valid price
1119  * Reminder: value = amount * price => amount = value / price */
1120  gnc_numeric inv_price = gnc_numeric_invert (info->lsplit_price);
1121  info->lsplit_amount = gnc_numeric_mul (info->lsplit_value,
1122  inv_price,
1125  }
1126  }
1127 }
1128 
1130 GNCImportTransInfo *
1131 gnc_import_TransInfo_new (Transaction *trans, Account *base_acc)
1132 {
1133  g_assert (trans);
1134 
1135  auto t_info = g_new0(GNCImportTransInfo, 1);
1136 
1137  t_info->trans = trans;
1138  /* Only use first split, the source split */
1139  auto split = xaccTransGetSplit(trans, 0);
1140  g_assert(split);
1141  t_info->first_split = split;
1142 
1143  /* Try to find a previously selected destination account
1144  string match for the ADD action */
1146  matchmap_find_destination (base_acc, t_info),
1147  false);
1148 
1149  return t_info;
1150 }
1151 
1152 
1154 static gint compare_probability (gconstpointer a,
1155  gconstpointer b)
1156 {
1157  return(((GNCImportMatchInfo *)b)->probability -
1158  ((GNCImportMatchInfo *)a)->probability);
1159 }
1160 
1165 void
1166 gnc_import_TransInfo_init_matches (GNCImportTransInfo *trans_info,
1167  GNCImportSettings *settings)
1168 {
1169  g_assert (trans_info);
1170 
1171  if (trans_info->match_list)
1172  {
1173  DEBUG("Number of matches %d", g_list_length(trans_info->match_list));
1174 
1175  trans_info->match_list = g_list_sort(trans_info->match_list,
1176  compare_probability);
1177  auto best_match = static_cast<GNCImportMatchInfo*>(g_list_nth_data(trans_info->match_list, 0));
1178  gnc_import_TransInfo_set_selected_match_info (trans_info, best_match, false);
1179  if (best_match &&
1180  best_match->probability >= gnc_import_Settings_get_clear_threshold(settings))
1181  {
1183  best_match->update_proposed)
1184  trans_info->action = GNCImport_UPDATE;
1185  else
1186  trans_info->action = GNCImport_CLEAR;
1187  }
1188  else if (!best_match ||
1189  best_match->probability <= gnc_import_Settings_get_add_threshold(settings))
1190  trans_info->action = GNCImport_ADD;
1192  trans_info->action = GNCImport_SKIP;
1194  trans_info->action = GNCImport_UPDATE;
1195  else
1196  trans_info->action = GNCImport_ADD;
1197  }
1198  else
1199  trans_info->action = GNCImport_ADD;
1200 
1201 
1202  trans_info->previous_action = trans_info->action;
1203 }
1204 
1205 
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:1129
#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:5530
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:97
#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.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:610
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:3375
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:178
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:262
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:5484
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...