GnuCash  4.11-11-ge9df8d41d2+
import-backend.c
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 "Query.h"
42 #include "gnc-engine.h"
43 #include "engine-helpers.h"
44 #include "gnc-prefs.h"
45 #include "gnc-ui-util.h"
46 
47 #define GNCIMPORT_DESC "desc"
48 #define GNCIMPORT_MEMO "memo"
49 #define GNCIMPORT_PAYEE "payee"
50 
51 /********************************************************************\
52  * Constants *
53 \********************************************************************/
54 
55 static QofLogModule log_module = GNC_MOD_IMPORT;
56 
57 /********************************************************************\
58  * Forward declared prototypes *
59 \********************************************************************/
60 
61 static void
62 matchmap_store_destination (GncImportMatchMap *matchmap,
63  GNCImportTransInfo *trans_info,
64  gboolean use_match);
65 
66 
67 /********************************************************************\
68  * Structures passed between the functions *
69 \********************************************************************/
70 
72 {
73  GNCImportMatchInfo *selected_match;
74  gboolean selected_manually;
75 };
76 
78 {
79  Transaction * trans;
80  Split * first_split;
81 
82  /* GList of GNCImportMatchInfo's, one for each possible duplicate match. */
83  GList * match_list;
84  GNCImportSelectedMatchInfo selected_match_info;
85 
86  GNCImportAction action;
87  GNCImportAction previous_action;
88 
89  /* A list of tokenized strings to use for bayesian matching purposes */
90  GList * match_tokens;
91 
92  /* In case of a single destination account it is stored here. */
93  Account *dest_acc;
94  gboolean dest_acc_selected_manually;
95 
96  /* Reference id to link gnc transaction to external object. E.g. aqbanking job id. */
97  guint32 ref_id;
98 
99  /* When updating a matched transaction, append Description and Notes instead of replacing */
100  gboolean append_text;
101 };
102 
103 /* Some simple getters and setters for the above data types. */
104 
105 GList *
106 gnc_import_TransInfo_get_match_list (const GNCImportTransInfo *info)
107 {
108  g_assert (info);
109  return info->match_list;
110 }
111 
112 void
113 gnc_import_TransInfo_set_match_list (GNCImportTransInfo *info, GList* match_list)
114 {
115  g_assert (info);
116  info->match_list = match_list;
117  if (match_list)
118  {
119  info->selected_match_info.selected_match = match_list->data;
120  }
121  else
122  {
123  info->selected_match_info.selected_match = NULL;
124  gnc_import_TransInfo_set_action (info, GNCImport_ADD);
125  }
126 }
127 
128 Transaction *
129 gnc_import_TransInfo_get_trans (const GNCImportTransInfo *info)
130 {
131  g_assert (info);
132  return info->trans;
133 }
134 
135 gboolean
136 gnc_import_TransInfo_is_balanced (const GNCImportTransInfo *info)
137 {
138  g_assert (info);
139  /* Assume that the importer won't create a transaction that involves two or more
140  currencies and no non-currency commodity. In that case can use the simpler
141  value imbalance check. */
143  {
144  return TRUE;
145  }
146  else
147  {
148  return FALSE;
149  }
150 }
151 
152 Split *
153 gnc_import_TransInfo_get_fsplit (const GNCImportTransInfo *info)
154 {
155  g_assert (info);
156  return info->first_split;
157 }
158 
160 gnc_import_TransInfo_get_selected_match (const GNCImportTransInfo *info)
161 {
162  g_assert (info);
163  return info->selected_match_info.selected_match;
164 }
165 
166 void
168  GNCImportMatchInfo *match,
169  gboolean selected_manually)
170 {
171  g_assert (info);
172  info->selected_match_info.selected_match = match;
173  info->selected_match_info.selected_manually = selected_manually;
174 }
175 
176 gboolean
177 gnc_import_TransInfo_get_match_selected_manually (const GNCImportTransInfo *info)
178 {
179  g_assert (info);
180  return info->selected_match_info.selected_manually;
181 }
182 
183 GNCImportAction
184 gnc_import_TransInfo_get_action (const GNCImportTransInfo *info)
185 {
186  g_assert (info);
187  return info->action;
188 }
189 
190 void
191 gnc_import_TransInfo_set_action (GNCImportTransInfo *info,
192  GNCImportAction action)
193 {
194  g_assert (info);
195  if (action != info->action)
196  {
197  info->previous_action = info->action;
198  info->action = action;
199  }
200 }
201 
202 Account *
203 gnc_import_TransInfo_get_destacc (const GNCImportTransInfo *info)
204 {
205  g_assert (info);
206  return info->dest_acc;
207 }
208 void gnc_import_TransInfo_set_destacc (GNCImportTransInfo *info,
209  Account *acc,
210  gboolean selected_manually)
211 {
212  g_assert (info);
213  info->dest_acc = acc;
214  info->dest_acc_selected_manually = selected_manually;
215 
216  /* Store the mapping to the other account in the MatchMap. */
217  if (selected_manually)
218  {
219  matchmap_store_destination (NULL, info, FALSE);
220  }
221 }
222 
223 gboolean
225 {
226  g_assert (info);
227  return info->dest_acc_selected_manually;
228 }
229 
230 guint32
231 gnc_import_TransInfo_get_ref_id (const GNCImportTransInfo *info)
232 {
233  g_assert (info);
234  return info->ref_id;
235 }
236 
237 void
238 gnc_import_TransInfo_set_ref_id (GNCImportTransInfo *info,
239  guint32 ref_id)
240 {
241  g_assert (info);
242  info->ref_id = ref_id;
243 }
244 
245 
246 void
247 gnc_import_TransInfo_set_append_text (GNCImportTransInfo *info,
248  gboolean append_text)
249 {
250  g_assert (info);
251  info->append_text = append_text;
252 }
253 
254 
255 Split *
257 {
258  g_assert (info);
259  return info->split;
260 }
261 
262 gint
264 {
265  if (info)
266  {
267  return info->probability;
268  }
269  else
270  {
271  return 0;
272  }
273 }
274 
275 void gnc_import_TransInfo_delete (GNCImportTransInfo *info)
276 {
277  if (info)
278  {
279  g_list_free (info->match_list);
280  /*If the transaction exists and is still open, it must be destroyed*/
281  if (info->trans && xaccTransIsOpen(info->trans))
282  {
283  xaccTransDestroy(info->trans);
284  xaccTransCommitEdit(info->trans);
285  }
286  if (info->match_tokens)
287  {
288  GList *node;
289 
290  for (node = info->match_tokens; node; node = node->next)
291  g_free (node->data);
292 
293  g_list_free (info->match_tokens);
294  }
295  g_free(info);
296  }
297 }
298 
299 GdkPixbuf* gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget * widget)
300 {
301  GdkPixbuf* retval = NULL;
302  gint i, j;
303  gint score;
304  const gint height = 15;
305  const gint width_each_bar = 7;
306  gchar * green_bar = ("bggggb ");
307  gchar * yellow_bar = ("byyyyb ");
308  gchar * red_bar = ("brrrrb ");
309  gchar * black_bar = ("bbbbbb ");
310  const gint width_first_bar = 1;
311  gchar * black_first_bar = ("b");
312  const gint num_colors = 5;
313  gchar * size_str;
314  gchar * none_color_str = g_strdup_printf(" c None");
315  gchar * green_color_str = g_strdup_printf("g c green");
316  gchar * yellow_color_str = g_strdup_printf("y c yellow");
317  gchar * red_color_str = g_strdup_printf("r c red");
318  gchar * black_color_str = g_strdup_printf("b c black");
319  gchar * xpm[2+num_colors+height];
320  gint add_threshold, clear_threshold;
321 
322  g_assert(settings);
323  g_assert(widget);
324  if (score_original < 0)
325  {
326  score = 0;
327  }
328  else
329  {
330  score = score_original;
331  }
332  size_str = g_strdup_printf("%d%s%d%s%d%s", (width_each_bar * score) + width_first_bar/*width*/, " ", height, " ", num_colors, " 1"/*characters per pixel*/);
333 
334  /*DEBUG("Begin");*/
335  xpm[0] = size_str;
336  xpm[1] = none_color_str;
337  xpm[2] = green_color_str;
338  xpm[3] = yellow_color_str;
339  xpm[4] = red_color_str;
340  xpm[5] = black_color_str;
341  add_threshold = gnc_import_Settings_get_add_threshold(settings);
342  clear_threshold = gnc_import_Settings_get_clear_threshold(settings);
343 
344  for (i = 0; i < height; i++)
345  {
346  xpm[num_colors+1+i] = g_new0(char, (width_each_bar * score) + width_first_bar + 1);
347  for (j = 0; j <= score; j++)
348  {
349  if (i == 0 || i == height - 1)
350  {
351  if (j == 0)
352  {
353  strcat(xpm[num_colors+1+i], black_first_bar);
354  }
355  else
356  {
357  strcat(xpm[num_colors+1+i], black_bar);
358  }
359  }
360  else
361  {
362  if (j == 0)
363  {
364  strcat(xpm[num_colors+1+i], black_first_bar);
365  }
366  else if (j <= add_threshold)
367  {
368  strcat(xpm[num_colors+1+i], red_bar);
369  }
370  else if (j >= clear_threshold)
371  {
372  strcat(xpm[num_colors+1+i], green_bar);
373  }
374  else
375  {
376  strcat(xpm[num_colors+1+i], yellow_bar);
377  }
378  }
379  }
380  }
381 
382  retval = gdk_pixbuf_new_from_xpm_data((const gchar **)xpm);
383  for (i = 0; i <= num_colors + height; i++)
384  {
385  /*DEBUG("free_loop i=%d%s%s",i,": ",xpm[i]);*/
386  g_free(xpm[i]);
387  }
388 
389  return retval;
390 }
391 
392 /*************************************************************************
393  * MatchMap- related functions (storing and retrieving)
394  */
395 
396 /* Tokenize a string and append to an existing GList(or an empty GList)
397  * the tokens
398  */
399 static GList*
400 tokenize_string(GList* existing_tokens, const char *string)
401 {
402  char **tokenized_strings; /* array of strings returned by g_strsplit() */
403  char **stringpos;
404 
405  tokenized_strings = g_strsplit(string, " ", 0);
406  stringpos = tokenized_strings;
407 
408  /* add each token to the token GList */
409  while (stringpos && *stringpos)
410  {
411  if (strlen(*stringpos) > 0)
412  {
413  /* check for duplicated tokens */
414  gboolean duplicated = FALSE;
415  for (GList* token = existing_tokens; token != NULL; token = token->next)
416  {
417  if (g_strcmp0(token->data, *stringpos) == 0)
418  {
419  duplicated = TRUE;
420  break;
421  }
422  }
423  if (duplicated == FALSE)
424  {
425  /* prepend the char* to the token GList */
426  existing_tokens = g_list_prepend(existing_tokens, g_strdup(*stringpos));
427  }
428  }
429 
430  /* then move to the next string */
431  stringpos++;
432  }
433 
434  /* free up the strings that g_strsplit() created */
435  g_strfreev(tokenized_strings);
436 
437  return existing_tokens;
438 }
439 
440 /* create and return a list of tokens for a given transaction info. */
441 static GList*
442 TransactionGetTokens(GNCImportTransInfo *info)
443 {
444  Transaction* transaction;
445  GList* tokens;
446  const char* text;
447  time64 transtime;
448  struct tm *tm_struct;
449  char local_day_of_week[16];
450 
451  g_return_val_if_fail (info, NULL);
452  if (info->match_tokens) return info->match_tokens;
453 
454  transaction = gnc_import_TransInfo_get_trans(info);
455  g_assert(transaction);
456 
457  tokens = 0; /* start off with an empty list */
458 
459  /* make tokens from the transaction description */
460  text = xaccTransGetDescription(transaction);
461  tokens = tokenize_string(tokens, text);
462 
463  /* The day of week the transaction occurred is a good indicator of
464  * what account this transaction belongs in. Get the date and convert
465  * it to day of week as a token
466  */
467  transtime = xaccTransGetDate(transaction);
468  tm_struct = gnc_gmtime(&transtime);
469  if (!qof_strftime(local_day_of_week, sizeof(local_day_of_week), "%A", tm_struct))
470  {
471  PERR("TransactionGetTokens: error, strftime failed\n");
472  }
473  gnc_tm_free (tm_struct);
474  /* we cannot add a locally allocated string to this array, dup it so
475  * it frees the same way the rest do
476  */
477  tokens = g_list_prepend(tokens, g_strdup(local_day_of_week));
478 
479  /* make tokens from the memo of each split of this transaction */
480  for (GList *split=xaccTransGetSplitList (transaction); split; split=split->next)
481  {
482  text = xaccSplitGetMemo(split->data);
483  tokens = tokenize_string(tokens, text);
484  }
485 
486  /* remember the list of tokens for later.. */
487  info->match_tokens = tokens;
488 
489  /* return the pointer to the GList */
490  return tokens;
491 }
492 /* Destroy an import map. But all stored entries will still continue
493  * to exist in the underlying kvp frame of the account.
494  */
495 static void
496 gnc_imap_destroy (GncImportMatchMap *imap)
497 {
498  if (!imap) return;
499  g_free (imap);
500 }
501 
502 /* searches using the GNCImportTransInfo through all existing transactions
503  * if there is an exact match of the description and memo
504  */
505 static Account *
506 matchmap_find_destination (GncImportMatchMap *matchmap, GNCImportTransInfo *info)
507 {
508  GncImportMatchMap *tmp_map;
509  Account *result;
510  GList* tokens;
511  gboolean useBayes;
512 
513  g_assert (info);
514  tmp_map = ((matchmap != NULL) ? matchmap :
518 
519  useBayes = gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_USE_BAYES);
520  if (useBayes)
521  {
522  /* get the tokens for this transaction* */
523  tokens = TransactionGetTokens(info);
524 
525  /* try to find the destination account for this transaction from its tokens */
526  result = gnc_account_imap_find_account_bayes(tmp_map, tokens);
527 
528  }
529  else
530  {
531  /* old system of transaction to account matching */
532  result = gnc_account_imap_find_account
533  (tmp_map, GNCIMPORT_DESC,
535  }
536 
537  /* Disable matching by memo, until bayesian filtering is implemented.
538  * It's currently unlikely to help, and has adverse effects,
539  * causing false positives, since very often the type of the
540  * transaction is stored there.
541 
542  if (result == NULL)
543  result = gnc_account_imap_find_account
544  (tmp_map, GNCIMPORT_MEMO,
545  xaccSplitGetMemo (gnc_import_TransInfo_get_fsplit (info)));
546  */
547 
548  if (matchmap == NULL)
549  gnc_imap_destroy (tmp_map);
550 
551  return result;
552 }
553 
558 static void
559 matchmap_store_destination (GncImportMatchMap *matchmap,
560  GNCImportTransInfo *trans_info,
561  gboolean use_match)
562 {
563  GncImportMatchMap *tmp_matchmap = NULL;
564  Account *dest;
565  const char *descr, *memo;
566  GList *tokens;
567  gboolean useBayes;
568 
569  g_assert (trans_info);
570 
571  /* This will store the destination account of the selected match if
572  the reconcile match selected has only two splits. Good idea
573  Christian! */
574  dest = ((use_match) ?
578  (gnc_import_TransInfo_get_selected_match (trans_info)))) :
579  gnc_import_TransInfo_get_destacc (trans_info));
580  if (dest == NULL)
581  return;
582 
583  tmp_matchmap = ((matchmap != NULL) ?
584  matchmap :
587  (gnc_import_TransInfo_get_fsplit (trans_info))));
588 
589  /* see what matching system we are currently using */
590  useBayes = gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_USE_BAYES);
591  if (useBayes)
592  {
593  /* tokenize this transaction */
594  tokens = TransactionGetTokens(trans_info);
595 
596  /* add the tokens to the imap with the given destination account */
597  gnc_account_imap_add_account_bayes(tmp_matchmap, tokens, dest);
598 
599  }
600  else
601  {
602  /* old matching system */
604  (gnc_import_TransInfo_get_trans (trans_info));
605  if (descr && (strlen (descr) > 0))
606  gnc_account_imap_add_account (tmp_matchmap,
607  GNCIMPORT_DESC,
608  descr,
609  dest);
610  memo = xaccSplitGetMemo
611  (gnc_import_TransInfo_get_fsplit (trans_info));
612  if (memo && (strlen (memo) > 0))
613  gnc_account_imap_add_account (tmp_matchmap,
614  GNCIMPORT_MEMO,
615  memo,
616  dest);
617  } /* if(useBayes) */
618 
619  if (matchmap == NULL)
620  gnc_imap_destroy (tmp_matchmap);
621 }
622 
623 
624 
627 void split_find_match (GNCImportTransInfo * trans_info,
628  Split * split,
629  gint display_threshold,
630  gint date_threshold,
631  gint date_not_threshold,
632  double fuzzy_amount_difference)
633 {
634  /* DEBUG("Begin"); */
635 
636  /*Ignore the split if the transaction is open for edit, meaning it
637  was just downloaded. */
638  if (xaccTransIsOpen(xaccSplitGetParent(split)) == FALSE)
639  {
640  GNCImportMatchInfo * match_info;
641  gint prob = 0;
642  gboolean update_proposed;
643  double downloaded_split_amount, match_split_amount;
644  time64 match_time, download_time;
645  int datediff_day;
646  Transaction *new_trans = gnc_import_TransInfo_get_trans (trans_info);
647  Split *new_trans_fsplit = gnc_import_TransInfo_get_fsplit (trans_info);
648 
649  /* Matching heuristics */
650 
651  /* Amount heuristics */
652  downloaded_split_amount =
653  gnc_numeric_to_double (xaccSplitGetAmount(new_trans_fsplit));
654  /*DEBUG(" downloaded_split_amount=%f", downloaded_split_amount);*/
655  match_split_amount = gnc_numeric_to_double(xaccSplitGetAmount(split));
656  /*DEBUG(" match_split_amount=%f", match_split_amount);*/
657  if (fabs(downloaded_split_amount - match_split_amount) < 1e-6)
658  /* bug#347791: Double type shouldn't be compared for exact
659  equality, so we're using fabs() instead. */
660  /*if (gnc_numeric_equal(xaccSplitGetAmount
661  (new_trans_fsplit),
662  xaccSplitGetAmount(split)))
663  -- gnc_numeric_equal is an expensive function call */
664  {
665  prob = prob + 3;
666  /*DEBUG("heuristics: probability + 3 (amount)");*/
667  }
668  else if (fabs (downloaded_split_amount - match_split_amount) <=
669  fuzzy_amount_difference)
670  {
671  /* ATM fees are sometimes added directly in the transaction.
672  So you withdraw 100$ and get charged 101,25$ in the same
673  transaction */
674  prob = prob + 2;
675  /*DEBUG("heuristics: probability + 2 (amount)");*/
676  }
677  else
678  {
679  /* If a transaction's amount doesn't match within the
680  threshold, it's very unlikely to be the same transaction
681  so we give it an extra -5 penalty */
682  prob = prob - 5;
683  /* DEBUG("heuristics: probability - 1 (amount)"); */
684  }
685 
686  /* Date heuristics */
687  match_time = xaccTransGetDate (xaccSplitGetParent (split));
688  download_time = xaccTransGetDate (new_trans);
689  datediff_day = llabs(match_time - download_time) / 86400;
690  /* Sorry, there are not really functions around at all that
691  provide for less hacky calculation of days of date
692  differences. Whatever. On the other hand, the difference
693  calculation itself will work regardless of month/year
694  turnarounds. */
695  /*DEBUG("diff day %d", datediff_day);*/
696  if (datediff_day == 0)
697  {
698  prob = prob + 3;
699  /*DEBUG("heuristics: probability + 3 (date)");*/
700  }
701  else if (datediff_day <= date_threshold)
702  {
703  prob = prob + 2;
704  /*DEBUG("heuristics: probability + 2 (date)");*/
705  }
706  else if (datediff_day > date_not_threshold)
707  {
708  /* Extra penalty if that split lies awfully far away from
709  the given one. */
710  prob = prob - 5;
711  /*DEBUG("heuristics: probability - 5 (date)"); */
712  /* Changed 2005-02-21: Revert the hard-limiting behaviour
713  back to the previous large penalty. (Changed 2004-11-27:
714  The penalty is so high that we can forget about this
715  split anyway and skip the rest of the tests.) */
716  }
717 
718  /* Check if date and amount are identical */
719  update_proposed = (prob < 6);
720 
721  /* Check number heuristics */
722  {
723  const char *new_trans_str = gnc_get_num_action(new_trans, new_trans_fsplit);
724  if (new_trans_str && strlen(new_trans_str) != 0)
725  {
726  long new_trans_number, split_number;
727  const gchar *split_str;
728  char *endptr;
729  gboolean conversion_ok = TRUE;
730 
731  /* To distinguish success/failure after strtol call */
732  errno = 0;
733  new_trans_number = strtol(new_trans_str, &endptr, 10);
734  /* Possible addressed problems: over/underflow, only non
735  numbers on string and string empty */
736  if (errno || endptr == new_trans_str)
737  conversion_ok = FALSE;
738 
739  split_str = gnc_get_num_action (xaccSplitGetParent (split), split);
740  errno = 0;
741  split_number = strtol(split_str, &endptr, 10);
742  if (errno || endptr == split_str)
743  conversion_ok = FALSE;
744 
745  if ( (conversion_ok && (split_number == new_trans_number)) ||
746  (g_strcmp0(new_trans_str, split_str) == 0) )
747  {
748  /* An exact match of the Check number gives a +4 */
749  prob += 4;
750  /*DEBUG("heuristics: probability + 4 (Check number)");*/
751  }
752  else if (strlen(new_trans_str) > 0 && strlen(split_str) > 0)
753  {
754  /* If both number are not empty yet do not match, add a
755  little extra penalty */
756  prob -= 2;
757  }
758  }
759  }
760 
761  /* Memo heuristics */
762  {
763  const char *memo = xaccSplitGetMemo(new_trans_fsplit);
764  if (memo && strlen(memo) != 0)
765  {
766  if (safe_strcasecmp(memo, xaccSplitGetMemo(split)) == 0)
767  {
768  /* An exact match of memo gives a +2 */
769  prob = prob + 2;
770  /* DEBUG("heuristics: probability + 2 (memo)"); */
771  }
772  else if ((strncasecmp(memo, xaccSplitGetMemo(split),
773  strlen(xaccSplitGetMemo(split)) / 2)
774  == 0))
775  {
776  /* Very primitive fuzzy match worth +1. This matches the
777  first 50% of the strings to skip annoying transaction
778  number some banks seem to include in the memo but someone
779  should write something more sophisticated */
780  prob = prob + 1;
781  /*DEBUG("heuristics: probability + 1 (memo)"); */
782  }
783  }
784  }
785 
786  /* Description heuristics */
787  {
788  const char *descr = xaccTransGetDescription(new_trans);
789  if (descr && strlen(descr) != 0)
790  {
791  if (safe_strcasecmp(descr,
793  == 0)
794  {
795  /*An exact match of Description gives a +2 */
796  prob = prob + 2;
797  /*DEBUG("heuristics: probability + 2 (description)");*/
798  }
799  else if ((strncasecmp(descr,
801  strlen(xaccTransGetDescription (new_trans)) / 2)
802  == 0))
803  {
804  /* Very primitive fuzzy match worth +1. This matches the
805  first 50% of the strings to skip annoying transaction
806  number some banks seem to include in the memo but someone
807  should write something more sophisticated */
808  prob = prob + 1;
809  /*DEBUG("heuristics: probability + 1 (description)"); */
810  }
811  }
812  }
813 
814  /* Is the probability high enough? Otherwise do nothing and return. */
815  if (prob < display_threshold)
816  {
817  return;
818  }
819 
820  /* The probability is high enough, so allocate an object
821  here. Allocating it only when it's actually being used is
822  probably quite some performance gain. */
823  match_info = g_new0(GNCImportMatchInfo, 1);
824 
825  match_info->probability = prob;
826  match_info->update_proposed = update_proposed;
827  match_info->split = split;
828  match_info->trans = xaccSplitGetParent(split);
829 
830 
831  /* Append that to the list. Do not use g_list_append because
832  it is slow. The list is sorted afterwards anyway. */
833  trans_info->match_list =
834  g_list_prepend(trans_info->match_list,
835  match_info);
836  }
837 }/* end split_find_match */
838 
839 /***********************************************************************
840  */
841 
842 /* append the imported transaction description to the matched transaction description */
843 static void
844 desc_append (Transaction* selected_match_trans, gchar *new_desc)
845 {
846  const gchar* curr_desc = xaccTransGetDescription (selected_match_trans);
847  gchar* tmp = g_strconcat(curr_desc, "|", new_desc, NULL);
848  xaccTransSetDescription (selected_match_trans, tmp);
849  g_free (tmp);
850 }
851 
852 /* append the imported transaction notes to the matched transaction notes */
853 static void
854 notes_append (Transaction* selected_match_trans, gchar* new_notes)
855 {
856  const gchar* curr_notes = xaccTransGetNotes (selected_match_trans);
857  gchar* tmp = g_strconcat (curr_notes, "|", new_notes, NULL);
858  xaccTransSetNotes (selected_match_trans, tmp );
859  g_free (tmp);
860 }
861 
862 /* Append or replace transaction description and notes
863  * depending on the Append checkbox
864  */
865 static void
866 update_desc_and_notes (const GNCImportTransInfo* trans_info)
867 {
868  GNCImportMatchInfo* selected_match =
870  Transaction* imp_trans = gnc_import_TransInfo_get_trans (trans_info);
871 
872  if (trans_info->append_text)
873  {
874  gchar *desc_imported, *desc_matched, *note_imported, *note_matched;
875  const gchar* raw_str = xaccTransGetDescription (imp_trans);
876 
877  desc_imported =
878  raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
879  raw_str = xaccTransGetDescription (selected_match->trans);
880  desc_matched =
881  raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
882  raw_str = xaccTransGetNotes (imp_trans);
883  note_imported =
884  raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
885  raw_str = xaccTransGetNotes (selected_match->trans);
886  note_matched =
887  raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
888 
889  // Append if desc_imported not already in desc_matched
890  if (desc_imported &&
891  (!desc_matched ||
892  g_utf8_strlen (desc_imported, -1) > g_utf8_strlen (desc_matched, -1) ||
893  !strstr (desc_matched, desc_imported)))
894  {
895  if (desc_matched && *desc_matched)
896  desc_append (selected_match->trans, desc_imported);
897  else
898  xaccTransSetDescription (selected_match->trans, desc_imported);
899  }
900 
901  // Append if note_imported not already in note_matched
902  if (note_imported &&
903  (!note_matched ||
904  g_utf8_strlen (note_imported, -1) > g_utf8_strlen (note_matched, -1) ||
905  !strstr (note_matched, note_imported)))
906  {
907  if (note_matched && *note_matched)
908  notes_append (selected_match->trans, note_imported);
909  else
910  xaccTransSetNotes (selected_match->trans, note_imported);
911  }
912 
913  g_free(desc_imported);
914  g_free(desc_matched);
915  g_free(note_imported);
916  g_free(note_matched);
917  }
918  else
919  {
920  // replace the matched transaction description with the imported transaction description
921  xaccTransSetDescription (selected_match->trans,
922  xaccTransGetDescription (imp_trans));
923  // replace the matched transaction notes with the imported transaction notes
924  xaccTransSetNotes (selected_match->trans,
925  xaccTransGetNotes (imp_trans));
926  }
927 }
928 
931 gboolean
933  GNCImportTransInfo *trans_info)
934 {
935  Split * other_split;
936  gnc_numeric imbalance_value;
937  Transaction *trans;
938 
939  /* DEBUG("Begin"); */
940 
941  g_assert (trans_info);
942  /*DEBUG("Iteration %d, action %d, split %s", i,
943  trans_info->action,
944  xaccTransGetDescription (gnc_import_TransInfo_get_trans
945  (trans_info)))*/
946  switch (gnc_import_TransInfo_get_action (trans_info))
947  {
948  case GNCImport_SKIP:
949  return FALSE;
950  case GNCImport_ADD:
951  /* Transaction gets imported. */
952 
953  /* Is the transaction not balanced and there is a non-NULL destination account? */
954  if (gnc_import_TransInfo_is_balanced(trans_info) == FALSE
955  && gnc_import_TransInfo_get_destacc(trans_info) != NULL)
956  {
957  /* Create the 'other' split. */
958  Split *split =
960  (gnc_account_get_book
961  (gnc_import_TransInfo_get_destacc (trans_info)));
963  (gnc_import_TransInfo_get_trans (trans_info), split);
965  (gnc_import_TransInfo_get_destacc (trans_info), split);
966  /*xaccSplitSetBaseValue
967  (split,
968  gnc_numeric_neg(xaccTransGetImbalance
969  (gnc_import_TransInfo_get_trans (trans_info))),
970  xaccTransGetCurrency
971  (gnc_import_TransInfo_get_trans (trans_info)));*/
972  {
973  /* This is a quick workaround for the bug described in
974  http://lists.gnucash.org/pipermail/gnucash-devel/2003-August/009982.html
975  Assume that importers won't create transactions involving two or more
976  currencies so we can use xaccTransGetImbalanceValue. */
977  imbalance_value =
979  (gnc_import_TransInfo_get_trans (trans_info)));
980  xaccSplitSetValue (split, imbalance_value);
981  xaccSplitSetAmount (split, imbalance_value);
982  }
983  /*xaccSplitSetMemo (split, _("Auto-Balance split"));
984  -- disabled due to popular request */
985  }
986 
988  /*Set reconcile date to today*/
990  gnc_time (NULL));
991  /* Done editing. */
992  trans = gnc_import_TransInfo_get_trans (trans_info);
993  xaccTransCommitEdit(trans);
994  xaccTransRecordPrice(trans, PRICE_SOURCE_SPLIT_IMPORT);
995  return TRUE;
996  case GNCImport_UPDATE:
997  {
998  GNCImportMatchInfo *selected_match =
1000 
1001  /* If there is no selection, ignore this transaction. */
1002  if (!selected_match)
1003  {
1004  PWARN("No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1005  break;
1006  }
1007 
1008  /* Transaction gets not imported but the matching one gets
1009  updated and reconciled. */
1010  if (gnc_import_MatchInfo_get_split(selected_match) == NULL)
1011  {
1012  PERR("The split I am trying to update and reconcile is NULL, shouldn't happen!");
1013  }
1014  else
1015  {
1016  /* Update and reconcile the matching transaction */
1017  /*DEBUG("BeginEdit selected_match")*/
1018  xaccTransBeginEdit(selected_match->trans);
1019 
1020  xaccTransSetDatePostedSecsNormalized(selected_match->trans,
1022  gnc_import_TransInfo_get_fsplit(trans_info))));
1023 
1024  xaccSplitSetAmount(selected_match->split,
1026  gnc_import_TransInfo_get_fsplit(trans_info)));
1027  xaccSplitSetValue(selected_match->split,
1029  gnc_import_TransInfo_get_fsplit(trans_info)));
1030 
1031  imbalance_value = xaccTransGetImbalanceValue(
1032  gnc_import_TransInfo_get_trans(trans_info));
1033  other_split = xaccSplitGetOtherSplit(selected_match->split);
1034  if (!gnc_numeric_zero_p(imbalance_value) && other_split)
1035  {
1036  if (xaccSplitGetReconcile(other_split) == NREC)
1037  {
1038  imbalance_value = gnc_numeric_neg(imbalance_value);
1039  xaccSplitSetValue(other_split, imbalance_value);
1040  xaccSplitSetAmount(other_split, imbalance_value);
1041  }
1042  /* else GC will automatically insert a split to equity
1043  to balance the transaction */
1044  }
1045 
1046  update_desc_and_notes( trans_info);
1047 
1048  if (xaccSplitGetReconcile(selected_match->split) == NREC)
1049  {
1050  xaccSplitSetReconcile(selected_match->split, CREC);
1051  }
1052 
1053  /* Set reconcile date to today */
1054  xaccSplitSetDateReconciledSecs(selected_match->split, gnc_time (NULL));
1055 
1056  /* Copy the online id to the reconciled transaction, so
1057  the match will be remembered */
1058  if (gnc_import_split_has_online_id(trans_info->first_split))
1059  {
1060  gnc_import_set_split_online_id(selected_match->split,
1061  gnc_import_get_split_online_id(trans_info->first_split));
1062  }
1063 
1064  /* Done editing. */
1065  /*DEBUG("CommitEdit selected_match")*/
1066  xaccTransCommitEdit(selected_match->trans);
1067 
1068  /* Store the mapping to the other account in the MatchMap. */
1069  matchmap_store_destination(matchmap, trans_info, TRUE);
1070 
1071  /* Erase the downloaded transaction */
1072  xaccTransDestroy(trans_info->trans);
1073  /*DEBUG("CommitEdit trans")*/
1074  xaccTransCommitEdit(trans_info->trans);
1075  /* Very important: Make sure the freed transaction is not freed again! */
1076  trans_info->trans = NULL;
1077  }
1078  }
1079  return TRUE;
1080  case GNCImport_CLEAR:
1081  {
1082  GNCImportMatchInfo *selected_match =
1084 
1085  /* If there is no selection, ignore this transaction. */
1086  if (!selected_match)
1087  {
1088  PWARN("No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1089  break;
1090  }
1091 
1092  /* Transaction gets not imported but the matching one gets
1093  reconciled. */
1094  if (gnc_import_MatchInfo_get_split (selected_match) == NULL)
1095  {
1096  PERR("The split I am trying to reconcile is NULL, shouldn't happen!");
1097  }
1098  else
1099  {
1100  /* Reconcile the matching transaction */
1101  /*DEBUG("BeginEdit selected_match")*/
1102  xaccTransBeginEdit(selected_match->trans);
1103 
1105  (selected_match->split) == NREC)
1107  (selected_match->split, CREC);
1108  /* Set reconcile date to today */
1110  (selected_match->split, gnc_time (NULL));
1111 
1112  /* Copy the online id to the reconciled transaction, so
1113  the match will be remembered */
1114  if (gnc_import_split_has_online_id(trans_info->first_split))
1115  gnc_import_set_split_online_id
1116  (selected_match->split,
1117  gnc_import_get_split_online_id(trans_info->first_split));
1118 
1119  /* Done editing. */
1120  /*DEBUG("CommitEdit selected_match")*/
1122  (selected_match->trans);
1123 
1124  /* Store the mapping to the other account in the MatchMap. */
1125  matchmap_store_destination (matchmap, trans_info, TRUE);
1126 
1127  /* Erase the downloaded transaction */
1128  xaccTransDestroy(trans_info->trans);
1129  /*DEBUG("CommitEdit trans")*/
1130  xaccTransCommitEdit(trans_info->trans);
1131  /* Very important: Make sure the freed transaction is not freed again! */
1132  trans_info->trans = NULL;
1133  }
1134  }
1135  return TRUE;
1136  default:
1137  DEBUG("Invalid GNCImportAction for this imported transaction.");
1138  break;
1139  }
1140  /*DEBUG("End");*/
1141  return FALSE;
1142 }
1143 
1144 /********************************************************************\
1145  * check_trans_online_id() Callback function used by
1146  * gnc_import_exists_online_id. Takes pointers to transaction and split,
1147  * returns 0 if their online_ids do NOT match, or if the split
1148  * belongs to the transaction
1149 \********************************************************************/
1150 static gint check_trans_online_id(Transaction *trans1, void *user_data)
1151 {
1152  Account *account;
1153  Split *split1;
1154  Split *split2 = user_data;
1155  const gchar *online_id1;
1156  const gchar *online_id2;
1157 
1158  account = xaccSplitGetAccount(split2);
1159  split1 = xaccTransFindSplitByAccount(trans1, account);
1160  if (split1 == split2)
1161  return 0;
1162 
1163  /* hack - we really want to iterate over the _splits_ of the account
1164  instead of the transactions */
1165  g_assert(split1 != NULL);
1166 
1167  if (gnc_import_split_has_online_id(split1))
1168  online_id1 = gnc_import_get_split_online_id(split1);
1169  else
1170  online_id1 = gnc_import_get_trans_online_id(trans1);
1171 
1172  online_id2 = gnc_import_get_split_online_id(split2);
1173 
1174  if ((online_id1 == NULL) ||
1175  (online_id2 == NULL) ||
1176  (strcmp(online_id1, online_id2) != 0))
1177  {
1178  return 0;
1179  }
1180  else
1181  {
1182  /*printf("test_trans_online_id(): Duplicate found\n");*/
1183  return 1;
1184  }
1185 }
1186 
1189 gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash)
1190 {
1191  gboolean online_id_exists = FALSE;
1192  Account *dest_acct;
1193  Split *source_split;
1194 
1195  /* Look for an online_id in the first split */
1196  source_split = xaccTransGetSplit(trans, 0);
1197  g_assert(source_split);
1198 
1199  // No online id, no point in continuing. We'd crash if we tried.
1200  if (!gnc_import_get_split_online_id (source_split))
1201  return FALSE;
1202  // Create a hash per account of a hash of all split IDs. Then the test below will be fast if
1203  // we have many transactions to import.
1204  dest_acct = xaccSplitGetAccount (source_split);
1205  if (!g_hash_table_contains (acct_id_hash, dest_acct))
1206  {
1207  GHashTable* new_hash = g_hash_table_new (g_str_hash, g_str_equal);
1208  GList* split_list = xaccAccountGetSplitList(dest_acct);
1209  g_hash_table_insert (acct_id_hash, dest_acct, new_hash);
1210  for (;split_list;split_list=split_list->next)
1211  {
1212  if (gnc_import_split_has_online_id (split_list->data))
1213  g_hash_table_add (new_hash, (void*) gnc_import_get_split_online_id (split_list->data));
1214  }
1215  }
1216  online_id_exists = g_hash_table_contains (g_hash_table_lookup (acct_id_hash, dest_acct),
1217  gnc_import_get_split_online_id (source_split));
1218 
1219  /* If it does, abort the process for this transaction, since it is
1220  already in the system. */
1221  if (online_id_exists == TRUE)
1222  {
1223  DEBUG("%s", "Transaction with same online ID exists, destroying current transaction");
1224  xaccTransDestroy(trans);
1225  xaccTransCommitEdit(trans);
1226  }
1227  return online_id_exists;
1228 }
1229 
1230 
1231 /* ******************************************************************
1232  */
1233 
1235 GNCImportTransInfo *
1236 gnc_import_TransInfo_new (Transaction *trans, GncImportMatchMap *matchmap)
1237 {
1238  GNCImportTransInfo *transaction_info;
1239  Split *split;
1240  g_assert (trans);
1241 
1242  transaction_info = g_new0(GNCImportTransInfo, 1);
1243 
1244  transaction_info->trans = trans;
1245  /* Only use first split, the source split */
1246  split = xaccTransGetSplit(trans, 0);
1247  g_assert(split);
1248  transaction_info->first_split = split;
1249 
1250  /* Try to find a previously selected destination account
1251  string match for the ADD action */
1252  gnc_import_TransInfo_set_destacc (transaction_info,
1253  matchmap_find_destination (matchmap, transaction_info),
1254  FALSE);
1255  return transaction_info;
1256 }
1257 
1258 
1260 static gint compare_probability (gconstpointer a,
1261  gconstpointer b)
1262 {
1263  return(((GNCImportMatchInfo *)b)->probability -
1264  ((GNCImportMatchInfo *)a)->probability);
1265 }
1266 
1271 void
1272 gnc_import_TransInfo_init_matches (GNCImportTransInfo *trans_info,
1273  GNCImportSettings *settings)
1274 {
1275  GNCImportMatchInfo * best_match = NULL;
1276  g_assert (trans_info);
1277 
1278  if (trans_info->match_list != NULL)
1279  {
1280  trans_info->match_list = g_list_sort(trans_info->match_list,
1281  compare_probability);
1282  best_match = g_list_nth_data(trans_info->match_list, 0);
1284  best_match,
1285  FALSE);
1286  if (best_match != NULL &&
1287  best_match->probability >= gnc_import_Settings_get_clear_threshold(settings))
1288  {
1289  trans_info->action = GNCImport_CLEAR;
1290  }
1291  else if (best_match == NULL ||
1292  best_match->probability <= gnc_import_Settings_get_add_threshold(settings))
1293  {
1294  trans_info->action = GNCImport_ADD;
1295  }
1297  {
1298  trans_info->action = GNCImport_SKIP;
1299  }
1301  {
1302  trans_info->action = GNCImport_UPDATE;
1303  }
1304  else
1305  {
1306  trans_info->action = GNCImport_ADD;
1307  }
1308  }
1309  else
1310  {
1311  trans_info->action = GNCImport_ADD;
1312  }
1313  if (best_match &&
1314  trans_info->action == GNCImport_CLEAR &&
1316  {
1317  if (best_match->update_proposed)
1318  {
1319  trans_info->action = GNCImport_UPDATE;
1320  }
1321  }
1322 
1323  trans_info->previous_action = trans_info->action;
1324 }
1325 
1326 
1327 /* Try to automatch a transaction to a destination account if the */
1328 /* transaction hasn't already been manually assigned to another account */
1329 gboolean
1330 gnc_import_TransInfo_refresh_destacc (GNCImportTransInfo *transaction_info,
1331  GncImportMatchMap *matchmap)
1332 {
1333  Account *orig_destacc;
1334  Account *new_destacc = NULL;
1335  g_assert(transaction_info);
1336 
1337  orig_destacc = gnc_import_TransInfo_get_destacc(transaction_info);
1338 
1339  /* if we haven't manually selected a destination account for this transaction */
1340  if (gnc_import_TransInfo_get_destacc_selected_manually(transaction_info) == FALSE)
1341  {
1342  /* Try to find the destination account for this transaction based on prior ones */
1343  new_destacc = matchmap_find_destination(matchmap, transaction_info);
1344  gnc_import_TransInfo_set_destacc(transaction_info, new_destacc, FALSE);
1345  }
1346  else
1347  {
1348  new_destacc = orig_destacc;
1349  }
1350 
1351  /* account has changed */
1352  if (new_destacc != orig_destacc)
1353  {
1354  return TRUE;
1355  }
1356  else /* account is the same */
1357  {
1358  return FALSE;
1359  }
1360 }
1361 
1362 
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:1058
GNCImportTransInfo * gnc_import_TransInfo_new(Transaction *trans, GncImportMatchMap *matchmap)
Create a new object of GNCImportTransInfo here.
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:362
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
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.
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account...
Definition: Account.cpp:4007
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.
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.
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_import_TransInfo_init_matches(GNCImportTransInfo *trans_info, GNCImportSettings *settings)
Iterates through all splits of the originating account of trans_info.
Account * gnc_account_imap_find_account_bayes(GncImportMatchMap *imap, GList *tokens)
Look up an Account in the map.
Definition: Account.cpp:5847
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:99
#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.
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
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
void gnc_account_imap_add_account_bayes(GncImportMatchMap *imap, GList *tokens, Account *acc)
Updates the imap for a given account using a list of tokens.
Definition: Account.cpp:5910
Account handling public routines.
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
gboolean gnc_import_process_trans_item(GncImportMatchMap *matchmap, GNCImportTransInfo *trans_info)
/brief – Processes one match according to its selected action.
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.
gboolean gnc_import_TransInfo_refresh_destacc(GNCImportTransInfo *transaction_info, GncImportMatchMap *matchmap)
Try to automatch a given transaction to a destination account.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
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...
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:71
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
GncImportMatchMap * gnc_account_imap_create_imap(Account *acc)
Obtain an ImportMatchMap object from an Account or a Book.
Definition: Account.cpp:5477
Generic api to store and retrieve preferences.
void gnc_import_TransInfo_set_match_list(GNCImportTransInfo *info, GList *match_list)
Assigns the list of possible matches.
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
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1038
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:189
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.cpp:273
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
Returns the stored list of possible matches.
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
Returns the currently selected match in this TransInfo.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
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.
#define NREC
not reconciled or cleared
Definition: Split.h:74
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69
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...