32 #include <glib/gi18n.h> 43 #include "engine-helpers.h" 47 #define GNCIMPORT_DESC "desc" 48 #define GNCIMPORT_MEMO "memo" 49 #define GNCIMPORT_PAYEE "payee" 55 static QofLogModule log_module = GNC_MOD_IMPORT;
63 GNCImportTransInfo *trans_info,
74 gboolean selected_manually;
84 GNCImportSelectedMatchInfo selected_match_info;
86 GNCImportAction action;
87 GNCImportAction previous_action;
94 gboolean dest_acc_selected_manually;
100 gboolean append_text;
109 return info->match_list;
116 info->match_list = match_list;
119 info->selected_match_info.selected_match = match_list->data;
123 info->selected_match_info.selected_match = NULL;
156 return info->first_split;
163 return info->selected_match_info.selected_match;
169 gboolean selected_manually)
172 info->selected_match_info.selected_match = match;
173 info->selected_match_info.selected_manually = selected_manually;
180 return info->selected_match_info.selected_manually;
192 GNCImportAction action)
195 if (action != info->action)
197 info->previous_action = info->action;
198 info->action = action;
206 return info->dest_acc;
210 gboolean selected_manually)
213 info->dest_acc = acc;
214 info->dest_acc_selected_manually = selected_manually;
217 if (selected_manually)
219 matchmap_store_destination (NULL, info, FALSE);
227 return info->dest_acc_selected_manually;
242 info->ref_id = ref_id;
248 gboolean append_text)
251 info->append_text = append_text;
267 return info->probability;
279 g_list_free (info->match_list);
286 if (info->match_tokens)
290 for (node = info->match_tokens; node; node = node->next)
293 g_list_free (info->match_tokens);
301 GdkPixbuf* retval = NULL;
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;
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;
324 if (score_original < 0)
330 score = score_original;
332 size_str = g_strdup_printf(
"%d%s%d%s%d%s", (width_each_bar * score) + width_first_bar,
" ", height,
" ", num_colors,
" 1");
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;
344 for (i = 0; i < height; i++)
346 xpm[num_colors+1+i] = g_new0(
char, (width_each_bar * score) + width_first_bar + 1);
347 for (j = 0; j <= score; j++)
349 if (i == 0 || i == height - 1)
353 strcat(xpm[num_colors+1+i], black_first_bar);
357 strcat(xpm[num_colors+1+i], black_bar);
364 strcat(xpm[num_colors+1+i], black_first_bar);
366 else if (j <= add_threshold)
368 strcat(xpm[num_colors+1+i], red_bar);
370 else if (j >= clear_threshold)
372 strcat(xpm[num_colors+1+i], green_bar);
376 strcat(xpm[num_colors+1+i], yellow_bar);
382 retval = gdk_pixbuf_new_from_xpm_data((
const gchar **)xpm);
383 for (i = 0; i <= num_colors + height; i++)
400 tokenize_string(GList* existing_tokens,
const char *
string)
402 char **tokenized_strings;
405 tokenized_strings = g_strsplit(
string,
" ", 0);
406 stringpos = tokenized_strings;
409 while (stringpos && *stringpos)
411 if (strlen(*stringpos) > 0)
414 gboolean duplicated = FALSE;
415 for (GList* token = existing_tokens; token != NULL; token = token->next)
417 if (g_strcmp0(token->data, *stringpos) == 0)
423 if (duplicated == FALSE)
426 existing_tokens = g_list_prepend(existing_tokens, g_strdup(*stringpos));
435 g_strfreev(tokenized_strings);
437 return existing_tokens;
442 TransactionGetTokens(GNCImportTransInfo *info)
444 Transaction* transaction;
448 struct tm *tm_struct;
449 char local_day_of_week[16];
451 g_return_val_if_fail (info, NULL);
452 if (info->match_tokens)
return info->match_tokens;
455 g_assert(transaction);
461 tokens = tokenize_string(tokens, text);
469 if (!
qof_strftime(local_day_of_week,
sizeof(local_day_of_week),
"%A", tm_struct))
471 PERR(
"TransactionGetTokens: error, strftime failed\n");
477 tokens = g_list_prepend(tokens, g_strdup(local_day_of_week));
483 tokens = tokenize_string(tokens, text);
487 info->match_tokens = tokens;
506 matchmap_find_destination (
GncImportMatchMap *matchmap, GNCImportTransInfo *info)
514 tmp_map = ((matchmap != NULL) ? matchmap :
523 tokens = TransactionGetTokens(info);
532 result = gnc_account_imap_find_account
533 (tmp_map, GNCIMPORT_DESC,
548 if (matchmap == NULL)
549 gnc_imap_destroy (tmp_map);
560 GNCImportTransInfo *trans_info,
565 const char *descr, *memo;
569 g_assert (trans_info);
574 dest = ((use_match) ?
583 tmp_matchmap = ((matchmap != NULL) ?
594 tokens = TransactionGetTokens(trans_info);
605 if (descr && (strlen (descr) > 0))
606 gnc_account_imap_add_account (tmp_matchmap,
612 if (memo && (strlen (memo) > 0))
613 gnc_account_imap_add_account (tmp_matchmap,
619 if (matchmap == NULL)
620 gnc_imap_destroy (tmp_matchmap);
629 gint display_threshold,
631 gint date_not_threshold,
632 double fuzzy_amount_difference)
642 gboolean update_proposed;
643 double downloaded_split_amount, match_split_amount;
644 time64 match_time, download_time;
652 downloaded_split_amount =
657 if (fabs(downloaded_split_amount - match_split_amount) < 1e-6)
668 else if (fabs (downloaded_split_amount - match_split_amount) <=
669 fuzzy_amount_difference)
689 datediff_day = llabs(match_time - download_time) / 86400;
696 if (datediff_day == 0)
701 else if (datediff_day <= date_threshold)
706 else if (datediff_day > date_not_threshold)
719 update_proposed = (prob < 6);
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)
726 long new_trans_number, split_number;
727 const gchar *split_str;
729 gboolean conversion_ok = TRUE;
733 new_trans_number = strtol(new_trans_str, &endptr, 10);
736 if (errno || endptr == new_trans_str)
737 conversion_ok = FALSE;
741 split_number = strtol(split_str, &endptr, 10);
742 if (errno || endptr == split_str)
743 conversion_ok = FALSE;
745 if ( (conversion_ok && (split_number == new_trans_number)) ||
746 (g_strcmp0(new_trans_str, split_str) == 0) )
752 else if (strlen(new_trans_str) > 0 && strlen(split_str) > 0)
764 if (memo && strlen(memo) != 0)
789 if (descr && strlen(descr) != 0)
799 else if ((strncasecmp(descr,
815 if (prob < display_threshold)
825 match_info->probability = prob;
826 match_info->update_proposed = update_proposed;
827 match_info->split = split;
833 trans_info->match_list =
834 g_list_prepend(trans_info->match_list,
844 desc_append (Transaction* selected_match_trans, gchar *new_desc)
847 gchar* tmp = g_strconcat(curr_desc,
"|", new_desc, NULL);
854 notes_append (Transaction* selected_match_trans, gchar* new_notes)
857 gchar* tmp = g_strconcat (curr_notes,
"|", new_notes, NULL);
866 update_desc_and_notes (
const GNCImportTransInfo* trans_info)
872 if (trans_info->append_text)
874 gchar *desc_imported, *desc_matched, *note_imported, *note_matched;
878 raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
881 raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
884 raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
887 raw_str ? g_utf8_normalize (raw_str, -1, G_NORMALIZE_ALL) : NULL;
892 g_utf8_strlen (desc_imported, -1) > g_utf8_strlen (desc_matched, -1) ||
893 !strstr (desc_matched, desc_imported)))
895 if (desc_matched && *desc_matched)
896 desc_append (selected_match->trans, desc_imported);
904 g_utf8_strlen (note_imported, -1) > g_utf8_strlen (note_matched, -1) ||
905 !strstr (note_matched, note_imported)))
907 if (note_matched && *note_matched)
908 notes_append (selected_match->trans, note_imported);
913 g_free(desc_imported);
914 g_free(desc_matched);
915 g_free(note_imported);
916 g_free(note_matched);
933 GNCImportTransInfo *trans_info)
936 gnc_numeric imbalance_value;
941 g_assert (trans_info);
960 (gnc_account_get_book
996 case GNCImport_UPDATE:
1002 if (!selected_match)
1004 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1012 PERR(
"The split I am trying to update and reconcile is NULL, shouldn't happen!");
1046 update_desc_and_notes( trans_info);
1058 if (gnc_import_split_has_online_id(trans_info->first_split))
1060 gnc_import_set_split_online_id(selected_match->split,
1061 gnc_import_get_split_online_id(trans_info->first_split));
1069 matchmap_store_destination(matchmap, trans_info, TRUE);
1076 trans_info->trans = NULL;
1080 case GNCImport_CLEAR:
1086 if (!selected_match)
1088 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1096 PERR(
"The split I am trying to reconcile is NULL, shouldn't happen!");
1105 (selected_match->split) ==
NREC)
1107 (selected_match->split,
CREC);
1110 (selected_match->split,
gnc_time (NULL));
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));
1122 (selected_match->trans);
1125 matchmap_store_destination (matchmap, trans_info, TRUE);
1132 trans_info->trans = NULL;
1137 DEBUG(
"Invalid GNCImportAction for this imported transaction.");
1150 static gint check_trans_online_id(Transaction *trans1,
void *user_data)
1154 Split *split2 = user_data;
1155 const gchar *online_id1;
1156 const gchar *online_id2;
1159 split1 = xaccTransFindSplitByAccount(trans1, account);
1160 if (split1 == split2)
1165 g_assert(split1 != NULL);
1167 if (gnc_import_split_has_online_id(split1))
1168 online_id1 = gnc_import_get_split_online_id(split1);
1170 online_id1 = gnc_import_get_trans_online_id(trans1);
1172 online_id2 = gnc_import_get_split_online_id(split2);
1174 if ((online_id1 == NULL) ||
1175 (online_id2 == NULL) ||
1176 (strcmp(online_id1, online_id2) != 0))
1191 gboolean online_id_exists = FALSE;
1193 Split *source_split;
1197 g_assert(source_split);
1200 if (!gnc_import_get_split_online_id (source_split))
1205 if (!g_hash_table_contains (acct_id_hash, dest_acct))
1207 GHashTable* new_hash = g_hash_table_new (g_str_hash, g_str_equal);
1209 g_hash_table_insert (acct_id_hash, dest_acct, new_hash);
1210 for (;split_list;split_list=split_list->next)
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));
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));
1221 if (online_id_exists == TRUE)
1223 DEBUG(
"%s",
"Transaction with same online ID exists, destroying current transaction");
1227 return online_id_exists;
1235 GNCImportTransInfo *
1238 GNCImportTransInfo *transaction_info;
1242 transaction_info = g_new0(GNCImportTransInfo, 1);
1244 transaction_info->trans = trans;
1248 transaction_info->first_split = split;
1253 matchmap_find_destination (matchmap, transaction_info),
1255 return transaction_info;
1260 static gint compare_probability (gconstpointer a,
1273 GNCImportSettings *settings)
1276 g_assert (trans_info);
1278 if (trans_info->match_list != NULL)
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);
1286 if (best_match != NULL &&
1289 trans_info->action = GNCImport_CLEAR;
1291 else if (best_match == NULL ||
1294 trans_info->action = GNCImport_ADD;
1298 trans_info->action = GNCImport_SKIP;
1302 trans_info->action = GNCImport_UPDATE;
1306 trans_info->action = GNCImport_ADD;
1311 trans_info->action = GNCImport_ADD;
1314 trans_info->action == GNCImport_CLEAR &&
1317 if (best_match->update_proposed)
1319 trans_info->action = GNCImport_UPDATE;
1323 trans_info->previous_action = trans_info->action;
1335 g_assert(transaction_info);
1343 new_destacc = matchmap_find_destination(matchmap, transaction_info);
1348 new_destacc = orig_destacc;
1352 if (new_destacc != orig_destacc)
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
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 ...
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.
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'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...
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.
#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.
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 'other account' 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.
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.
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()
#define PWARN(format, args...)
Log a warning.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
Split * gnc_import_MatchInfo_get_split(const GNCImportMatchInfo *info)
Get the split ('this-side split') 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's commodity that the split should have...
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.
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'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.
Split * xaccMallocSplit(QofBook *book)
Constructor.
GncImportMatchMap * gnc_account_imap_create_imap(Account *acc)
Obtain an ImportMatchMap object from an Account or a Book.
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's commodity.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
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 'other account' of this transaction.
struct tm * gnc_gmtime(const time64 *secs)
fill out a time struct from a 64-bit time value
time64 gnc_time(time64 *tbuf)
get the current local time
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
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...
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
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
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...