GnuCash  5.6-150-g038405b370+
gnc-imp-settings-csv-tx.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2  * gnc-imp-settings-csv-tx.cpp -- Trans CSV Import Settings *
3  * *
4  * Copyright (C) 2014 Robert Fewell *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22 \********************************************************************/
29 #include "gnc-imp-settings-csv.hpp"
31 #include <algorithm>
32 #include <memory>
33 #include <sstream>
34 #include <string>
35 #include <vector>
36 #include <cstdint>
37 
38 #include <config.h>
39 
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42 
43 #include "Account.h"
44 #include "gnc-state.h"
45 #include "gnc-ui-util.h"
46 
47 constexpr auto group_prefix = "Import csv,transaction - ";
48 
49 #define CSV_COL_TYPES "ColumnTypes"
50 
51 #define CSV_ACCOUNT "BaseAccount"
52 #define CSV_ACCOUNT_GUID "BaseAccountGuid"
53 #define CSV_MULTI_SPLIT "MultiSplit"
54 
55 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
56 
57 preset_vec_trans presets_trans;
58 
59 static std::shared_ptr<CsvTransImpSettings> create_int_no_preset(void)
60 {
61  auto preset = std::make_shared<CsvTransImpSettings>();
62  preset->m_name = get_no_settings();
63 
64  return preset;
65 }
66 
67 static std::shared_ptr<CsvTransImpSettings> create_int_gnc_exp_preset(void)
68 {
69  auto preset = std::make_shared<CsvTransImpSettings>();
70  preset->m_name = get_gnc_exp();
71  preset->m_skip_start_lines = 1;
72  preset->m_multi_split = true;
73 
74  /* FIXME date and currency format should still be aligned with export format!
75  * That's currently hard to do, because the export uses whatever the user
76  * had set as global preference.
77  preset->date_active = 0;
78  preset->currency_active = 0;
79  */
80  preset->m_column_types = {
81  GncTransPropType::DATE,
82  GncTransPropType::UNIQUE_ID,
83  GncTransPropType::NUM,
84  GncTransPropType::DESCRIPTION,
85  GncTransPropType::NOTES,
86  GncTransPropType::COMMODITY,
87  GncTransPropType::VOID_REASON,
88  GncTransPropType::ACTION,
89  GncTransPropType::MEMO,
90  GncTransPropType::ACCOUNT,
91  GncTransPropType::NONE,
92  GncTransPropType::NONE,
93  GncTransPropType::AMOUNT,
94  GncTransPropType::NONE,
95  GncTransPropType::VALUE,
96  GncTransPropType::REC_STATE,
97  GncTransPropType::REC_DATE,
98  GncTransPropType::PRICE
99  };
100  return preset;
101 }
102 
103 static std::shared_ptr<CsvTransImpSettings> create_int_gnc_exp_4_preset(void)
104 {
105  auto preset = std::make_shared<CsvTransImpSettings>();
106  preset->m_name = get_gnc_exp_4();
107  preset->m_skip_start_lines = 1;
108  preset->m_multi_split = true;
109 
110  /* FIXME date and currency format should still be aligned with export format!
111  * That's currently hard to do, because the export uses whatever the user
112  * had set as global preference.
113  * preset->date_active = 0;
114  * preset->currency_active = 0;
115  */
116  preset->m_column_types = {
117  GncTransPropType::DATE,
118  GncTransPropType::UNIQUE_ID,
119  GncTransPropType::NUM,
120  GncTransPropType::DESCRIPTION,
121  GncTransPropType::NOTES,
122  GncTransPropType::COMMODITY,
123  GncTransPropType::VOID_REASON,
124  GncTransPropType::ACTION,
125  GncTransPropType::MEMO,
126  GncTransPropType::ACCOUNT,
127  GncTransPropType::NONE,
128  GncTransPropType::NONE,
129  GncTransPropType::AMOUNT,
130  GncTransPropType::REC_STATE,
131  GncTransPropType::REC_DATE,
132  GncTransPropType::PRICE
133  };
134  return preset;
135 }
136 
137 /**************************************************
138  * find
139  *
140  * find all settings entries in the state key file
141  * based on settings type.
142  **************************************************/
143 const preset_vec_trans& get_import_presets_trans (void)
144 {
145  // Search all Groups in the state key file for ones starting with prefix
146  auto preset_names = std::vector<std::string>();
147  auto keyfile = gnc_state_get_current ();
148  gsize grouplength;
149  gchar **groups = g_key_file_get_groups (keyfile, &grouplength);
150 
151  /* Start by building a sorted list of candidate presets as found in the state file */
152  for (gsize i=0; i < grouplength; i++)
153  {
154  auto group = std::string(groups[i]);
155  auto gp = std::string {group_prefix};
156  auto pos = group.find(gp);
157  if (pos == std::string::npos)
158  continue;
159 
160  preset_names.push_back(group.substr(gp.size()));
161  }
162  // string array from the state file is no longer needed now.
163  g_strfreev (groups);
164 
165  /* We want our settings to appear sorted alphabetically to the user */
166  std::sort(preset_names.begin(), preset_names.end());
167 
168  /* Now add each preset to our global list */
169  presets_trans.clear();
170 
171  /* Start with the internally generated ones */
172  presets_trans.push_back(create_int_no_preset());
173  presets_trans.push_back(create_int_gnc_exp_preset());
174  presets_trans.push_back(create_int_gnc_exp_4_preset());
175 
176  /* Then add all the ones we found in the state file */
177  for (auto preset_name : preset_names)
178  {
179  auto preset = std::make_shared<CsvTransImpSettings>();
180  preset->m_name = preset_name;
181  preset->load();
182  presets_trans.push_back(preset);
183  }
184  return presets_trans;
185 }
186 
187 /**************************************************
188  * load
189  *
190  * load the settings from a state key file
191  **************************************************/
192 bool
194 {
195  if (preset_is_reserved_name (m_name))
196  return true;
197 
198  GError *key_error = nullptr;
199  m_load_error = false;
200  auto keyfile = gnc_state_get_current ();
201  auto group = get_group_prefix() + m_name;
202 
203  // Start Loading the settings
204  m_load_error = CsvImportSettings::load(); // load the common settings
205 
206  m_multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
207  m_load_error |= handle_load_error (&key_error, group);
208 
209  gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT_GUID, &key_error);
210  if (key_char && *key_char != '\0')
211  {
212  QofBook* book = gnc_get_current_book ();
213  GncGUID guid;
214 
215  if (string_to_guid (key_char, &guid)) // find account by guid first
216  m_base_account = xaccAccountLookup (&guid, book);
217  }
218  m_load_error |= handle_load_error (&key_error, group);
219  if (key_char)
220  g_free (key_char);
221 
222  key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
223  if (key_char && *key_char != '\0')
224  {
225  if (m_base_account == nullptr)
226  {
227  m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
228 
229  if (m_base_account) // save the account as guid, introduced in version 4.5
230  {
231  gchar acct_guid[GUID_ENCODING_LENGTH + 1];
232  guid_to_string_buff (xaccAccountGetGUID (m_base_account), acct_guid);
233  g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT_GUID, acct_guid);
234  }
235  }
236  else // check to see if saved full name is the same and save if not
237  {
238  gchar *full_name = gnc_account_get_full_name (m_base_account);
239 
240  if (g_strcmp0 (key_char, full_name) != 0)
241  g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, full_name);
242  g_free (full_name);
243  }
244  }
245  m_load_error |= handle_load_error (&key_error, group);
246  if (key_char)
247  g_free (key_char);
248 
249  gsize list_len;
250  m_column_types.clear();
251  gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
252  &list_len, &key_error);
253  for (uint32_t i = 0; i < list_len; i++)
254  {
255  /* Special case a few legacy column names */
256  const char *col_type_str = col_types_str[i];
257  if (!g_strcmp0(col_type_str, "Deposit")) // -> "Amount"
258  col_type_str = gnc_csv_col_type_strs[GncTransPropType::AMOUNT];
259  if (!g_strcmp0(col_type_str, "Withdrawal")) // -> "Amount (Negated)"
260  col_type_str = gnc_csv_col_type_strs[GncTransPropType::AMOUNT_NEG];
261  if (!g_strcmp0(col_type_str, "Num")) // -> "Number"
262  col_type_str = gnc_csv_col_type_strs[GncTransPropType::NUM];
263  auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(),
264  gnc_csv_col_type_strs.end(), test_prop_type_str (col_type_str));
265  auto prop = GncTransPropType::NONE;
266  if (col_types_it != gnc_csv_col_type_strs.end())
267  {
268  /* Found a valid column type. Now check whether it is allowed
269  * in the selected mode (two-split vs multi-split) */
270  prop = sanitize_trans_prop (col_types_it->first, m_multi_split);
271  if (prop != col_types_it->first)
272  PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. "
273  "Inserting column type 'NONE' instead'.",
274  col_types_it->second, m_multi_split ? "enabled" : "disabled");
275  }
276  else
277  PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
278  col_types_str[i]);
279  m_column_types.push_back(prop);
280  }
281  if (col_types_str)
282  g_strfreev (col_types_str);
283 
284  return m_load_error;
285 }
286 
287 /**************************************************
288  * save
289  *
290  * save settings to a key file
291  **************************************************/
292 bool
294 {
295  if (preset_is_reserved_name (m_name))
296  {
297  PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
298  return true;
299  }
300 
301  if ((m_name.find('[') != std::string::npos))
302  {
303  PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str());
304  return true;
305  }
306 
307  auto keyfile = gnc_state_get_current ();
308  auto group = get_group_prefix() + m_name;
309 
310  // Drop previous saved settings with this name
311  g_key_file_remove_group (keyfile, group.c_str(), nullptr);
312 
313  // Start Saving the settings
314  bool error = CsvImportSettings::save(); // save the common settings
315 
316  if (error)
317  return error;
318 
319  g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
320 
321  if (m_base_account) // also save account guid introduced in version 4.5
322  {
323  gchar acct_guid[GUID_ENCODING_LENGTH + 1];
324  guid_to_string_buff (xaccAccountGetGUID (m_base_account), acct_guid);
325  g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT_GUID, acct_guid);
326 
327  gchar *full_name = gnc_account_get_full_name (m_base_account);
328  g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, full_name);
329  g_free (full_name);
330  }
331 
332  std::vector<const char*> col_types_str;
333  for (auto col_type : m_column_types)
334  col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
335 
336  if (!col_types_str.empty())
337  g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
338  col_types_str.data(), col_types_str.size());
339 
340  return error;
341 }
342 
343 void
345 {
346  if (preset_is_reserved_name (m_name))
347  return;
348 
350 }
351 
352 const char*
353 CsvTransImpSettings::get_group_prefix (void)
354 {
355  return group_prefix;
356 }
CSV Import Settings.
Functions to load, save and get gui state.
bool save(void)
Save the gathered widget properties to a key File.
utility functions for the GnuCash UI
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
CSV Import Settings.
void remove(void)
Remove the preset from the state file.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:173
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
#define xaccAccountGetGUID(X)
Definition: Account.h:248
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3255
Account handling public routines.
bool preset_is_reserved_name(const std::string &name)
Check whether name can be used as a preset name.
Functor to check if the above map has an element of which the value equals name.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
Account * gnc_account_lookup_by_full_name(const Account *any_acc, const gchar *name)
The gnc_account_lookup_full_name() subroutine works like gnc_account_lookup_by_name, but uses fully-qualified names using the given separator.
Definition: Account.cpp:3113
bool load(void)
Load the widget properties from a key File.
const preset_vec_trans & get_import_presets_trans(void)
Creates a vector of CsvTransImpSettings which combines.
void remove(void)
Remove the preset from the state file.
bool save(void)
Save the gathered widget properties to a key File.
The type used to store guids in C.
Definition: guid.h:75
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2032
bool load(void)
Load the widget properties from a key File.