GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-file-aqb-import.c
1 /*
2  * gnc-file-aqb-import.c --
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, contact:
16  *
17  * Free Software Foundation Voice: +1-617-542-5942
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
19  * Boston, MA 02110-1301, USA [email protected]
20  */
21 
33 #include "config.h"
34 
35 #include <glib/gi18n.h>
36 #include <glib/gstdio.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 
40 #include "gnc-ab-utils.h"
41 
42 #ifdef AQBANKING_VERSION_5_PLUS
43 # include <gwenhywfar/syncio_file.h>
44 # include <gwenhywfar/syncio_buffered.h>
45 typedef GWEN_SYNCIO GWEN_IO_LAYER;
46 #else
47 # include <gwenhywfar/io_file.h>
48 # include <gwenhywfar/io_buffered.h>
49 # include <gwenhywfar/iomanager.h>
50 #endif
51 
52 #include "dialog-ab-trans.h"
53 #include "dialog-utils.h"
54 #include "gnc-file.h"
55 #include "gnc-file-aqb-import.h"
56 #include "gnc-gwen-gui.h"
57 #include "gnc-ui.h"
58 #include "gnc-ui-util.h"
59 #include "import-account-matcher.h"
60 #include "import-main-matcher.h"
61 
62 /* This static indicates the debugging module that this .o belongs to. */
63 static QofLogModule log_module = GNC_MOD_IMPORT;
64 
65 void
66 gnc_file_aqbanking_import(const gchar *aqbanking_importername,
67  const gchar *aqbanking_profilename,
68  gboolean execute_transactions)
69 {
70  gchar *default_dir;
71  gchar *selected_filename = NULL;
72  gint dtaus_fd = -1;
73  AB_BANKING *api = NULL;
74  gboolean online = FALSE;
75  GncGWENGui *gui = NULL;
76  AB_IMEXPORTER *importer;
77  GWEN_DB_NODE *db_profiles = NULL;
78  GWEN_DB_NODE *db_profile;
79  AB_IMEXPORTER_CONTEXT *context = NULL;
80  GWEN_IO_LAYER *io = NULL;
81  GncABImExContextImport *ieci = NULL;
82  AB_JOB_LIST2 *job_list = NULL;
83  AB_JOB_LIST2_ITERATOR *jit;
84  AB_JOB *job;
85  AB_JOB_STATUS job_status;
86  gboolean successful = TRUE;
87  int num_jobs = 0;
88  int num_jobs_failed = 0;
89  int max_failures = 5;
90  GString *errstr = NULL;
91 
92  /* Select a file */
93  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP_AQBANKING);
94  selected_filename = gnc_file_dialog(_("Select a file to import"),
95  NULL, default_dir,
96  GNC_FILE_DIALOG_IMPORT);
97  g_free(default_dir);
98 
99  if (!selected_filename)
100  goto cleanup;
101  DEBUG("filename: %s", selected_filename);
102 
103  /* Remember the directory as the default */
104  default_dir = g_path_get_dirname(selected_filename);
105  gnc_set_default_directory(GNC_PREFS_GROUP_AQBANKING, default_dir);
106  g_free(default_dir);
107 
108  dtaus_fd = g_open(selected_filename, O_RDONLY, 0);
109  if (dtaus_fd == -1)
110  {
111  DEBUG("Could not open file %s", selected_filename);
112  goto cleanup;
113  }
114 
115  /* Get the API */
116  api = gnc_AB_BANKING_new();
117  if (!api)
118  {
119  g_warning("gnc_file_aqbanking_import: Couldn't get AqBanking API");
120  goto cleanup;
121  }
122  if (AB_Banking_OnlineInit(api
123 #ifdef AQBANKING_VERSION_4_EXACTLY
124  , 0
125 #endif
126  ) != 0)
127  {
128  g_warning("gnc_file_aqbanking_import: "
129  "Couldn't initialize AqBanking API");
130  goto cleanup;
131  }
132  online = TRUE;
133 
134  /* Get import module */
135  importer = AB_Banking_GetImExporter(api, aqbanking_importername);
136  if (!importer)
137  {
138  g_warning("Import module %s not found", aqbanking_importername);
139  gnc_error_dialog(NULL, "%s",
140  _("Import module for DTAUS import not found."));
141  goto cleanup;
142  }
143 
144  /* Load the import profile */
145  db_profiles = AB_Banking_GetImExporterProfiles(api, aqbanking_importername);
146 
147  /* Select profile */
148  db_profile = GWEN_DB_GetFirstGroup(db_profiles);
149  while (db_profile)
150  {
151  const gchar *name;
152 
153  name = GWEN_DB_GetCharValue(db_profile, "name", 0, 0);
154  g_return_if_fail(name);
155  if (g_ascii_strcasecmp(name, aqbanking_profilename) == 0)
156  break;
157  db_profile = GWEN_DB_GetNextGroup(db_profile);
158  }
159  if (!db_profile)
160  {
161  g_warning("Profile \"%s\" for importer \"%s\" not found",
162  aqbanking_profilename, aqbanking_importername);
163  /* For debugging: Print those available names that have been found */
164  db_profile = GWEN_DB_GetFirstGroup(db_profiles);
165  while (db_profile)
166  {
167  const char *name = GWEN_DB_GetCharValue(db_profile, "name", 0, 0);
168  g_warning("Only found profile \"%s\"\n", name ? name : "(null)");
169  db_profile = GWEN_DB_GetNextGroup(db_profile);
170  }
171  goto cleanup;
172  }
173 
174  /* Create a context to store the results */
175  context = AB_ImExporterContext_new();
176 
177  /* Wrap file in buffered gwen io */
178 #ifdef AQBANKING_VERSION_5_PLUS
179  close(dtaus_fd);
180  io = GWEN_SyncIo_File_new(selected_filename, GWEN_SyncIo_File_CreationMode_OpenExisting);
181  g_assert(io);
182  GWEN_SyncIo_AddFlags(io, GWEN_SYNCIO_FILE_FLAGS_READ);
183  {
184  /* We must explicitly call "Connect" on the GWEN_SYNCIO
185  * object. */
186  int rv = GWEN_SyncIo_Connect(io);
187  if (rv < 0)
188  {
189  g_warning("gnc_file_aqbanking_import: Failed to open file %s: %d", selected_filename, rv);
190  goto cleanup;
191  }
192  g_assert(GWEN_SyncIo_GetStatus(io) == GWEN_SyncIo_Status_Connected);
193  }
194 #else
195  io = GWEN_Io_LayerFile_new(dtaus_fd, -1);
196  g_assert(io);
197  if (GWEN_Io_Manager_RegisterLayer(io))
198  {
199  g_warning("gnc_file_aqbanking_import: Failed to wrap file");
200  goto cleanup;
201  }
202 #endif
203  dtaus_fd = -1;
204 
205  /* Run the import */
206  if (AB_ImExporter_Import(importer, context, io, db_profile
207 #ifndef AQBANKING_VERSION_5_PLUS
208  , 0
209 #endif
210  ))
211  {
212  g_warning("gnc_file_aqbanking_import: Error on import");
213  goto cleanup;
214  }
215 
216  /* Close the file */
217 #ifdef AQBANKING_VERSION_5_PLUS
218  GWEN_SyncIo_free(io);
219 #else
220  GWEN_Io_Layer_free(io);
221 #endif
222  io = NULL;
223 
224  /* Before importing the results, if this is a new book, let user specify
225  * book options, since they affect how transactions are created */
226  if (gnc_is_new_book())
227  gnc_new_book_option_display(gnc_ui_get_toplevel());
228 
229  /* Import the results */
230  ieci = gnc_ab_import_context(context, AWAIT_TRANSACTIONS,
231  execute_transactions,
232  execute_transactions ? api : NULL,
233  NULL);
234 
235  if (execute_transactions)
236  {
237  if (gnc_ab_ieci_run_matcher(ieci))
238  {
239  AB_IMEXPORTER_CONTEXT *execution_context;
240 
241  /* Extract the list of jobs */
242  job_list = gnc_ab_ieci_get_job_list(ieci);
243 
244  /* Create a context to store possible results */
245  execution_context = AB_ImExporterContext_new();
246 
247  /* Get a GUI object */
248  gui = gnc_GWEN_Gui_get(NULL);
249  if (!gui)
250  {
251  g_warning("gnc_file_aqbanking_import: Couldn't initialize Gwenhywfar GUI");
252  goto cleanup;
253  }
254 
255  /* And execute the jobs */
256  AB_Banking_ExecuteJobs(api, job_list, execution_context
257 #ifndef AQBANKING_VERSION_5_PLUS
258  , 0
259 #endif
260  );
261 
262  /* Ignore the return value of AB_Banking_ExecuteJobs(), as the job's
263  * status always describes better whether the job was actually
264  * transferred to and accepted by the bank. See also
265  * http://lists.gnucash.org/pipermail/gnucash-de/2008-September/006389.html
266  */
267 
268  /* So we must go through all jobs and check AB_Job_GetStatus(job)
269  * to give the appropriate feedback if any of the jobs didn't
270  * work. */
271 
272  jit = AB_Job_List2_First(job_list);
273  if (jit)
274  {
275 
276  job = AB_Job_List2Iterator_Data(jit);
277  while (job)
278  {
279  num_jobs += 1;
280  job_status = AB_Job_GetStatus(job);
281  if (job_status != AB_Job_StatusFinished
282  && job_status != AB_Job_StatusPending)
283  {
284  successful = FALSE;
285  num_jobs_failed += 1;
286 
287  if (num_jobs_failed <= max_failures)
288  {
289  if (num_jobs_failed == 1)
290  {
291  errstr = g_string_new("Failed jobs:\n");
292  }
293  g_string_append_printf(errstr, _("Job %d status %d - %s: %s \n")
294  , num_jobs
295  , job_status
296  , AB_Job_Status2Char(job_status)
297  , AB_Job_GetResultText(job));
298  }
299  else
300  {
301  if (num_jobs_failed == (max_failures + 1) )
302  {
303  /* indicate that additional failures exist */
304  g_string_append(errstr, _("...\n"));
305  }
306  }
307  }
308  job = AB_Job_List2Iterator_Next(jit);
309  } /* while */
310 
311  AB_Job_List2Iterator_free(jit);
312  }
313 
314  if (!successful)
315  {
316  g_warning("%s", errstr->str);
317  gnc_error_dialog(NULL,
318  _("An error occurred while executing jobs: %d of %d failed. "
319  "Please check the log window or gnucash.trace for the exact "
320  "error message.\n\n%s")
321  , num_jobs_failed, num_jobs, errstr->str);
322  }
323  else
324  {
325  if (num_jobs == 0)
326  {
327  gnc_info_dialog(NULL,
328  _("No jobs to be send.")
329  );
330  }
331  else
332  {
333  gnc_info_dialog(NULL, ngettext
334  ("The job was executed successfully, but as a precaution "
335  "please check the log window for potential errors.",
336  "All %d jobs were executed successfully, but as a precaution "
337  "please check the log window for potential errors.",
338  num_jobs), num_jobs);
339  }
340  }
341  AB_ImExporterContext_free(execution_context);
342  }
343  }
344 
345 cleanup:
346  if (io)
347  {
348 #ifdef AQBANKING_VERSION_5_PLUS
349  GWEN_SyncIo_free(io);
350 #else
351  GWEN_Io_Layer_free(io);
352 #endif
353  }
354 
355  if (job_list)
356  AB_Job_List2_FreeAll(job_list);
357  if (ieci)
358  g_free(ieci);
359  if (context)
360  AB_ImExporterContext_free(context);
361  if (db_profiles)
362  GWEN_DB_Group_free(db_profiles);
363  if (gui)
365  if (online)
366 #ifdef AQBANKING_VERSION_4_EXACTLY
367  AB_Banking_OnlineFini(api, 0);
368 #else
369  AB_Banking_OnlineFini(api);
370 #endif
371  if (api)
372  gnc_AB_BANKING_fini(api);
373  if (dtaus_fd != -1)
374  close(dtaus_fd);
375  if (selected_filename)
376  g_free(selected_filename);
377  if (errstr)
378  g_string_free(errstr, TRUE);
379 
380 }
Dialog for AqBanking transaction data.
utility functions for the GnuCash UI
AB_JOB_LIST2 * gnc_ab_ieci_get_job_list(GncABImExContextImport *ieci)
#define DEBUG(format, args...)
Definition: qoflog.h:255
Transaction matcher main window.
gboolean gnc_ab_ieci_run_matcher(GncABImExContextImport *ieci)
Generic and very flexible account matcher/picker.
GncGWENGui * gnc_GWEN_Gui_get(GtkWidget *parent)
Definition: gnc-gwen-gui.c:311
void gnc_GWEN_Gui_release(GncGWENGui *gui)
Definition: gnc-gwen-gui.c:346
void gnc_file_aqbanking_import(const gchar *aqbanking_importername, const gchar *aqbanking_profilename, gboolean execute_transactions)
AB_BANKING * gnc_AB_BANKING_new(void)
Definition: gnc-ab-utils.c:137
gint gnc_AB_BANKING_fini(AB_BANKING *api)
Definition: gnc-ab-utils.c:237
GncABImExContextImport * gnc_ab_import_context(AB_IMEXPORTER_CONTEXT *context, guint awaiting, gboolean execute_txns, AB_BANKING *api, GtkWidget *parent)
GtkWidget * gnc_ui_get_toplevel(void)
GUI callbacks for AqBanking.
AqBanking utility functions.
const gchar * QofLogModule
Definition: qofid.h:89