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);
863 maybe_append_string (
const char* match_string,
const char* imp_string)
865 char *norm_match_string, *norm_imp_string, *retval = NULL;
867 if (!(match_string && *match_string))
868 return g_strdup(imp_string);
870 if (!(imp_string && *imp_string))
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);
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);
880 g_free (norm_match_string);
881 g_free (norm_imp_string);
890 update_desc_and_notes (
const GNCImportTransInfo* trans_info)
895 Transaction* match_trans = selected_match->trans;
897 if (trans_info->append_text)
930 GNCImportTransInfo *trans_info)
933 gnc_numeric imbalance_value;
938 g_assert (trans_info);
957 (gnc_account_get_book
993 case GNCImport_UPDATE:
1001 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1009 PERR(
"The split I am trying to update and reconcile is NULL, shouldn't happen!");
1043 update_desc_and_notes( trans_info);
1055 if (gnc_import_split_has_online_id(trans_info->first_split))
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);
1068 matchmap_store_destination(matchmap, trans_info, TRUE);
1075 trans_info->trans = NULL;
1079 case GNCImport_CLEAR:
1085 if (!selected_match)
1087 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1095 PERR(
"The split I am trying to reconcile is NULL, shouldn't happen!");
1104 (selected_match->split) ==
NREC)
1106 (selected_match->split,
CREC);
1109 (selected_match->split,
gnc_time (NULL));
1113 if (gnc_import_split_has_online_id(trans_info->first_split))
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);
1124 (selected_match->trans);
1127 matchmap_store_destination (matchmap, trans_info, TRUE);
1134 trans_info->trans = NULL;
1139 DEBUG(
"Invalid GNCImportAction for this imported transaction.");
1152 static gint check_trans_online_id(Transaction *trans1,
void *user_data)
1156 Split *split2 = user_data;
1157 gchar *online_id1, *online_id2;
1161 split1 = xaccTransFindSplitByAccount(trans1, account);
1162 if (split1 == split2)
1167 g_assert(split1 != NULL);
1169 online_id1 = gnc_import_get_split_online_id (split1);
1171 if (!online_id1 || !online_id1[0])
1174 g_free (online_id1);
1175 online_id1 = gnc_import_get_trans_online_id (trans1);
1178 online_id2 = gnc_import_get_split_online_id(split2);
1180 retval = (!online_id1 || !online_id2 || strcmp (online_id1, online_id2)) ? 0 : 1;
1182 g_free (online_id1);
1183 g_free (online_id2);
1188 hash_account_online_ids (
Account *account)
1190 GHashTable* acct_hash = g_hash_table_new_full
1191 (g_str_hash, g_str_equal, g_free, NULL);
1194 if (gnc_import_split_has_online_id (n->data))
1196 char *
id = gnc_import_get_split_online_id (n->data);
1197 g_hash_table_insert (acct_hash, (
void*)
id, GINT_TO_POINTER (1));
1207 gboolean online_id_exists = FALSE;
1209 Split *source_split;
1210 char *source_online_id;
1214 g_assert(source_split);
1216 source_online_id = gnc_import_get_split_online_id (source_split);
1219 if (!source_online_id)
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),
1233 if (online_id_exists == TRUE)
1235 DEBUG(
"%s",
"Transaction with same online ID exists, destroying current transaction");
1239 g_free (source_online_id);
1240 return online_id_exists;
1248 GNCImportTransInfo *
1251 GNCImportTransInfo *transaction_info;
1255 transaction_info = g_new0(GNCImportTransInfo, 1);
1257 transaction_info->trans = trans;
1261 transaction_info->first_split = split;
1266 matchmap_find_destination (matchmap, transaction_info),
1268 return transaction_info;
1273 static gint compare_probability (gconstpointer a,
1286 GNCImportSettings *settings)
1289 g_assert (trans_info);
1291 if (trans_info->match_list != NULL)
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);
1299 if (best_match != NULL &&
1302 trans_info->action = GNCImport_CLEAR;
1304 else if (best_match == NULL ||
1307 trans_info->action = GNCImport_ADD;
1311 trans_info->action = GNCImport_SKIP;
1315 trans_info->action = GNCImport_UPDATE;
1319 trans_info->action = GNCImport_ADD;
1324 trans_info->action = GNCImport_ADD;
1327 trans_info->action == GNCImport_CLEAR &&
1330 if (best_match->update_proposed)
1332 trans_info->action = GNCImport_UPDATE;
1336 trans_info->previous_action = trans_info->action;
1348 g_assert(transaction_info);
1356 new_destacc = matchmap_find_destination(matchmap, transaction_info);
1361 new_destacc = orig_destacc;
1365 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...