GnuCash c935c2f+
Loading...
Searching...
No Matches
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 gnu@gnu.org *
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-glib-utils.h"
34#include "gnc-ui-util.h"
35#include <regex.h>
36#include "Account.h"
37#include "gnc-component-manager.h"
38#include "csv-account-import.h"
39
40/* This static indicates the debugging module that this .o belongs to. */
41static QofLogModule log_module = GNC_MOD_ASSISTANT;
42
43/* This helper function takes a regexp match and fills the model */
44static void
45fill_model_with_match(GMatchInfo *match_info,
46 const gchar *match_name,
47 GtkListStore *store,
48 GtkTreeIter *iterptr,
49 gint column)
50{
51 gchar *temp;
52
53 if (!match_info || !match_name)
54 return;
55
56 temp = g_match_info_fetch_named (match_info, match_name);
57 if (temp)
58 {
59 g_strstrip (temp);
60 if (g_str_has_prefix (temp, "\""))
61 {
62 if (strlen (temp) >= 2)
63 {
64 gchar *toptail = g_strndup (temp + 1, strlen (temp)-2);
65 gchar **parts = g_strsplit (toptail, "\"\"", -1);
66 temp = g_strjoinv ("\"", parts);
67 g_strfreev (parts);
68 g_free (toptail);
69 }
70 }
71 gtk_list_store_set (store, iterptr, column, temp, -1);
72 g_free (temp);
73 }
74}
75
76/*******************************************************
77 * csv_import_read_file
78 *
79 * Parse the file for a correctly formatted file
80 *******************************************************/
81csv_import_result
82csv_import_read_file (GtkWindow *window, const gchar *filename,
83 const gchar *parser_regexp,
84 GtkListStore *store, guint max_rows)
85{
86 gchar *locale_cont, *contents;
87 GMatchInfo *match_info = NULL;
88 GRegex *regexpat = NULL;
89 GError *err;
90 gint row = 0;
91 gboolean match_found = FALSE;
92
93 // model
94 GtkTreeIter iter;
95
96 if (!g_file_get_contents (filename, &locale_cont, NULL, NULL))
97 {
98 //gnc_error_dialog (NULL, _("File %s cannot be opened."), filename );
99 return RESULT_OPEN_FAILED;
100 }
101
102 // if the contents don't conform to UTF-8, try a default charcter set
103 // conversion based on locale
104 if (g_utf8_validate(locale_cont, -1, NULL))
105 {
106 contents = locale_cont;
107 }
108 else
109 {
110 contents = g_locale_to_utf8 (locale_cont, -1, NULL, NULL, NULL);
111 g_free (locale_cont);
112 }
113
114 // Remove the potential XML-prohibited codepoints from the UTF-8 compliant content
115 gnc_utf8_strip_invalid(contents);
116
117
118 // compile the regular expression and check for errors
119 err = NULL;
120 regexpat =
121 g_regex_new (parser_regexp, G_REGEX_OPTIMIZE, 0, &err);
122 if (err != NULL)
123 {
124 GtkWidget *dialog;
125 gchar *errmsg;
126
127 errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
128 parser_regexp, err->message);
129 g_error_free (err);
130
131 dialog = gtk_message_dialog_new (window,
132 GTK_DIALOG_MODAL,
133 GTK_MESSAGE_ERROR,
134 GTK_BUTTONS_OK, "%s", errmsg);
135 gtk_dialog_run (GTK_DIALOG (dialog));
136 gtk_widget_destroy (dialog);
137 g_free (errmsg);
138 g_free (contents);
139
140 return RESULT_ERROR_IN_REGEXP;
141 }
142
143 g_regex_match (regexpat, contents, 0, &match_info);
144 while (g_match_info_matches (match_info))
145 {
146 // fill in the values, pattern match names must match those defined in
147 // regular expression
148 gtk_list_store_append (store, &iter);
149 fill_model_with_match (match_info, "type", store, &iter, TYPE);
150 fill_model_with_match (match_info, "full_name", store, &iter, FULL_NAME);
151 fill_model_with_match (match_info, "name", store, &iter, NAME);
152 fill_model_with_match (match_info, "code", store, &iter, CODE);
153 fill_model_with_match (match_info, "description", store, &iter, DESCRIPTION);
154 fill_model_with_match (match_info, "color", store, &iter, COLOR);
155 fill_model_with_match (match_info, "notes", store, &iter, NOTES);
156 fill_model_with_match (match_info, "symbol", store, &iter, SYMBOL);
157 fill_model_with_match (match_info, "namespace", store, &iter, NAMESPACE);
158 fill_model_with_match (match_info, "hidden", store, &iter, HIDDEN);
159 fill_model_with_match (match_info, "tax", store, &iter, TAX);
160 fill_model_with_match (match_info, "placeholder", store, &iter, PLACE_HOLDER);
161 gtk_list_store_set (store, &iter, ROW_COLOR, NULL, -1);
162
163 if (row == 0)
164 {
165 gchar *str_type;
166 gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, TYPE, &str_type, -1);
167
168 if (g_strcmp0 (_("Type"), str_type) == 0)
169 match_found = TRUE;
170 g_free (str_type);
171 }
172
173 row++;
174 if (row == max_rows)
175 break;
176 g_match_info_next (match_info, &err);
177 }
178
179 g_match_info_free (match_info);
180 g_regex_unref (regexpat);
181 g_free (contents);
182
183 if (err != NULL)
184 {
185 g_printerr ("Error while matching: %s\n", err->message);
186 g_error_free (err);
187 }
188
189 if (match_found == TRUE)
190 return MATCH_FOUND;
191 else
192 return RESULT_OK;
193}
194
195
196/*******************************************************
197 * csv_account_import
198 *
199 * Parse the liststore for account updates
200 *******************************************************/
201void
202csv_account_import (CsvImportInfo *info)
203{
204 QofBook *book;
205 Account *acc, *parent, *root;
206 gboolean valid;
207 GdkRGBA testcolor;
208 GtkTreeIter iter;
209 gchar *type, *full_name, *name, *code, *description, *color;
210 gchar *notes, *symbol, *namespace, *hidden, *tax, *place_holder;
211 int row;
212
213 ENTER("");
214 book = gnc_get_current_book();
215 root = gnc_book_get_root_account (book);
216
217 info->num_new = 0;
218 info->num_updates = 0;
219
220 /* Move to the first valid entry in store */
221 row = info->header_rows;
222 valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, row );
223 while (valid)
224 {
225 /* Walk through the list, reading each row */
226 gtk_tree_model_get (GTK_TREE_MODEL (info->store), &iter,
227 TYPE, &type,
228 FULL_NAME, &full_name,
229 NAME, &name,
230 CODE, &code,
231 DESCRIPTION, &description,
232 COLOR, &color,
233 NOTES, &notes,
234 SYMBOL, &symbol,
235 NAMESPACE, &namespace,
236 HIDDEN, &hidden,
237 TAX, &tax,
238 PLACE_HOLDER, &place_holder, -1);
239
240 /* See if we can find the account by full name */
241 acc = gnc_account_lookup_by_full_name (root, full_name);
242
243 DEBUG("Row is %u and full name is %s", row, full_name);
244 if (acc == NULL)
245 {
246 /* Account does not exist, Lets try and add it */
247 if (g_strrstr (full_name, name) != NULL)
248 {
249 gint string_position;
250 gnc_commodity *commodity;
251 gnc_commodity_table *table;
252 gchar *full_parent;
253
254 /* Get full name of parent account, allow for separator */
255 string_position = strlen (full_name) - strlen (name) - 1;
256
257 if (string_position == -1)
258 full_parent = g_strdup (full_name);
259 else
260 full_parent = g_strndup (full_name, string_position);
261
262 parent = gnc_account_lookup_by_full_name (root, full_parent);
263 g_free (full_parent);
264
265 if (parent == NULL && string_position != -1)
266 {
267 gchar *text = g_strdup_printf (gettext("Row %u, path to account %s not found, added as top level\n"), row + 1, name);
268 info->error = g_strconcat (info->error, text, NULL);
269 g_free (text);
270 PINFO("Unable to import Row %u for account %s, path not found!", row, name);
271 }
272
273 if (parent == NULL)
274 parent = root;
275
276 /* Do we have a valid commodity */
278 commodity = gnc_commodity_table_lookup (table, namespace, symbol);
279
280 if (commodity)
281 {
282 DEBUG("We have a valid commodity and will add account %s", full_name);
283 info->num_new = info->num_new + 1;
284 gnc_suspend_gui_refresh ();
285 acc = xaccMallocAccount (book);
287 xaccAccountSetName (acc, name);
289
290 if (g_strcmp0 (notes, "") != 0)
291 xaccAccountSetNotes (acc, notes);
292 if (g_strcmp0 (description, "") != 0)
293 xaccAccountSetDescription (acc, description);
294 if (g_strcmp0 (code, "") != 0)
295 xaccAccountSetCode (acc, code);
296
297 if (g_strcmp0 (color, "") != 0)
298 {
299 if (gdk_rgba_parse (&testcolor, color))
300 xaccAccountSetColor (acc, color);
301 else
302 xaccAccountSetColor (acc, "");
303 }
304
305 if (g_strcmp0 (hidden, "T") == 0)
306 xaccAccountSetHidden (acc, TRUE);
307 if (g_strcmp0 (place_holder, "T") == 0)
308 xaccAccountSetPlaceholder (acc, TRUE);
309
310 xaccAccountSetCommodity (acc, commodity);
311 xaccAccountBeginEdit (parent);
312 gnc_account_append_child (parent, acc);
313 xaccAccountCommitEdit (parent);
315 gnc_resume_gui_refresh ();
316 }
317 else
318 {
319 gchar *err_string = g_strdup_printf (gettext("Row %u, commodity %s / %s not found\n"), row + 1,
320 symbol, namespace);
321 info->error = g_strconcat (info->error, err_string, NULL);
322 g_free (err_string);
323 PINFO("Unable to import Row %u for account %s, commodity!", row, full_name);
324 }
325 }
326 else
327 {
328 gchar *err_string = g_strdup_printf (gettext("Row %u, account %s not in %s\n"), row + 1, name, full_name);
329 info->error = g_strconcat (info->error, err_string, NULL);
330 g_free (err_string);
331 PINFO("Unable to import Row %u for account %s, name!", row, full_name);
332 }
333 }
334 else
335 {
336 /* Lets try and update the color, notes, description, code entries */
337 DEBUG("Existing account, will try and update account %s", full_name);
338 info->num_updates = info->num_updates + 1;
339 if (g_strcmp0 (color, "") != 0)
340 {
341 if (gdk_rgba_parse (&testcolor, color))
342 xaccAccountSetColor (acc, color);
343 else
344 xaccAccountSetColor (acc, "");
345 }
346
347 if (g_strcmp0 (notes, "") != 0)
348 xaccAccountSetNotes (acc, notes);
349
350 if (g_strcmp0 (description, "") != 0)
351 xaccAccountSetDescription (acc, description);
352
353 if (g_strcmp0 (code, "") != 0)
354 xaccAccountSetCode (acc, code);
355 }
356 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (info->store), &iter);
357 row++;
358
359 /* free resources */
360 g_free (type);
361 g_free (full_name);
362 g_free (name);
363 g_free (code);
364 g_free (description);
365 g_free (color);
366 g_free (notes);
367 g_free (symbol);
368 g_free (namespace);
369 g_free (hidden);
370 g_free (tax);
371 g_free (place_holder);
372 }
373 LEAVE("");
374}
Account handling public routines.
GLib helper routines.
utility functions for the GnuCash UI
void xaccAccountSetColor(Account *acc, const char *str)
Set the account's Color.
Definition Account.cpp:2613
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship,...
Definition Account.cpp:2836
void xaccAccountSetName(Account *acc, const char *str)
Set the account's name.
Definition Account.cpp:2458
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account's description.
Definition Account.cpp:2497
void xaccAccountSetNotes(Account *acc, const char *str)
Set the account's notes.
Definition Account.cpp:2655
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account's type.
Definition Account.cpp:2437
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition Account.cpp:1270
void xaccAccountSetCode(Account *acc, const char *str)
Set the account's accounting code.
Definition Account.cpp:2478
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account's commodity.
Definition Account.cpp:2678
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
GNCAccountType xaccAccountStringToEnum(const char *str)
Conversion routines for the account types to/from strings that are used in persistent storage,...
Definition Account.cpp:4318
void xaccAccountSetHidden(Account *acc, gboolean val)
Set the "hidden" flag for an account.
Definition Account.cpp:4196
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition Account.cpp:4125
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,...
Definition Account.cpp:3163
void gnc_utf8_strip_invalid(gchar *str)
Strip any non-UTF-8 characters from a string.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
STRUCTS.
QofBook reference.
Definition qofbook-p.hpp:47