32 #include <glib/gi18n.h> 44 #include "engine-helpers.h" 50 #define GNCIMPORT_DESC "desc" 51 #define GNCIMPORT_MEMO "memo" 52 #define GNCIMPORT_PAYEE "payee" 58 static QofLogModule log_module = GNC_MOD_IMPORT;
64 static void matchmap_store_destination(
Account* base_acc,
65 GNCImportTransInfo* trans_info,
68 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info);
78 gboolean selected_manually;
88 GNCImportSelectedMatchInfo selected_match_info;
90 GNCImportAction action;
91 GNCImportAction previous_action;
98 gboolean dest_acc_selected_manually;
104 gboolean append_text;
108 gnc_numeric lsplit_price;
111 char lsplit_rec_state;
114 gnc_numeric lsplit_value;
119 gnc_numeric lsplit_amount;
120 gboolean lsplit_amount_selected_manually;
129 return info->match_list;
135 g_return_if_fail (info);
136 info->match_list = g_list_remove (info->match_list, static_cast<gpointer>(info->match_list->data));
137 if (info->match_list)
138 info->selected_match_info.selected_match =
static_cast<GNCImportMatchInfo*
>(info->match_list->data);
141 info->selected_match_info.selected_match =
nullptr;
164 return info->first_split;
171 return info->selected_match_info.selected_match;
177 gboolean selected_manually)
180 info->selected_match_info.selected_match = match;
181 info->selected_match_info.selected_manually = selected_manually;
188 return info->selected_match_info.selected_manually;
200 GNCImportAction action)
203 if (action != info->action)
205 info->previous_action = info->action;
206 info->action = action;
214 return info->dest_acc;
218 gboolean selected_manually)
221 info->dest_acc = acc;
222 info->dest_acc_selected_manually = selected_manually;
225 if (selected_manually)
226 matchmap_store_destination (
nullptr, info,
false);
228 trans_info_calculate_dest_amount (info);
235 return info->dest_acc_selected_manually;
250 info->ref_id = ref_id;
257 return info->lsplit_price;
265 info->lsplit_price = lprice;
271 info->lsplit_amount_selected_manually =
false;
272 trans_info_calculate_dest_amount(info);
280 return info->lsplit_amount;
287 return info->lsplit_value;
297 info->lsplit_price = lsplit->price;
298 info->lsplit_action = g_strdup(lsplit->action);
299 info->lsplit_memo = g_strdup(lsplit->memo);
302 info->lsplit_amount = lsplit->amount;
303 info->lsplit_amount_selected_manually =
true;
309 info->dest_acc = lsplit->account;
310 info->lsplit_rec_state = lsplit->rec_state;
311 info->lsplit_rec_date = lsplit->rec_date;
317 gboolean append_text)
320 info->append_text = append_text;
335 return info->probability;
344 g_list_free_full (info->match_list, g_free);
351 g_list_free_full (info->match_tokens, g_free);
352 g_free(info->lsplit_action);
353 g_free(info->lsplit_memo);
361 constexpr gint height = 15;
362 constexpr gint width_each_bar = 7;
363 constexpr gint width_first_bar = 1;
364 constexpr gint num_colors = 5;
365 gchar * xpm[2 + num_colors + height];
370 auto score = std::max (0, score_original);
373 xpm[0] = g_strdup_printf(
"%d%s%d%s%d%s", (width_each_bar * score) + width_first_bar,
" ", height,
" ", num_colors,
" 1");
376 xpm[1] = g_strdup(
" c None");
377 xpm[2] = g_strdup(
"g c green");
378 xpm[3] = g_strdup(
"y c yellow");
379 xpm[4] = g_strdup(
"r c red");
380 xpm[5] = g_strdup(
"b c black");
384 for (
int i = 0; i < height; i++)
386 xpm[num_colors+1+i] = g_new0(
char, (width_each_bar * score) + width_first_bar + 1);
387 for (
int j = 0; j <= score; j++)
390 strcat(xpm[num_colors+1+i],
"b");
391 else if (i == 0 || i == height - 1)
392 strcat(xpm[num_colors+1+i],
"bbbbbb ");
393 else if (j <= add_threshold)
394 strcat(xpm[num_colors+1+i],
"brrrrb ");
395 else if (j >= clear_threshold)
396 strcat(xpm[num_colors+1+i],
"bggggb ");
398 strcat(xpm[num_colors+1+i],
"byyyyb ");
402 auto retval = gdk_pixbuf_new_from_xpm_data((
const gchar **)xpm);
403 for (
int i = 0; i <= num_colors + height; i++)
417 tokenize_string(GList* existing_tokens,
const char *
string)
419 char **tokenized_strings = g_strsplit(
string,
" ", 0);
420 char **stringpos = tokenized_strings;
423 while (stringpos && *stringpos)
425 if ((strlen(*stringpos) > 0) &&
426 (!g_list_find_custom (existing_tokens, *stringpos, (GCompareFunc)g_strcmp0)))
427 existing_tokens = g_list_prepend(existing_tokens, g_strdup(*stringpos));
433 g_strfreev(tokenized_strings);
435 return existing_tokens;
440 TransactionGetTokens(GNCImportTransInfo *info)
443 g_return_val_if_fail (info,
nullptr);
444 if (info->match_tokens)
return info->match_tokens;
447 g_assert(transaction);
451 GList *tokens =
nullptr;
452 tokens = tokenize_string(tokens, text);
460 char local_day_of_week[16];
461 if (!
qof_strftime(local_day_of_week,
sizeof(local_day_of_week),
"%A", tm_struct))
462 PERR(
"TransactionGetTokens: error, strftime failed\n");
467 tokens = g_list_prepend(tokens, g_strdup(local_day_of_week));
473 tokens = tokenize_string(tokens, text);
476 info->match_tokens = tokens;
484 matchmap_find_destination (
Account *base_acc, GNCImportTransInfo *info)
494 GList* tokens = TransactionGetTokens(info);
501 result = gnc_account_imap_find_account
502 (orig_acc, GNCIMPORT_DESC,
513 matchmap_store_destination (
Account *base_acc,
514 GNCImportTransInfo *trans_info,
517 g_assert (trans_info);
538 auto tokens = TransactionGetTokens(trans_info);
551 gnc_account_imap_add_account (orig_acc, GNCIMPORT_DESC, desc, dest);
553 gnc_account_imap_add_account (orig_acc, GNCIMPORT_MEMO, memo, dest);
563 gint display_threshold,
565 gint date_not_threshold,
566 double fuzzy_amount_difference)
576 auto downloaded_split_amount =
581 if (fabs(downloaded_split_amount - match_split_amount) < 1e-6)
592 else if (fabs (downloaded_split_amount - match_split_amount) <=
593 fuzzy_amount_difference)
613 auto datediff_day = llabs(match_time - download_time) / 86400;
620 if (datediff_day == 0)
625 else if (datediff_day <= date_threshold)
630 else if (datediff_day > date_not_threshold)
643 auto update_proposed = (prob < 6);
646 auto new_trans_str = gnc_get_num_action(new_trans, new_trans_fsplit);
647 if (new_trans_str && *new_trans_str)
650 auto conversion_ok =
true;
654 auto new_trans_number = strtol(new_trans_str, &endptr, 10);
657 conversion_ok = !(errno || endptr == new_trans_str);
661 auto split_number = strtol(split_str, &endptr, 10);
662 conversion_ok = !(errno || endptr == split_str);
664 if ( (conversion_ok && (split_number == new_trans_number)) ||
665 (g_strcmp0(new_trans_str, split_str) == 0) )
671 else if (strlen(new_trans_str) > 0 && strlen(split_str) > 0)
712 else if ((strncasecmp(descr,
726 if (prob < display_threshold)
734 match_info->probability = prob;
735 match_info->update_proposed = update_proposed;
736 match_info->split = split;
742 trans_info->match_list = g_list_prepend(trans_info->match_list, match_info);
749 maybe_append_string (
const char* match_string,
const char* imp_string)
751 if (!(match_string && *match_string))
752 return g_strdup(imp_string);
754 if (!(imp_string && *imp_string))
757 auto norm_match_string = g_utf8_normalize (match_string, -1, G_NORMALIZE_NFC);
758 auto norm_imp_string = g_utf8_normalize (imp_string, -1, G_NORMALIZE_NFC);
760 char *retval =
nullptr;
761 if (g_utf8_strlen (norm_imp_string, -1) > g_utf8_strlen (norm_match_string, -1) ||
762 !strstr (norm_match_string, norm_imp_string))
763 retval = g_strconcat(match_string,
"|", imp_string,
nullptr);
765 g_free (norm_match_string);
766 g_free (norm_imp_string);
775 update_desc_and_notes (
const GNCImportTransInfo* trans_info)
780 if (trans_info->append_text)
782 auto match_trans = selected_match->trans;
807 process_reconcile(
Account *base_acc,
808 GNCImportTransInfo *trans_info,
823 auto online_id = gnc_import_get_split_online_id(trans_info->first_split);
824 if (online_id && *online_id)
825 gnc_import_set_split_online_id(selected_match->split, online_id);
834 matchmap_store_destination(base_acc, trans_info,
true);
841 trans_info->trans =
nullptr;
848 GNCImportTransInfo *trans_info)
850 g_assert (trans_info);
880 PWARN(
"Missing exchange rate while adding transaction '%s', will assume rate of 1",
893 case GNCImport_UPDATE:
900 PWARN(
"No matching transaction to be cleared was chosen. Imported transaction will be ignored.");
907 PERR(
"The split I am trying to update and reconcile is nullptr, shouldn't happen!");
947 PWARN(
"Updated transaction '%s', but not other split.",
953 if (fs_memo && *fs_memo)
956 update_desc_and_notes(trans_info);
961 process_reconcile (base_acc, trans_info, selected_match);
965 case GNCImport_CLEAR:
972 PWARN(
"No matching translaction to be cleared was chosen. Imported transaction will be ignored.");
979 PERR(
"The split I am trying to reconcile is nullptr, shouldn't happen!");
983 process_reconcile(base_acc, trans_info, selected_match);
988 DEBUG(
"Invalid GNCImportAction for this imported transaction.");
996 hash_account_online_ids (
Account *account)
998 auto acct_hash = g_hash_table_new_full
999 (g_str_hash, g_str_equal, g_free,
nullptr);
1000 for (
auto split : xaccAccountGetSplits (account))
1002 auto id = gnc_import_get_split_online_id (split);
1004 g_hash_table_insert (acct_hash, (
void*)
id, GINT_TO_POINTER (1));
1016 g_assert(source_split);
1018 auto source_online_id = gnc_import_get_split_online_id (source_split);
1021 if (!source_online_id)
1028 auto online_id_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (acct_id_hash, dest_acct));
1030 if (!online_id_hash)
1032 online_id_hash = hash_account_online_ids (dest_acct);
1033 g_hash_table_insert (acct_id_hash, dest_acct, online_id_hash);
1036 auto online_id_exists = g_hash_table_contains (online_id_hash, source_online_id);
1037 g_free (source_online_id);
1038 return online_id_exists;
1048 static void trans_info_calculate_dest_amount (GNCImportTransInfo *info)
1051 if (!info->lsplit_amount_selected_manually)
1052 info->lsplit_amount = {0, 1};
1063 info->lsplit_amount = info->lsplit_value;
1064 else if (info->lsplit_amount_selected_manually &&
1083 GNCImportTransInfo *
1088 auto t_info = g_new0(GNCImportTransInfo, 1);
1090 t_info->trans = trans;
1094 t_info->first_split = split;
1099 matchmap_find_destination (base_acc, t_info),
1107 static gint compare_probability (gconstpointer a,
1120 GNCImportSettings *settings)
1122 g_assert (trans_info);
1124 if (trans_info->match_list)
1126 trans_info->match_list = g_list_sort(trans_info->match_list,
1127 compare_probability);
1128 auto best_match =
static_cast<GNCImportMatchInfo*
>(g_list_nth_data(trans_info->match_list, 0));
1134 best_match->update_proposed)
1135 trans_info->action = GNCImport_UPDATE;
1137 trans_info->action = GNCImport_CLEAR;
1139 else if (!best_match ||
1141 trans_info->action = GNCImport_ADD;
1143 trans_info->action = GNCImport_SKIP;
1145 trans_info->action = GNCImport_UPDATE;
1147 trans_info->action = GNCImport_ADD;
1150 trans_info->action = GNCImport_ADD;
1153 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...