32 #include <glib/gi18n.h> 44 #include "engine-helpers.h" 51 #define GNCIMPORT_DESC "desc" 52 #define GNCIMPORT_MEMO "memo" 53 #define GNCIMPORT_PAYEE "payee" 59 static QofLogModule log_module = GNC_MOD_IMPORT;
65 static void matchmap_store_destination(
Account* base_acc,
66 GNCImportTransInfo* trans_info,
69 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info);
79 gboolean selected_manually;
89 GNCImportSelectedMatchInfo selected_match_info;
91 GNCImportAction action;
92 GNCImportAction previous_action;
99 gboolean dest_acc_selected_manually;
105 gboolean append_text;
109 gnc_numeric lsplit_price;
112 char lsplit_rec_state;
115 gnc_numeric lsplit_value;
120 gnc_numeric lsplit_amount;
121 gboolean lsplit_amount_selected_manually;
130 return info->match_list;
136 g_return_if_fail (info);
137 info->match_list = g_list_remove (info->match_list, static_cast<gpointer>(info->match_list->data));
138 if (info->match_list)
139 info->selected_match_info.selected_match =
static_cast<GNCImportMatchInfo*
>(info->match_list->data);
142 info->selected_match_info.selected_match =
nullptr;
165 return info->first_split;
172 return info->selected_match_info.selected_match;
178 gboolean selected_manually)
181 info->selected_match_info.selected_match = match;
182 info->selected_match_info.selected_manually = selected_manually;
189 return info->selected_match_info.selected_manually;
201 GNCImportAction action)
204 if (action != info->action)
206 info->previous_action = info->action;
207 info->action = action;
215 return info->dest_acc;
219 gboolean selected_manually)
222 info->dest_acc = acc;
223 info->dest_acc_selected_manually = selected_manually;
226 if (selected_manually)
227 matchmap_store_destination (
nullptr, info,
false);
229 trans_info_calculate_dest_amount (info);
236 return info->dest_acc_selected_manually;
251 info->ref_id = ref_id;
258 return info->lsplit_price;
266 info->lsplit_price = lprice;
272 info->lsplit_amount_selected_manually =
false;
273 trans_info_calculate_dest_amount(info);
281 return info->lsplit_amount;
288 return info->lsplit_value;
298 info->lsplit_price = lsplit->price;
299 info->lsplit_action = g_strdup(lsplit->action);
300 info->lsplit_memo = g_strdup(lsplit->memo);
303 info->lsplit_amount = lsplit->amount;
304 info->lsplit_amount_selected_manually =
true;
310 info->dest_acc = lsplit->account;
311 info->lsplit_rec_state = lsplit->rec_state;
312 info->lsplit_rec_date = lsplit->rec_date;
318 gboolean append_text)
321 info->append_text = append_text;
336 return info->probability;
345 g_list_free_full (info->match_list, g_free);
352 g_list_free_full (info->match_tokens, g_free);
353 g_free(info->lsplit_action);
354 g_free(info->lsplit_memo);
362 constexpr gint height = 15;
363 constexpr gint width_each_bar = 7;
364 constexpr gint width_first_bar = 1;
365 constexpr gint num_colors = 5;
366 constexpr
size_t xpm_size = 2 + num_colors + height;
367 gchar * xpm[xpm_size];
372 auto score = std::max (0, score_original);
375 xpm[0] = g_strdup_printf(
"%d%s%d%s%d%s", (width_each_bar * score) + width_first_bar,
" ", height,
" ", num_colors,
" 1");
378 xpm[1] = g_strdup(
" c None");
379 xpm[2] = g_strdup(
"g c green");
380 xpm[3] = g_strdup(
"y c yellow");
381 xpm[4] = g_strdup(
"r c red");
382 xpm[5] = g_strdup(
"b c black");
386 for (
int i = 0; i < height; i++)
388 xpm[num_colors+1+i] = g_new0(
char, (width_each_bar * score) + width_first_bar + 1);
389 for (
int j = 0; j <= score; j++)
392 strcat(xpm[num_colors+1+i],
"b");
393 else if (i == 0 || i == height - 1)
394 strcat(xpm[num_colors+1+i],
"bbbbbb ");
395 else if (j <= add_threshold)
396 strcat(xpm[num_colors+1+i],
"brrrrb ");
397 else if (j >= clear_threshold)
398 strcat(xpm[num_colors+1+i],
"bggggb ");
400 strcat(xpm[num_colors+1+i],
"byyyyb ");
403 GError *err =
nullptr;
404 std::string xpm_str =
"/* XPM */\nstatic char * XFACE[] = {\n";
406 for (
auto i = 0UL; i < xpm_size - 1; i++)
415 auto gstream = g_memory_input_stream_new_from_data(xpm_str.c_str(), -1,
418 gdk_pixbuf_new_from_stream(G_INPUT_STREAM(gstream),
nullptr, &err);
419 g_object_unref(gstream);
422 PERR(
"Failed to create pixbuf from XPM data: %s", err->message);
435 tokenize_string(GList* existing_tokens,
const char *
string)
437 char **tokenized_strings = g_strsplit(
string,
" ", 0);
438 char **stringpos = tokenized_strings;
441 while (stringpos && *stringpos)
443 if ((strlen(*stringpos) > 0) &&
444 (!g_list_find_custom (existing_tokens, *stringpos, (GCompareFunc)g_strcmp0)))
445 existing_tokens = g_list_prepend(existing_tokens, g_strdup(*stringpos));
451 g_strfreev(tokenized_strings);
453 return existing_tokens;
458 TransactionGetTokens(GNCImportTransInfo *info)
461 g_return_val_if_fail (info,
nullptr);
462 if (info->match_tokens)
return info->match_tokens;
465 g_assert(transaction);
469 GList *tokens =
nullptr;
470 tokens = tokenize_string(tokens, text);
478 char local_day_of_week[16];
479 if (!
qof_strftime(local_day_of_week,
sizeof(local_day_of_week),
"%A", tm_struct))
480 PERR(
"TransactionGetTokens: error, strftime failed\n");
485 tokens = g_list_prepend(tokens, g_strdup(local_day_of_week));
491 tokens = tokenize_string(tokens, text);
494 info->match_tokens = tokens;
502 matchmap_find_destination (
Account *base_acc, GNCImportTransInfo *info)
512 GList* tokens = TransactionGetTokens(info);
519 result = gnc_account_imap_find_account
520 (orig_acc, GNCIMPORT_DESC,
531 matchmap_store_destination (
Account *base_acc,
532 GNCImportTransInfo *trans_info,
535 g_assert (trans_info);
556 auto tokens = TransactionGetTokens(trans_info);
569 gnc_account_imap_add_account (orig_acc, GNCIMPORT_DESC, desc, dest);
571 gnc_account_imap_add_account (orig_acc, GNCIMPORT_MEMO, memo, dest);
581 gint display_threshold,
583 gint date_not_threshold,
584 double fuzzy_amount_difference)
594 auto downloaded_split_amount =
599 if (fabs(downloaded_split_amount - match_split_amount) < 1e-6)
610 else if (fabs (downloaded_split_amount - match_split_amount) <=
611 fuzzy_amount_difference)
631 auto datediff_day = llabs(match_time - download_time) / 86400;
638 if (datediff_day == 0)
643 else if (datediff_day <= date_threshold)
648 else if (datediff_day > date_not_threshold)
661 auto update_proposed = (prob < 6);
664 auto new_trans_str = gnc_get_num_action(new_trans, new_trans_fsplit);
665 if (new_trans_str && *new_trans_str)
668 auto conversion_ok =
true;
672 auto new_trans_number = strtol(new_trans_str, &endptr, 10);
675 conversion_ok = !(errno || endptr == new_trans_str);
679 auto split_number = strtol(split_str, &endptr, 10);
680 conversion_ok = !(errno || endptr == split_str);
682 if ( (conversion_ok && (split_number == new_trans_number)) ||
683 (g_strcmp0(new_trans_str, split_str) == 0) )
689 else if (strlen(new_trans_str) > 0 && strlen(split_str) > 0)
730 else if ((strncasecmp(descr,
744 if (prob < display_threshold)
752 match_info->probability = prob;
753 match_info->update_proposed = update_proposed;
754 match_info->split = split;
760 trans_info->match_list = g_list_prepend(trans_info->match_list, match_info);
767 maybe_append_string (
const char* match_string,
const char* imp_string)
769 if (!(match_string && *match_string))
770 return g_strdup(imp_string);
772 if (!(imp_string && *imp_string))
775 auto norm_match_string = g_utf8_normalize (match_string, -1, G_NORMALIZE_NFC);
776 auto norm_imp_string = g_utf8_normalize (imp_string, -1, G_NORMALIZE_NFC);
778 char *retval =
nullptr;
779 if (g_utf8_strlen (norm_imp_string, -1) > g_utf8_strlen (norm_match_string, -1) ||
780 !strstr (norm_match_string, norm_imp_string))
781 retval = g_strconcat(match_string,
"|", imp_string,
nullptr);
783 g_free (norm_match_string);
784 g_free (norm_imp_string);
793 update_desc_and_notes (
const GNCImportTransInfo* trans_info)
798 if (trans_info->append_text)
800 auto match_trans = selected_match->trans;
825 process_reconcile(
Account *base_acc,
826 GNCImportTransInfo *trans_info,
841 auto online_id = gnc_import_get_split_online_id(trans_info->first_split);
842 if (online_id && *online_id)
843 gnc_import_set_split_online_id(selected_match->split, online_id);
852 matchmap_store_destination(base_acc, trans_info,
true);
859 trans_info->trans =
nullptr;
866 GNCImportTransInfo *trans_info)
868 g_assert (trans_info);
898 PWARN(
"Missing exchange rate while adding transaction '%s', will assume rate of 1",
911 case GNCImport_UPDATE:
918 PWARN(
"No matching transaction to be cleared was chosen. Imported transaction will be ignored.");
925 PERR(
"The split I am trying to update and reconcile is nullptr, shouldn't happen!");
965 PWARN(
"Updated transaction '%s', but not other split.",
971 if (fs_memo && *fs_memo)
974 update_desc_and_notes(trans_info);
979 process_reconcile (base_acc, trans_info, selected_match);
983 case GNCImport_CLEAR:
990 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
997 PERR(
"The split I am trying to reconcile is nullptr, shouldn't happen!");
1001 process_reconcile(base_acc, trans_info, selected_match);
1006 DEBUG(
"Invalid GNCImportAction for this imported transaction.");
1014 hash_account_online_ids (
Account *account)
1016 auto acct_hash = g_hash_table_new_full
1017 (g_str_hash, g_str_equal, g_free,
nullptr);
1018 for (
auto split : xaccAccountGetSplits (account))
1020 auto id = gnc_import_get_split_online_id (split);
1022 g_hash_table_insert (acct_hash, (
void*)
id, GINT_TO_POINTER (1));
1034 g_assert(source_split);
1036 auto source_online_id = gnc_import_get_split_online_id (source_split);
1039 if (!source_online_id)
1046 auto online_id_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (acct_id_hash, dest_acct));
1048 if (!online_id_hash)
1050 online_id_hash = hash_account_online_ids (dest_acct);
1051 g_hash_table_insert (acct_id_hash, dest_acct, online_id_hash);
1054 auto online_id_exists = g_hash_table_contains (online_id_hash, source_online_id);
1055 g_free (source_online_id);
1056 return online_id_exists;
1066 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info)
1069 if (!info->lsplit_amount_selected_manually)
1070 info->lsplit_amount = {0, 1};
1081 info->lsplit_amount = info->lsplit_value;
1082 else if (info->lsplit_amount_selected_manually &&
1101 GNCImportTransInfo *
1106 auto t_info = g_new0(GNCImportTransInfo, 1);
1108 t_info->trans = trans;
1112 t_info->first_split = split;
1117 matchmap_find_destination (base_acc, t_info),
1125 static gint compare_probability (gconstpointer a,
1138 GNCImportSettings *settings)
1140 g_assert (trans_info);
1142 if (trans_info->match_list)
1144 trans_info->match_list = g_list_sort(trans_info->match_list,
1145 compare_probability);
1146 auto best_match =
static_cast<GNCImportMatchInfo*
>(g_list_nth_data(trans_info->match_list, 0));
1152 best_match->update_proposed)
1153 trans_info->action = GNCImport_UPDATE;
1155 trans_info->action = GNCImport_CLEAR;
1157 else if (!best_match ||
1159 trans_info->action = GNCImport_ADD;
1161 trans_info->action = GNCImport_SKIP;
1163 trans_info->action = GNCImport_UPDATE;
1165 trans_info->action = GNCImport_ADD;
1168 trans_info->action = GNCImport_ADD;
1171 trans_info->previous_action = trans_info->action;
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 ...
#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))...
gnc_numeric gnc_import_TransInfo_get_dest_value(const GNCImportTransInfo *info)
Returns the destination split value for this TransInfo.
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.
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.
GNCImportTransInfo * gnc_import_TransInfo_new(Transaction *trans, Account *base_acc)
Create a new object of GNCImportTransInfo here.
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_account_imap_add_account_bayes(Account *acc, GList *tokens, Account *added_acc)
Updates the imap for a given account using a list of tokens.
void gnc_import_TransInfo_init_matches(GNCImportTransInfo *trans_info, GNCImportSettings *settings)
Iterates through all splits of trans_info's originating account match list.
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.
gboolean gnc_import_process_trans_item(Account *base_acc, GNCImportTransInfo *trans_info)
/brief – Processes one match according to its selected action.
gdouble gnc_numeric_to_double(gnc_numeric n)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
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...
Account handling public routines.
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
Account public routines (C++ api)
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
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.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gnc_numeric gnc_import_TransInfo_get_dest_amount(const GNCImportTransInfo *info)
Returns the destination split amount for this TransInfo.
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...
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
Division.
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.
Generic api to store and retrieve preferences.
void gnc_import_TransInfo_set_price(GNCImportTransInfo *info, gnc_numeric lprice)
Set the exchange rate for this TransInfo.
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().
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
#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
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
time64 gnc_time(time64 *tbuf)
get the current time
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
Check for error signal in value.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
gnc_numeric gnc_import_TransInfo_get_price(const GNCImportTransInfo *info)
Returns the exchange rate for this TransInfo.
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
Returns the stored list of possible matches.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
Returns the currently selected match in this TransInfo.
void gnc_import_TransInfo_remove_top_match(GNCImportTransInfo *info)
Remove the first match in the list of possible matches.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
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.
void gnc_import_TransInfo_set_last_split_info(GNCImportTransInfo *info, GNCImportLastSplitInfo *lsplit)
Sets additional parameters to be used to generate the closing split.
#define NREC
not reconciled or cleared
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
Account * gnc_account_imap_find_account_bayes(Account *acc, GList *tokens)
Look up an Account in the map.
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...