32 #include <glib/gi18n.h>
43 #include "engine-helpers.h"
78 #define GNCIMPORT_DESC "desc"
79 #define GNCIMPORT_MEMO "memo"
80 #define GNCIMPORT_PAYEE "payee"
92 static const int MATCH_DATE_THRESHOLD = 4;
93 static const int MATCH_DATE_NOT_THRESHOLD = 14;
117 gboolean match_selected_manually;
119 GNCImportAction action;
120 GNCImportAction previous_action;
123 GList * match_tokens;
127 gboolean dest_acc_selected_manually;
139 gboolean update_proposed;
148 return info->match_list;
179 return info->first_split;
186 return info->selected_match_info;
192 gboolean selected_manually)
195 info->selected_match_info = match;
196 info->match_selected_manually = selected_manually;
203 return info->match_selected_manually;
215 GNCImportAction action)
218 if (action != info->action)
220 info->previous_action = info->action;
221 info->action = action;
229 return info->dest_acc;
233 gboolean selected_manually)
236 info->dest_acc = acc;
237 info->dest_acc_selected_manually = selected_manually;
240 if (selected_manually)
242 matchmap_store_destination (NULL, info, FALSE);
250 return info->dest_acc_selected_manually;
265 info->ref_id = ref_id;
281 return info->probability;
293 g_list_free (info->match_list);
300 if (info->match_tokens)
304 for (node = info->match_tokens; node; node = node->next)
307 g_list_free (info->match_tokens);
315 GdkPixbuf* retval = NULL;
318 const gint height = 15;
319 const gint width_each_bar = 7;
320 gchar * green_bar = (
"bggggb ");
321 gchar * yellow_bar = (
"byyyyb ");
322 gchar * red_bar = (
"brrrrb ");
323 gchar * black_bar = (
"bbbbbb ");
324 const gint width_first_bar = 1;
325 gchar * black_first_bar = (
"b");
326 const gint num_colors = 5;
328 gchar * none_color_str = g_strdup_printf(
" c None");
329 gchar * green_color_str = g_strdup_printf(
"g c green");
330 gchar * yellow_color_str = g_strdup_printf(
"y c yellow");
331 gchar * red_color_str = g_strdup_printf(
"r c red");
332 gchar * black_color_str = g_strdup_printf(
"b c black");
333 gchar * xpm[2+num_colors+height];
334 gint add_threshold, clear_threshold;
338 if (score_original < 0)
344 score = score_original;
346 size_str = g_strdup_printf(
"%d%s%d%s%d%s", (width_each_bar * score) + width_first_bar,
" ", height,
" ", num_colors,
" 1");
350 xpm[1] = none_color_str;
351 xpm[2] = green_color_str;
352 xpm[3] = yellow_color_str;
353 xpm[4] = red_color_str;
354 xpm[5] = black_color_str;
358 for (i = 0; i < height; i++)
360 xpm[num_colors+1+i] = g_new0(
char, (width_each_bar * score) + width_first_bar + 1);
361 for (j = 0; j <= score; j++)
363 if (i == 0 || i == height - 1)
367 strcat(xpm[num_colors+1+i], black_first_bar);
371 strcat(xpm[num_colors+1+i], black_bar);
378 strcat(xpm[num_colors+1+i], black_first_bar);
380 else if (j <= add_threshold)
382 strcat(xpm[num_colors+1+i], red_bar);
384 else if (j >= clear_threshold)
386 strcat(xpm[num_colors+1+i], green_bar);
390 strcat(xpm[num_colors+1+i], yellow_bar);
396 retval = gdk_pixbuf_new_from_xpm_data((
const gchar **)xpm);
397 for (i = 0; i <= num_colors + height; i++)
414 tokenize_string(GList* existing_tokens,
const char *
string)
416 char **tokenized_strings;
419 tokenized_strings = g_strsplit(
string,
" ", 0);
420 stringpos = tokenized_strings;
423 while (stringpos && *stringpos)
426 existing_tokens = g_list_prepend(existing_tokens, g_strdup(*stringpos));
433 g_strfreev(tokenized_strings);
435 return existing_tokens;
446 struct tm *tm_struct;
447 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));
484 tokens = tokenize_string(tokens, text);
489 info->match_tokens = tokens;
516 tmp_map = ((matchmap != NULL) ? matchmap :
525 tokens = TransactionGetTokens(info);
534 result = gnc_imap_find_account
535 (tmp_map, GNCIMPORT_DESC,
550 if (matchmap == NULL)
551 gnc_imap_destroy (tmp_map);
567 const char *descr, *memo;
571 g_assert (trans_info);
576 dest = ((use_match) ?
585 tmp_matchmap = ((matchmap != NULL) ?
596 tokens = TransactionGetTokens(trans_info);
607 if (descr && (strlen (descr) > 0))
608 gnc_imap_add_account (tmp_matchmap,
614 if (memo && (strlen (memo) > 0))
615 gnc_imap_add_account (tmp_matchmap,
621 if (matchmap == NULL)
622 gnc_imap_destroy (tmp_matchmap);
631 gint display_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 = labs(match_time - download_time) / 86400;
696 if (datediff_day == 0)
701 else if (datediff_day <= MATCH_DATE_THRESHOLD)
706 else if (datediff_day > MATCH_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,
843 gint process_threshold,
844 double fuzzy_amount_difference,
845 gint match_date_hardlimit)
847 GList * list_element;
848 Query *query = qof_query_create_for(GNC_ID_SPLIT);
849 g_assert (trans_info);
864 xaccQueryAddSingleAccountMatch (query, importaccount,
866 xaccQueryAddDateMatchTT (query,
867 TRUE, download_time - match_date_hardlimit * 86400,
868 TRUE, download_time + match_date_hardlimit * 86400,
888 while (list_element != NULL)
890 split_find_match (trans_info, list_element->data,
891 process_threshold, fuzzy_amount_difference);
892 list_element = g_list_next (list_element);
913 g_assert (trans_info);
932 (gnc_account_get_book
966 case GNCImport_UPDATE:
974 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
982 PERR(
"The split I am trying to update and reconcile is NULL, shouldn't happen!");
1030 if (gnc_import_split_has_online_id(trans_info->first_split))
1032 gnc_import_set_split_online_id(selected_match->split,
1033 gnc_import_get_split_online_id(trans_info->first_split));
1041 matchmap_store_destination(matchmap, trans_info, TRUE);
1048 trans_info->trans = NULL;
1052 case GNCImport_CLEAR:
1058 if (!selected_match)
1060 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
1068 PERR(
"The split I am trying to reconcile is NULL, shouldn't happen!");
1077 (selected_match->split) ==
NREC)
1079 (selected_match->split,
CREC);
1082 (selected_match->split,
gnc_time (NULL));
1086 if (gnc_import_split_has_online_id(trans_info->first_split))
1087 gnc_import_set_split_online_id
1088 (selected_match->split,
1089 gnc_import_get_split_online_id(trans_info->first_split));
1094 (selected_match->trans);
1097 matchmap_store_destination (matchmap, trans_info, TRUE);
1104 trans_info->trans = NULL;
1109 DEBUG(
"Invalid GNCImportAction for this imported transaction.");
1122 static gint check_trans_online_id(
Transaction *trans1,
void *user_data)
1126 Split *split2 = user_data;
1127 const gchar *online_id1;
1128 const gchar *online_id2;
1131 split1 = xaccTransFindSplitByAccount(trans1, account);
1132 if (split1 == split2)
1137 g_assert(split1 != NULL);
1139 if (gnc_import_split_has_online_id(split1))
1140 online_id1 = gnc_import_get_split_online_id(split1);
1142 online_id1 = gnc_import_get_trans_online_id(trans1);
1144 online_id2 = gnc_import_get_split_online_id(split2);
1146 if ((online_id1 == NULL) ||
1147 (online_id2 == NULL) ||
1148 (strcmp(online_id1, online_id2) != 0))
1163 gboolean online_id_exists = FALSE;
1165 Split *source_split;
1169 g_assert(source_split);
1174 check_trans_online_id,
1179 if (online_id_exists == TRUE)
1181 DEBUG(
"%s",
"Transaction with same online ID exists, destroying current transaction");
1185 return online_id_exists;
1202 transaction_info->trans = trans;
1206 transaction_info->first_split = split;
1211 matchmap_find_destination (matchmap, transaction_info),
1213 return transaction_info;
1218 static gint compare_probability (gconstpointer a,
1234 g_assert (trans_info);
1243 if (trans_info->match_list != NULL)
1245 trans_info->match_list = g_list_sort(trans_info->match_list,
1246 compare_probability);
1247 best_match = g_list_nth_data(trans_info->match_list, 0);
1251 if (best_match != NULL &&
1254 trans_info->action = GNCImport_CLEAR;
1255 trans_info->selected_match_info = best_match;
1257 else if (best_match == NULL ||
1260 trans_info->action = GNCImport_ADD;
1264 trans_info->action = GNCImport_SKIP;
1268 trans_info->action = GNCImport_UPDATE;
1272 trans_info->action = GNCImport_ADD;
1277 trans_info->action = GNCImport_ADD;
1280 trans_info->action == GNCImport_CLEAR &&
1283 if (best_match->update_proposed)
1285 trans_info->action = GNCImport_UPDATE;
1289 trans_info->previous_action = trans_info->action;
1301 g_assert(transaction_info);
1309 new_destacc = matchmap_find_destination(matchmap, transaction_info);
1314 new_destacc = orig_destacc;
1318 if (new_destacc != orig_destacc)
void xaccSplitSetValue(Split *s, gnc_numeric amt)
gint gnc_import_Settings_get_match_date_hardlimit(const GNCImportSettings *s)
gint xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, void *data)
GNCImportTransInfo * gnc_import_TransInfo_new(Transaction *trans, GncImportMatchMap *matchmap)
#define xaccTransAppendSplit(t, s)
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
gint gnc_import_Settings_get_clear_threshold(GNCImportSettings *settings)
Split * xaccTransGetSplit(const Transaction *trans, int i)
time64 xaccTransGetDate(const Transaction *trans)
gboolean xaccTransIsOpen(const Transaction *trans)
utility functions for the GnuCash UI
void gnc_import_TransInfo_delete(GNCImportTransInfo *info)
gnc_numeric gnc_numeric_neg(gnc_numeric a)
gint gnc_import_Settings_get_display_threshold(GNCImportSettings *settings)
void gnc_import_TransInfo_set_ref_id(GNCImportTransInfo *info, guint32 ref_id)
#define DEBUG(format, args...)
void gnc_imap_add_account_bayes(GncImportMatchMap *imap, GList *tokens, Account *acc)
#define GNC_PREFS_GROUP_IMPORT
Generic importer backend interface.
char xaccSplitGetReconcile(const Split *split)
gint safe_strcasecmp(const gchar *da, const gchar *db)
void gnc_import_TransInfo_set_selected_match(GNCImportTransInfo *info, GNCImportMatchInfo *match, gboolean selected_manually)
Split * gnc_import_TransInfo_get_fsplit(const GNCImportTransInfo *info)
void xaccTransSetDescription(Transaction *trans, const char *desc)
GdkPixbuf * gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget *widget)
Transaction * gnc_import_TransInfo_get_trans(const GNCImportTransInfo *info)
gboolean gnc_numeric_zero_p(gnc_numeric a)
void xaccSplitSetReconcile(Split *split, char recn)
Transaction * xaccSplitGetParent(const Split *split)
void gnc_import_TransInfo_set_destacc(GNCImportTransInfo *info, Account *acc, gboolean selected_manually)
Account * gnc_imap_find_account_bayes(GncImportMatchMap *imap, GList *tokens)
guint32 gnc_import_TransInfo_get_ref_id(const GNCImportTransInfo *info)
#define PERR(format, args...)
void gnc_import_TransInfo_init_matches(GNCImportTransInfo *trans_info, GNCImportSettings *settings)
GNCImportAction gnc_import_TransInfo_get_action(const GNCImportTransInfo *info)
void xaccTransDestroy(Transaction *trans)
void gnc_tm_free(struct tm *time)
free a struct tm* created with gnc_localtime() or gnc_gmtime()
#define PWARN(format, args...)
Split * gnc_import_MatchInfo_get_split(const GNCImportMatchInfo *info)
gdouble gnc_numeric_to_double(gnc_numeric n)
gint gnc_import_MatchInfo_get_probability(const GNCImportMatchInfo *info)
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Account handling public routines.
void qof_query_destroy(QofQuery *q)
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
void qof_query_set_book(QofQuery *q, QofBook *book)
gboolean gnc_import_process_trans_item(GncImportMatchMap *matchmap, GNCImportTransInfo *trans_info)
void gnc_import_TransInfo_set_action(GNCImportTransInfo *info, GNCImportAction action)
gboolean gnc_import_TransInfo_refresh_destacc(GNCImportTransInfo *transaction_info, GncImportMatchMap *matchmap)
double gnc_import_Settings_get_fuzzy_amount(GNCImportSettings *settings)
const char * xaccTransGetDescription(const Transaction *trans)
gsize qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
void xaccTransCommitEdit(Transaction *trans)
void xaccTransBeginEdit(Transaction *trans)
gboolean gnc_import_exists_online_id(Transaction *trans)
All type declarations for the whole Gnucash engine.
Split * xaccMallocSplit(QofBook *book)
void gnc_import_find_split_matches(GNCImportTransInfo *trans_info, gint process_threshold, double fuzzy_amount_difference, gint match_date_hardlimit)
Generic api to store and retrieve preferences.
void xaccSplitSetDateReconciledSecs(Split *split, time64 secs)
gboolean gnc_import_Settings_get_action_update_enabled(GNCImportSettings *settings)
gboolean gnc_import_TransInfo_get_match_selected_manually(const GNCImportTransInfo *info)
GList * qof_query_run(QofQuery *query)
gnc_numeric xaccSplitGetValue(const Split *split)
Account * xaccSplitGetAccount(const Split *s)
#define xaccAccountInsertSplit(acc, s)
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Split * xaccSplitGetOtherSplit(const Split *split)
Utility functions for writing import modules.
Account * gnc_import_TransInfo_get_destacc(const GNCImportTransInfo *info)
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)
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
GncImportMatchMap * gnc_account_create_imap(Account *acc)
gboolean gnc_import_Settings_get_action_skip_enabled(GNCImportSettings *settings)
gboolean gnc_import_TransInfo_is_balanced(const GNCImportTransInfo *info)
gint gnc_import_Settings_get_add_threshold(GNCImportSettings *settings)
const gchar * QofLogModule
gnc_numeric xaccSplitGetAmount(const Split *split)
gboolean gnc_import_TransInfo_get_destacc_selected_manually(const GNCImportTransInfo *info)