GnuCash  4.13-74-g66d9f1383e+
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 static char*
863 maybe_append_string (const char* match_string, const char* imp_string)
864 {
865  char *norm_match_string, *norm_imp_string, *retval = NULL;
866 
867  if (!(match_string && *match_string))
868  return g_strdup(imp_string);
869 
870  if (!(imp_string && *imp_string))
871  return retval;
872 
873  norm_match_string = g_utf8_normalize (match_string, -1, G_NORMALIZE_NFC);
874  norm_imp_string = g_utf8_normalize (imp_string, -1, G_NORMALIZE_NFC);
875 
876  if (g_utf8_strlen (norm_imp_string, -1) > g_utf8_strlen (norm_match_string, -1) ||
877  !strstr (norm_match_string, norm_imp_string))
878  retval = g_strconcat(match_string, "|", imp_string, NULL);
879 
880  g_free (norm_match_string);
881  g_free (norm_imp_string);
882  return retval;
883 
884 }
885 
886 /* Append or replace transaction description and notes
887  * depending on the Append checkbox
888  */
889 static void
890 update_desc_and_notes (const GNCImportTransInfo* trans_info)
891 {
892  GNCImportMatchInfo* selected_match =
894  Transaction* imp_trans = gnc_import_TransInfo_get_trans (trans_info);
895  Transaction* match_trans = selected_match->trans;
896 
897  if (trans_info->append_text)
898  {
899  gchar *repl_str;
900 
901  repl_str =
902  maybe_append_string (xaccTransGetDescription(match_trans),
903  xaccTransGetDescription(imp_trans));
904  if (repl_str)
905  xaccTransSetDescription(match_trans, repl_str);
906  g_free (repl_str);
907 
908  repl_str =
909  maybe_append_string (xaccTransGetNotes(match_trans),
910  xaccTransGetNotes(imp_trans));
911  if (repl_str)
912  xaccTransSetNotes (match_trans, repl_str);
913  g_free (repl_str);
914  }
915  else
916  {
917  // replace the matched transaction description with the imported transaction description
918  xaccTransSetDescription (selected_match->trans,
919  xaccTransGetDescription (imp_trans));
920  // replace the matched transaction notes with the imported transaction notes
921  xaccTransSetNotes (selected_match->trans,
922  xaccTransGetNotes (imp_trans));
923  }
924 }
925 
928 gboolean
930  GNCImportTransInfo *trans_info)
931 {
932  Split * other_split;
933  gnc_numeric imbalance_value;
934  Transaction *trans;
935 
936  /* DEBUG("Begin"); */
937 
938  g_assert (trans_info);
939  /*DEBUG("Iteration %d, action %d, split %s", i,
940  trans_info->action,
941  xaccTransGetDescription (gnc_import_TransInfo_get_trans
942  (trans_info)))*/
943  switch (gnc_import_TransInfo_get_action (trans_info))
944  {
945  case GNCImport_SKIP:
946  return FALSE;
947  case GNCImport_ADD:
948  /* Transaction gets imported. */
949 
950  /* Is the transaction not balanced and there is a non-NULL destination account? */
951  if (gnc_import_TransInfo_is_balanced(trans_info) == FALSE
952  && gnc_import_TransInfo_get_destacc(trans_info) != NULL)
953  {
954  /* Create the 'other' split. */
955  Split *split =
957  (gnc_account_get_book
958  (gnc_import_TransInfo_get_destacc (trans_info)));
960  (gnc_import_TransInfo_get_trans (trans_info), split);
962  (gnc_import_TransInfo_get_destacc (trans_info), split);
963  /*xaccSplitSetBaseValue
964  (split,
965  gnc_numeric_neg(xaccTransGetImbalance
966  (gnc_import_TransInfo_get_trans (trans_info))),
967  xaccTransGetCurrency
968  (gnc_import_TransInfo_get_trans (trans_info)));*/
969  {
970  /* This is a quick workaround for the bug described in
971  http://lists.gnucash.org/pipermail/gnucash-devel/2003-August/009982.html
972  Assume that importers won't create transactions involving two or more
973  currencies so we can use xaccTransGetImbalanceValue. */
974  imbalance_value =
976  (gnc_import_TransInfo_get_trans (trans_info)));
977  xaccSplitSetValue (split, imbalance_value);
978  xaccSplitSetAmount (split, imbalance_value);
979  }
980  /*xaccSplitSetMemo (split, _("Auto-Balance split"));
981  -- disabled due to popular request */
982  }
983 
985  /*Set reconcile date to today*/
987  gnc_time (NULL));
988  /* Done editing. */
989  trans = gnc_import_TransInfo_get_trans (trans_info);
990  xaccTransCommitEdit(trans);
991  xaccTransRecordPrice(trans, PRICE_SOURCE_SPLIT_IMPORT);
992  return TRUE;
993  case GNCImport_UPDATE:
994  {
995  GNCImportMatchInfo *selected_match =
997 
998  /* If there is no selection, ignore this transaction. */
999  if (!selected_match)
1000  {
1001  PWARN("No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1002  break;
1003  }
1004 
1005  /* Transaction gets not imported but the matching one gets
1006  updated and reconciled. */
1007  if (gnc_import_MatchInfo_get_split(selected_match) == NULL)
1008  {
1009  PERR("The split I am trying to update and reconcile is NULL, shouldn't happen!");
1010  }
1011  else
1012  {
1013  /* Update and reconcile the matching transaction */
1014  /*DEBUG("BeginEdit selected_match")*/
1015  xaccTransBeginEdit(selected_match->trans);
1016 
1017  xaccTransSetDatePostedSecsNormalized(selected_match->trans,
1019  gnc_import_TransInfo_get_fsplit(trans_info))));
1020 
1021  xaccSplitSetAmount(selected_match->split,
1023  gnc_import_TransInfo_get_fsplit(trans_info)));
1024  xaccSplitSetValue(selected_match->split,
1026  gnc_import_TransInfo_get_fsplit(trans_info)));
1027 
1028  imbalance_value = xaccTransGetImbalanceValue(
1029  gnc_import_TransInfo_get_trans(trans_info));
1030  other_split = xaccSplitGetOtherSplit(selected_match->split);
1031  if (!gnc_numeric_zero_p(imbalance_value) && other_split)
1032  {
1033  if (xaccSplitGetReconcile(other_split) == NREC)
1034  {
1035  imbalance_value = gnc_numeric_neg(imbalance_value);
1036  xaccSplitSetValue(other_split, imbalance_value);
1037  xaccSplitSetAmount(other_split, imbalance_value);
1038  }
1039  /* else GC will automatically insert a split to equity
1040  to balance the transaction */
1041  }
1042 
1043  update_desc_and_notes( trans_info);
1044 
1045  if (xaccSplitGetReconcile(selected_match->split) == NREC)
1046  {
1047  xaccSplitSetReconcile(selected_match->split, CREC);
1048  }
1049 
1050  /* Set reconcile date to today */
1051  xaccSplitSetDateReconciledSecs(selected_match->split, gnc_time (NULL));
1052 
1053  /* Copy the online id to the reconciled transaction, so
1054  the match will be remembered */
1055  if (gnc_import_split_has_online_id(trans_info->first_split))
1056  {
1057  char *online_id = gnc_import_get_split_online_id
1058  (trans_info->first_split);
1059  gnc_import_set_split_online_id(selected_match->split, online_id);
1060  g_free (online_id);
1061  }
1062 
1063  /* Done editing. */
1064  /*DEBUG("CommitEdit selected_match")*/
1065  xaccTransCommitEdit(selected_match->trans);
1066 
1067  /* Store the mapping to the other account in the MatchMap. */
1068  matchmap_store_destination(matchmap, trans_info, TRUE);
1069 
1070  /* Erase the downloaded transaction */
1071  xaccTransDestroy(trans_info->trans);
1072  /*DEBUG("CommitEdit trans")*/
1073  xaccTransCommitEdit(trans_info->trans);
1074  /* Very important: Make sure the freed transaction is not freed again! */
1075  trans_info->trans = NULL;
1076  }
1077  }
1078  return TRUE;
1079  case GNCImport_CLEAR:
1080  {
1081  GNCImportMatchInfo *selected_match =
1083 
1084  /* If there is no selection, ignore this transaction. */
1085  if (!selected_match)
1086  {
1087  PWARN("No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1088  break;
1089  }
1090 
1091  /* Transaction gets not imported but the matching one gets
1092  reconciled. */
1093  if (gnc_import_MatchInfo_get_split (selected_match) == NULL)
1094  {
1095  PERR("The split I am trying to reconcile is NULL, shouldn't happen!");
1096  }
1097  else
1098  {
1099  /* Reconcile the matching transaction */
1100  /*DEBUG("BeginEdit selected_match")*/
1101  xaccTransBeginEdit(selected_match->trans);
1102 
1104  (selected_match->split) == NREC)
1106  (selected_match->split, CREC);
1107  /* Set reconcile date to today */
1109  (selected_match->split, gnc_time (NULL));
1110 
1111  /* Copy the online id to the reconciled transaction, so
1112  the match will be remembered */
1113  if (gnc_import_split_has_online_id(trans_info->first_split))
1114  {
1115  char *online_id = gnc_import_get_split_online_id
1116  (trans_info->first_split);
1117  gnc_import_set_split_online_id (selected_match->split, online_id);
1118  g_free (online_id);
1119  }
1120 
1121  /* Done editing. */
1122  /*DEBUG("CommitEdit selected_match")*/
1124  (selected_match->trans);
1125 
1126  /* Store the mapping to the other account in the MatchMap. */
1127  matchmap_store_destination (matchmap, trans_info, TRUE);
1128 
1129  /* Erase the downloaded transaction */
1130  xaccTransDestroy(trans_info->trans);
1131  /*DEBUG("CommitEdit trans")*/
1132  xaccTransCommitEdit(trans_info->trans);
1133  /* Very important: Make sure the freed transaction is not freed again! */
1134  trans_info->trans = NULL;
1135  }
1136  }
1137  return TRUE;
1138  default:
1139  DEBUG("Invalid GNCImportAction for this imported transaction.");
1140  break;
1141  }
1142  /*DEBUG("End");*/
1143  return FALSE;
1144 }
1145 
1146 /********************************************************************\
1147  * check_trans_online_id() Callback function used by
1148  * gnc_import_exists_online_id. Takes pointers to transaction and split,
1149  * returns 0 if their online_ids do NOT match, or if the split
1150  * belongs to the transaction
1151 \********************************************************************/
1152 static gint check_trans_online_id(Transaction *trans1, void *user_data)
1153 {
1154  Account *account;
1155  Split *split1;
1156  Split *split2 = user_data;
1157  gchar *online_id1, *online_id2;
1158  gint retval;
1159 
1160  account = xaccSplitGetAccount(split2);
1161  split1 = xaccTransFindSplitByAccount(trans1, account);
1162  if (split1 == split2)
1163  return 0;
1164 
1165  /* hack - we really want to iterate over the _splits_ of the account
1166  instead of the transactions */
1167  g_assert(split1 != NULL);
1168 
1169  online_id1 = gnc_import_get_split_online_id (split1);
1170 
1171  if (!online_id1 || !online_id1[0])
1172  {
1173  if (online_id1)
1174  g_free (online_id1);
1175  online_id1 = gnc_import_get_trans_online_id (trans1);
1176  }
1177 
1178  online_id2 = gnc_import_get_split_online_id(split2);
1179 
1180  retval = (!online_id1 || !online_id2 || strcmp (online_id1, online_id2)) ? 0 : 1;
1181 
1182  g_free (online_id1);
1183  g_free (online_id2);
1184  return retval;
1185 }
1186 
1187 static GHashTable*
1188 hash_account_online_ids (Account *account)
1189 {
1190  GHashTable* acct_hash = g_hash_table_new_full
1191  (g_str_hash, g_str_equal, g_free, NULL);
1192  for (GList *n = xaccAccountGetSplitList (account) ; n; n = n->next)
1193  {
1194  if (gnc_import_split_has_online_id (n->data))
1195  {
1196  char *id = gnc_import_get_split_online_id (n->data);
1197  g_hash_table_insert (acct_hash, (void*) id, GINT_TO_POINTER (1));
1198  }
1199  }
1200  return acct_hash;
1201 }
1202 
1205 gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash)
1206 {
1207  gboolean online_id_exists = FALSE;
1208  Account *dest_acct;
1209  Split *source_split;
1210  char *source_online_id;
1211 
1212  /* Look for an online_id in the first split */
1213  source_split = xaccTransGetSplit(trans, 0);
1214  g_assert(source_split);
1215 
1216  source_online_id = gnc_import_get_split_online_id (source_split);
1217 
1218  // No online id, no point in continuing. We'd crash if we tried.
1219  if (!source_online_id)
1220  return FALSE;
1221 
1222  // Create a hash per account of a hash of all split IDs. Then the
1223  // test below will be fast if we have many transactions to import.
1224  dest_acct = xaccSplitGetAccount (source_split);
1225  if (!g_hash_table_contains (acct_id_hash, dest_acct))
1226  g_hash_table_insert (acct_id_hash, dest_acct,
1227  hash_account_online_ids (dest_acct));
1228  online_id_exists = g_hash_table_contains (g_hash_table_lookup (acct_id_hash, dest_acct),
1229  source_online_id);
1230 
1231  /* If it does, abort the process for this transaction, since it is
1232  already in the system. */
1233  if (online_id_exists == TRUE)
1234  {
1235  DEBUG("%s", "Transaction with same online ID exists, destroying current transaction");
1236  xaccTransDestroy(trans);
1237  xaccTransCommitEdit(trans);
1238  }
1239  g_free (source_online_id);
1240  return online_id_exists;
1241 }
1242 
1243 
1244 /* ******************************************************************
1245  */
1246 
1248 GNCImportTransInfo *
1249 gnc_import_TransInfo_new (Transaction *trans, GncImportMatchMap *matchmap)
1250 {
1251  GNCImportTransInfo *transaction_info;
1252  Split *split;
1253  g_assert (trans);
1254 
1255  transaction_info = g_new0(GNCImportTransInfo, 1);
1256 
1257  transaction_info->trans = trans;
1258  /* Only use first split, the source split */
1259  split = xaccTransGetSplit(trans, 0);
1260  g_assert(split);
1261  transaction_info->first_split = split;
1262 
1263  /* Try to find a previously selected destination account
1264  string match for the ADD action */
1265  gnc_import_TransInfo_set_destacc (transaction_info,
1266  matchmap_find_destination (matchmap, transaction_info),
1267  FALSE);
1268  return transaction_info;
1269 }
1270 
1271 
1273 static gint compare_probability (gconstpointer a,
1274  gconstpointer b)
1275 {
1276  return(((GNCImportMatchInfo *)b)->probability -
1277  ((GNCImportMatchInfo *)a)->probability);
1278 }
1279 
1284 void
1285 gnc_import_TransInfo_init_matches (GNCImportTransInfo *trans_info,
1286  GNCImportSettings *settings)
1287 {
1288  GNCImportMatchInfo * best_match = NULL;
1289  g_assert (trans_info);
1290 
1291  if (trans_info->match_list != NULL)
1292  {
1293  trans_info->match_list = g_list_sort(trans_info->match_list,
1294  compare_probability);
1295  best_match = g_list_nth_data(trans_info->match_list, 0);
1297  best_match,
1298  FALSE);
1299  if (best_match != NULL &&
1300  best_match->probability >= gnc_import_Settings_get_clear_threshold(settings))
1301  {
1302  trans_info->action = GNCImport_CLEAR;
1303  }
1304  else if (best_match == NULL ||
1305  best_match->probability <= gnc_import_Settings_get_add_threshold(settings))
1306  {
1307  trans_info->action = GNCImport_ADD;
1308  }
1310  {
1311  trans_info->action = GNCImport_SKIP;
1312  }
1314  {
1315  trans_info->action = GNCImport_UPDATE;
1316  }
1317  else
1318  {
1319  trans_info->action = GNCImport_ADD;
1320  }
1321  }
1322  else
1323  {
1324  trans_info->action = GNCImport_ADD;
1325  }
1326  if (best_match &&
1327  trans_info->action == GNCImport_CLEAR &&
1329  {
1330  if (best_match->update_proposed)
1331  {
1332  trans_info->action = GNCImport_UPDATE;
1333  }
1334  }
1335 
1336  trans_info->previous_action = trans_info->action;
1337 }
1338 
1339 
1340 /* Try to automatch a transaction to a destination account if the */
1341 /* transaction hasn't already been manually assigned to another account */
1342 gboolean
1343 gnc_import_TransInfo_refresh_destacc (GNCImportTransInfo *transaction_info,
1344  GncImportMatchMap *matchmap)
1345 {
1346  Account *orig_destacc;
1347  Account *new_destacc = NULL;
1348  g_assert(transaction_info);
1349 
1350  orig_destacc = gnc_import_TransInfo_get_destacc(transaction_info);
1351 
1352  /* if we haven't manually selected a destination account for this transaction */
1353  if (gnc_import_TransInfo_get_destacc_selected_manually(transaction_info) == FALSE)
1354  {
1355  /* Try to find the destination account for this transaction based on prior ones */
1356  new_destacc = matchmap_find_destination(matchmap, transaction_info);
1357  gnc_import_TransInfo_set_destacc(transaction_info, new_destacc, FALSE);
1358  }
1359  else
1360  {
1361  new_destacc = orig_destacc;
1362  }
1363 
1364  /* account has changed */
1365  if (new_destacc != orig_destacc)
1366  {
1367  return TRUE;
1368  }
1369  else /* account is the same */
1370  {
1371  return FALSE;
1372  }
1373 }
1374 
1375 
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:370
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...