GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
csv-account-import.c
1 /*******************************************************************\
2  * csv-account-import.c -- Account importing from file *
3  * *
4  * Copyright (C) 2012 Robert Fewell *
5  * *
6  * Based on code from bi_import written by Sebastian Held and *
7  * Mike Evans. *
8  * *
9  * This program is free software; you can redistribute it and/or *
10  * modify it under the terms of the GNU General Public License as *
11  * published by the Free Software Foundation; either version 2 of *
12  * the License, or (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License*
20  * along with this program; if not, contact: *
21  * *
22  * Free Software Foundation Voice: +1-617-542-5942 *
23  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
24  * Boston, MA 02110-1301, USA [email protected] *
25 \********************************************************************/
26 
27 #include "config.h"
28 
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32 
33 #include "gnc-ui-util.h"
34 #include <regex.h>
35 #include "Account.h"
36 #include "gnc-component-manager.h"
37 #include "csv-account-import.h"
38 
39 /* This static indicates the debugging module that this .o belongs to. */
40 static QofLogModule log_module = GNC_MOD_ASSISTANT;
41 
42 /* This helper function takes a regexp match and fills the model */
43 static void
44 fill_model_with_match(GMatchInfo *match_info,
45  const gchar *match_name,
46  GtkListStore *store,
47  GtkTreeIter *iterptr,
48  gint column)
49 {
50  gchar *temp;
51 
52  if (!match_info || !match_name)
53  return;
54 
55  temp = g_match_info_fetch_named (match_info, match_name);
56  if (temp)
57  {
58  g_strstrip (temp);
59  if (g_str_has_prefix (temp, "\""))
60  {
61  if (strlen (temp) >= 2)
62  {
63  gchar *toptail = g_strndup (temp + 1, strlen (temp)-2);
64  gchar **parts = g_strsplit (toptail, "\"\"", -1);
65  temp = g_strjoinv ("\"", parts);
66  g_strfreev (parts);
67  g_free (toptail);
68  }
69  }
70  gtk_list_store_set (store, iterptr, column, temp, -1);
71  g_free (temp);
72  }
73 }
74 
75 /*******************************************************
76  * csv_import_read_file
77  *
78  * Parse the file for a correctly formatted file
79  *******************************************************/
80 csv_import_result
81 csv_import_read_file (const gchar *filename, const gchar *parser_regexp,
82  GtkListStore *store, guint max_rows)
83 {
84  gchar *locale_cont, *contents;
85  GMatchInfo *match_info = NULL;
86  GRegex *regexpat = NULL;
87  GError *err;
88  gint row = 0;
89  gboolean match_found = FALSE;
90 
91  // model
92  GtkTreeIter iter;
93 
94  if (!g_file_get_contents (filename, &locale_cont, NULL, NULL))
95  {
96  //gnc_error_dialog( 0, _("File %s cannot be opened."), filename );
97  return RESULT_OPEN_FAILED;
98  }
99 
100  contents = g_locale_to_utf8 (locale_cont, -1, NULL, NULL, NULL);
101  g_free (locale_cont);
102 
103  // compile the regular expression and check for errors
104  err = NULL;
105  regexpat =
106  g_regex_new (parser_regexp, G_REGEX_OPTIMIZE, 0, &err);
107  if (err != NULL)
108  {
109  GtkWidget *dialog;
110  gchar *errmsg;
111 
112  errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
113  parser_regexp, err->message);
114  g_error_free (err);
115 
116  dialog = gtk_message_dialog_new (NULL,
117  GTK_DIALOG_MODAL,
118  GTK_MESSAGE_ERROR,
119  GTK_BUTTONS_OK, "%s", errmsg);
120  gtk_dialog_run (GTK_DIALOG (dialog));
121  gtk_widget_destroy (dialog);
122  g_free (errmsg);
123  g_free (contents);
124 
125  return RESULT_ERROR_IN_REGEXP;
126  }
127 
128  g_regex_match (regexpat, contents, 0, &match_info);
129  while (g_match_info_matches (match_info))
130  {
131  match_found = TRUE;
132  // fill in the values
133  gtk_list_store_append (store, &iter);
134  fill_model_with_match (match_info, "type", store, &iter, TYPE);
135  fill_model_with_match (match_info, "full_name", store, &iter, FULL_NAME);
136  fill_model_with_match (match_info, "name", store, &iter, NAME);
137  fill_model_with_match (match_info, "code", store, &iter, CODE);
138  fill_model_with_match (match_info, "description", store, &iter, DESCRIPTION);
139  fill_model_with_match (match_info, "color", store, &iter, COLOR);
140  fill_model_with_match (match_info, "notes", store, &iter, NOTES);
141  fill_model_with_match (match_info, "commoditym", store, &iter, COMMODITYM);
142  fill_model_with_match (match_info, "commodityn", store, &iter, COMMODITYN);
143  fill_model_with_match (match_info, "hidden", store, &iter, HIDDEN);
144  fill_model_with_match (match_info, "tax", store, &iter, TAX);
145  fill_model_with_match (match_info, "place_holder", store, &iter, PLACE_HOLDER);
146  gtk_list_store_set (store, &iter, ROW_COLOR, NULL, -1);
147 
148  row++;
149  if (row == max_rows)
150  break;
151  g_match_info_next (match_info, &err);
152  }
153 
154  g_match_info_free (match_info);
155  g_regex_unref (regexpat);
156  g_free (contents);
157 
158  if (err != NULL)
159  {
160  g_printerr ("Error while matching: %s\n", err->message);
161  g_error_free (err);
162  }
163 
164  if (match_found == TRUE)
165  return MATCH_FOUND;
166  else
167  return RESULT_OK;
168 }
169 
170 
171 /*******************************************************
172  * csv_account_import
173  *
174  * Parse the liststore for account updates
175  *******************************************************/
176 void
177 csv_account_import (CsvImportInfo *info)
178 {
179  QofBook *book;
180  Account *acc, *parent, *root;
181  gboolean valid;
182  GdkColor testcolor;
183  GtkTreeIter iter;
184  gchar *type, *full_name, *name, *code, *description, *color;
185  gchar *notes, *commoditym, *commodityn, *hidden, *tax, *place_holder;
186  int row;
187 
188  ENTER("");
189  book = gnc_get_current_book();
190  root = gnc_book_get_root_account (book);
191 
192  info->num_new = 0;
193  info->num_updates = 0;
194 
195  /* Move to the first valid entry in store */
196  row = info->header_rows;
197  valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, row );
198  while (valid)
199  {
200  /* Walk through the list, reading each row */
201  gtk_tree_model_get (GTK_TREE_MODEL (info->store), &iter,
202  TYPE, &type,
203  FULL_NAME, &full_name,
204  NAME, &name,
205  CODE, &code,
206  DESCRIPTION, &description,
207  COLOR, &color,
208  NOTES, &notes,
209  COMMODITYM, &commoditym,
210  COMMODITYN, &commodityn,
211  HIDDEN, &hidden,
212  TAX, &tax,
213  PLACE_HOLDER, &place_holder, -1);
214 
215  /* See if we can find the account by full name */
216  acc = gnc_account_lookup_by_full_name (root, full_name);
217 
218  DEBUG("Row is %u and full name is %s", row, full_name);
219  if (acc == NULL)
220  {
221  /* Account does not exist, Lets try and add it */
222  if (g_strrstr (full_name, name) != NULL)
223  {
224  gint string_position;
225  gnc_commodity *commodity;
227  gchar *full_parent;
228 
229  /* Get full name of parent account, allow for separator */
230  string_position = strlen (full_name) - strlen (name) - 1;
231 
232  if (string_position == -1)
233  full_parent = g_strdup (full_name);
234  else
235  full_parent = g_strndup (full_name, string_position);
236 
237  parent = gnc_account_lookup_by_full_name (root, full_parent);
238  g_free (full_parent);
239 
240  if (parent == NULL && string_position != -1)
241  {
242  gchar *text = g_strdup_printf (gettext("Row %u, path to account %s not found, added as top level\n"), row + 1, name);
243  info->error = g_strconcat (info->error, text, NULL);
244  g_free (text);
245  PINFO("Unable to import Row %u for account %s, path not found!", row, name);
246  }
247 
248  if (parent == NULL)
249  parent = root;
250 
251  /* Do we have a valid commodity */
252  table = gnc_commodity_table_get_table (book);
253  commodity = gnc_commodity_table_lookup (table, commodityn, commoditym);
254 
255  if (commodity)
256  {
257  DEBUG("We have a valid commodity and will add account %s", full_name);
258  info->num_new = info->num_new + 1;
259  gnc_suspend_gui_refresh ();
260  acc = xaccMallocAccount (book);
261  xaccAccountBeginEdit (acc);
262  xaccAccountSetName (acc, name);
264 
265  if (!g_strcmp0 (notes, "") == 0)
266  xaccAccountSetNotes (acc, notes);
267  if (!g_strcmp0 (description, "") == 0)
268  xaccAccountSetDescription (acc, description);
269  if (!g_strcmp0 (code, "") == 0)
270  xaccAccountSetCode (acc, code);
271 
272  if (!g_strcmp0 (color, "") == 0)
273  {
274  if (gdk_color_parse (color, &testcolor))
275  xaccAccountSetColor (acc, color);
276  }
277 
278  if (g_strcmp0 (hidden, "T") == 0)
279  xaccAccountSetHidden (acc, TRUE);
280  if (g_strcmp0 (place_holder, "T") == 0)
281  xaccAccountSetPlaceholder (acc, TRUE);
282 
283  xaccAccountSetCommodity (acc, commodity);
284  xaccAccountBeginEdit (parent);
285  gnc_account_append_child (parent, acc);
286  xaccAccountCommitEdit (parent);
287  xaccAccountCommitEdit (acc);
288  gnc_resume_gui_refresh ();
289  }
290  else
291  {
292  gchar *err_string = g_strdup_printf (gettext("Row %u, commodity %s / %s not found\n"), row + 1,
293  commoditym, commodityn);
294  info->error = g_strconcat (info->error, err_string, NULL);
295  g_free (err_string);
296  PINFO("Unable to import Row %u for account %s, commodity!", row, full_name);
297  }
298  }
299  else
300  {
301  gchar *err_string = g_strdup_printf (gettext("Row %u, account %s not in %s\n"), row + 1, name, full_name);
302  info->error = g_strconcat (info->error, err_string, NULL);
303  g_free (err_string);
304  PINFO("Unable to import Row %u for account %s, name!", row, full_name);
305  }
306  }
307  else
308  {
309  /* Lets try and update the color, notes, description, code entries */
310  DEBUG("Existing account, will try and update account %s", full_name);
311  info->num_updates = info->num_updates + 1;
312  if (!g_strcmp0 (color, "") == 0)
313  {
314  if (gdk_color_parse (color, &testcolor))
315  xaccAccountSetColor (acc, color);
316  }
317 
318  if (!g_strcmp0 (notes, "") == 0)
319  xaccAccountSetNotes (acc, notes);
320 
321  if (!g_strcmp0 (description, "") == 0)
322  xaccAccountSetDescription (acc, description);
323 
324  if (!g_strcmp0 (code, "") == 0)
325  xaccAccountSetCode (acc, code);
326  }
327  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (info->store), &iter);
328  row++;
329 
330  /* free resources */
331  g_free (type);
332  g_free (full_name);
333  g_free (name);
334  g_free (code);
335  g_free (description);
336  g_free (color);
337  g_free (notes);
338  g_free (commoditym);
339  g_free (commodityn);
340  g_free (hidden);
341  g_free (tax);
342  g_free (place_holder);
343  }
344  LEAVE("");
345 }
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Definition: Account.c:2208
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
void gnc_account_append_child(Account *new_parent, Account *child)
Definition: Account.c:2525
void xaccAccountSetNotes(Account *acc, const char *str)
Definition: Account.c:2368
utility functions for the GnuCash UI
#define PINFO(format, args...)
Definition: qoflog.h:249
#define DEBUG(format, args...)
Definition: qoflog.h:255
void xaccAccountSetCode(Account *acc, const char *str)
Definition: Account.c:2249
#define ENTER(format, args...)
Definition: qoflog.h:261
GNCAccountType xaccAccountStringToEnum(const char *str)
Definition: Account.c:4098
Account handling public routines.
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Definition: Account.c:3923
void xaccAccountSetColor(Account *acc, const char *str)
Definition: Account.c:2287
Account * gnc_account_lookup_by_full_name(const Account *any_acc, const gchar *name)
Definition: Account.c:2915
void xaccAccountSetHidden(Account *acc, gboolean val)
Definition: Account.c:3970
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
#define LEAVE(format, args...)
Definition: qoflog.h:271
Account * xaccMallocAccount(QofBook *book)
Definition: Account.c:1083
void xaccAccountSetDescription(Account *acc, const char *str)
Definition: Account.c:2268
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
void xaccAccountSetName(Account *acc, const char *str)
Definition: Account.c:2229
const gchar * QofLogModule
Definition: qofid.h:89
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Definition: Account.c:2389