GnuCash  5.6-150-g038405b370+
dialog-bi-import.c
1 /*
2  * dialog-bi-import.c -- Invoice importer Core functions
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 gnu@gnu.org
20  */
21 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include <glib/gi18n.h>
35 #include <regex.h>
36 #include <glib.h>
37 #include <glib/gstdio.h>
38 
39 #include "gnc-date.h"
40 #include "gnc-ui.h"
41 #include "gnc-ui-util.h"
42 #include "gnc-gui-query.h"
43 #include "gncAddress.h"
44 #include "gncVendorP.h"
45 #include "gncVendor.h"
46 #include "gncEntry.h"
47 #include "gnc-prefs.h"
48 
49 #include "gnc-exp-parser.h"
50 
51 // query
52 #include "Query.h"
53 #include "qof.h"
54 #include "gncIDSearch.h"
55 #include "dialog-bi-import.h"
56 #include "dialog-bi-import-helper.h"
57 
58 // To open the invoices for editing
60 #include "dialog-invoice.h"
61 #include "business-gnome-utils.h"
62 
63 // this helper macro takes a regexp match and fills the model
64 #define FILL_IN_HELPER(match_name,column) \
65  temp = g_match_info_fetch_named (match_info, match_name); \
66  if (temp) \
67  { \
68  g_strstrip( temp ); \
69  gtk_list_store_set (store, &iter, column, temp, -1); \
70  g_free (temp); \
71  } else gtk_list_store_set (store, &iter, column, "", -1);
72 
73 static QofLogModule log_module = G_LOG_DOMAIN; //G_LOG_BUSINESS;
74 static char * un_escape(char *str);
75 
96 bi_import_result
97 gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
98  GtkListStore * store, guint max_rows,
99  bi_import_stats * stats)
100 {
101  // some statistics
102  bi_import_stats stats_fallback;
103  FILE *f;
104 
105  // regexp
106  char *line = NULL;
107  gchar *line_utf8 = NULL;
108  gchar *temp = NULL;
109  GMatchInfo *match_info;
110  GError *err;
111  GRegex *regexpat;
112 
113  // model
114  GtkTreeIter iter;
115 
116  f = g_fopen (filename, "rt");
117  if (!f)
118  {
119  //gnc_error_dialog (NULL, _("File %s cannot be opened."), filename );
120  return RESULT_OPEN_FAILED;
121  }
122 
123  // set up statistics
124  if (!stats)
125  stats = &stats_fallback;
126 
127  // compile the regular expression and check for errors
128  err = NULL;
129  regexpat =
130  g_regex_new (parser_regexp, G_REGEX_EXTENDED | G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES, 0, &err);
131  if (err != NULL)
132  {
133  GtkWidget *dialog;
134  gchar *errmsg;
135 
136  errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
137  parser_regexp, err->message);
138  g_error_free (err);
139  err = NULL;
140 
141  dialog = gtk_message_dialog_new (NULL,
142  GTK_DIALOG_MODAL,
143  GTK_MESSAGE_ERROR,
144  GTK_BUTTONS_OK, "%s", errmsg);
145  gtk_dialog_run (GTK_DIALOG (dialog));
146  gtk_widget_destroy (dialog);
147  g_free (errmsg);
148  errmsg = 0;
149 
150  fclose (f);
151  return RESULT_ERROR_IN_REGEXP;
152  }
153 
154  // start the import
155  stats->n_imported = 0;
156  stats->n_ignored = 0;
157  stats->ignored_lines = g_string_new (NULL);
158 #define buffer_size 1000
159  line = g_malloc0 (buffer_size);
160  while (!feof (f)
161  && ((max_rows == 0)
162  || (stats->n_imported + stats->n_ignored < max_rows)))
163  {
164  int l;
165  // read one line
166  if (!fgets (line, buffer_size, f))
167  break; // eof
168  // now strip the '\n' from the end of the line
169  l = strlen (line);
170  if ((l > 0) && (line[l - 1] == '\n'))
171  line[l - 1] = 0;
172 
173  // convert line from locale into utf8
174  line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL);
175 
176  // parse the line
177  match_info = NULL; // it seems, that in contrast to documentation, match_info is not always set -> g_match_info_free will segfault
178  if (g_regex_match (regexpat, line_utf8, 0, &match_info))
179  {
180  // match found
181  stats->n_imported++;
182 
183  // fill in the values
184  gtk_list_store_append (store, &iter);
185  FILL_IN_HELPER ("id", ID); /* FIXME: Should "id" be translated? I don't think so. */
186  FILL_IN_HELPER ("date_opened", DATE_OPENED);
187  FILL_IN_HELPER ("owner_id", OWNER_ID);
188  FILL_IN_HELPER ("billing_id", BILLING_ID);
189  FILL_IN_HELPER ("notes", NOTES);
190 
191  FILL_IN_HELPER ("date", DATE);
192  FILL_IN_HELPER ("desc", DESC);
193  FILL_IN_HELPER ("action", ACTION);
194  FILL_IN_HELPER ("account", ACCOUNT);
195  FILL_IN_HELPER ("quantity", QUANTITY);
196  FILL_IN_HELPER ("price", PRICE);
197  FILL_IN_HELPER ("disc_type", DISC_TYPE);
198  FILL_IN_HELPER ("disc_how", DISC_HOW);
199  FILL_IN_HELPER ("discount", DISCOUNT);
200  FILL_IN_HELPER ("taxable", TAXABLE);
201  FILL_IN_HELPER ("taxincluded", TAXINCLUDED);
202  FILL_IN_HELPER ("tax_table", TAX_TABLE);
203 
204  FILL_IN_HELPER ("date_posted", DATE_POSTED);
205  FILL_IN_HELPER ("due_date", DUE_DATE);
206  FILL_IN_HELPER ("account_posted", ACCOUNT_POSTED);
207  FILL_IN_HELPER ("memo_posted", MEMO_POSTED);
208  FILL_IN_HELPER ("accu_splits", ACCU_SPLITS);
209  }
210  else
211  {
212  // ignore line
213  stats->n_ignored++;
214  g_string_append (stats->ignored_lines, line_utf8);
215  g_string_append_c (stats->ignored_lines, '\n');
216  }
217 
218  g_match_info_free (match_info);
219  match_info = 0;
220  g_free (line_utf8);
221  line_utf8 = 0;
222  }
223  g_free (line);
224  line = 0;
225 
226  g_regex_unref (regexpat);
227  regexpat = 0;
228  fclose (f);
229 
230  if (stats == &stats_fallback)
231  // stats are not requested -> free the string
232  g_string_free (stats->ignored_lines, TRUE);
233 
234  return RESULT_OK;
235 }
236 
237 
268 void
269 gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_rows_ignored,
270  GString * info, gchar *type)
271 {
272  GtkTreeIter iter, first_row_of_invoice;
273  gboolean valid, row_fixed, on_first_row_of_invoice, ignore_invoice;
274  gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL, *account_posted = NULL,
275  *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
276  GString *running_id;
277  Account *acc = NULL;
278  guint dummy;
279  gint row = 1, fixed_for_invoice = 0, invoice_line = 0;
280  const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
281 
282  DEBUG("date_format_string: %s",date_format_string);
283  // allow the call to this function with only GtkListeStore* specified
284  if (!n_rows_fixed)
285  n_rows_fixed = &dummy;
286  if (!n_rows_ignored)
287  n_rows_ignored = &dummy;
288 
289  *n_rows_fixed = 0;
290  *n_rows_ignored = 0;
291 
292  // Init control variables
293  running_id = g_string_new("");
294  ignore_invoice = FALSE;
295  on_first_row_of_invoice = TRUE;
296 
297  g_string_append_printf (info, _("Validation…\n") );
298 
299  // Walk through the list, reading each row.
300  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
301  while (valid)
302  {
303  ++invoice_line;
304  row_fixed = FALSE;
305 
306  // If this is a row for a new invoice id, validate header values.
307  if (on_first_row_of_invoice)
308  {
309  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
310  ID, &id,
311  DATE_OPENED, &date_opened,
312  DATE_POSTED, &date_posted,
313  DUE_DATE, &due_date,
314  ACCOUNT_POSTED, &account_posted,
315  OWNER_ID, &owner_id, -1);
316 
317  g_string_assign (running_id, id);
318  first_row_of_invoice = iter;
319 
320  // Validate the invoice id.
321  if (strlen (id) == 0)
322  {
323  // If there was an earlier valid id, then it replaces an empty id when the next row is read at the end of the loop.
324  // So an empty id error can only happen on the first row of an import file.
325  ignore_invoice = TRUE;
326  g_string_append_printf (info,
327  _("Row %d: no invoice ID in first row of import file.\n"), row);
328  }
329 
330  // Validate customer or vendor.
331  if (strlen (owner_id) == 0)
332  {
333  ignore_invoice = TRUE;
334  g_string_append_printf (info,
335  _("Row %d, invoice %s/%u: owner not set.\n"),
336  row, id, invoice_line);
337  }
338  // Verify that customer or vendor exists.
339  if (g_ascii_strcasecmp (type, "BILL") == 0)
340  {
341  if (!gnc_search_vendor_on_id
342  (gnc_get_current_book (), owner_id))
343  {
344  // Vendor not found.
345  ignore_invoice = TRUE;
346  g_string_append_printf (info,
347  _("Row %d, invoice %s/%u: vendor %s does not exist.\n"),
348  row, id, invoice_line, owner_id);
349  }
350  }
351  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
352  {
353  if (!gnc_search_customer_on_id
354  (gnc_get_current_book (), owner_id))
355  {
356  // Customer not found.
357  ignore_invoice = TRUE;
358  g_string_append_printf (info,
359  _("Row %d, invoice %s/%u: customer %s does not exist.\n"),
360  row, id, invoice_line, owner_id);
361  }
362  }
363 
364  if (strlen(date_posted) != 0)
365  {
366  // Validate the date posted and due date.
367  if (!isDateValid(date_posted))
368  {
369  // Invalid date posted in first row of invoice, ignore the invoice
370  ignore_invoice = TRUE;
371  g_string_append_printf (info,
372  _("Row %d, invoice %s/%u: %s is not a valid posting date.\n"),
373  row, id, invoice_line, date_posted);
374 
375  // Verify the due date.
376  if (!isDateValid(due_date))
377  {
378  // Invalid due date in first row of invoice, without valid posting date to substitute.
379  g_string_append_printf (info,
380  _("Row %d, invoice %s/%u: %s is not a valid due date.\n"),
381  row, id, invoice_line, due_date);
382  }
383  }
384  else
385  {
386  // Verify the due date.
387  if (!isDateValid(due_date))
388  {
389  // Fix this by using the date posted.
390  gtk_list_store_set (store, &iter, DUE_DATE,
391  date_posted, -1);
392  row_fixed = TRUE;
393  }
394  }
395 
396  // Validate account posted.
397  // Account should exists, and should be of type A/R for invoices, A/P for bills.
399  (gnc_get_current_root_account (), account_posted);
400  if (acc == NULL)
401  {
402  ignore_invoice = TRUE;
403  g_string_append_printf (info,
404  _("Row %d, invoice %s/%u: account %s does not exist.\n"),
405  row, id, invoice_line, account_posted);
406  }
407  else
408  {
409  if (g_ascii_strcasecmp (type, "BILL") == 0)
410  {
411 
413  {
414  ignore_invoice = TRUE;
415  g_string_append_printf (info,
416  _("Row %d, invoice %s/%u: account %s is not of type Accounts Payable.\n"),
417  row, id, invoice_line, account_posted);
418  }
419  }
420  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
421  {
423  {
424  ignore_invoice = TRUE;
425  g_string_append_printf (info,
426  _("Row %d, invoice %s/%u: account %s is not of type Accounts Receivable.\n"),
427  row, id, invoice_line, account_posted);
428  }
429  }
430  }
431  }
432 
433  // Verify the date opened.
434  if(!isDateValid(date_opened))
435  {
436  // Fix this by using the current date.
437  gchar temp[20];
438  GDate date;
439  g_date_clear (&date, 1);
440  gnc_gdate_set_today (&date);
441  g_date_strftime (temp, 20, date_format_string, &date); // Create a user specified date string.
442  gtk_list_store_set (store, &iter, DATE_OPENED,
443  temp, -1);
444  row_fixed = TRUE;
445  }
446  }
447 
448  // Validate and fix item data for each row.
449 
450  // Get item data.
451  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
452  DATE, &date,
453  ACCOUNT, &account,
454  QUANTITY, &quantity,
455  PRICE, &price, -1);
456 
457 
458  // Validate the price.
459  if (strlen (price) == 0)
460  {
461  // No valid price, delete the row
462  ignore_invoice = TRUE;
463  g_string_append_printf (info,
464  _("Row %d, invoice %s/%u: price not set.\n"),
465  row, id, invoice_line);
466  }
467 
468  // Validate the account
469  acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
470  account);
471  if (acc == NULL)
472  {
473  ignore_invoice = TRUE;
474  g_string_append_printf (info,
475  _("Row %d, invoice %s/%u: account %s does not exist.\n"),
476  row, id, invoice_line, account);
477  }
478 
479  // Fix item data.
480  if (!ignore_invoice)
481  {
482 
483  // Verify the quantity.
484  if (strlen (quantity) == 0)
485  {
486  // The quantity is not set, default to 1.
487  gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
488  row_fixed = TRUE;
489  }
490 
491  // Verify the item date
492  if(!isDateValid(date))
493  {
494  // Invalid item date, replace with date opened
495  gtk_list_store_set (store, &iter, DATE,
496  date_opened, -1);
497  row_fixed = TRUE;
498  }
499 
500  }
501  if (row_fixed) ++fixed_for_invoice;
502 
503  // Get the next row and its id.
504  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
505  if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
506 
507 
508  // If the id of the next row is blank, it takes the id of the previous row.
509  if (valid && strlen(id) == 0)
510  {
511  strcpy( id, running_id->str);
512  gtk_list_store_set (store, &iter, ID, id, -1);
513  }
514 
515  // If this row was the last row of the invoice...
516  if (!valid || (valid && g_strcmp0 (id, running_id->str) != 0))
517  {
518  // If invoice should be ignored, remove all rows of this invoice.
519  if (ignore_invoice)
520  {
521  iter = first_row_of_invoice;
522  do
523  {
524  (*n_rows_ignored)++;
525  valid = gtk_list_store_remove (store, &iter);
526  if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
527  }
528  while (valid && (g_strcmp0 (id, running_id->str) == 0));
529 
530  if (running_id->len != 0)
531  {
532  g_string_append_printf (info,
533  _("Error(s) in invoice %s, all rows of this invoice ignored.\n"),
534  running_id->str);
535  }
536  else
537  {
538  g_string_append_printf (info,
539  _("Error(s) in invoice without id, all rows of this invoice ignored.\n"));
540  }
541 
542  // Fixes for ignored invoices don't count in the statistics.
543  fixed_for_invoice = 0;
544 
545  ignore_invoice = FALSE;
546  }
547 
548  on_first_row_of_invoice = TRUE;
549  (*n_rows_fixed) += fixed_for_invoice;
550  fixed_for_invoice = 0;
551  invoice_line = 0;
552 
553  g_free (id);
554  g_free (date_opened);
555  g_free (date_posted);
556  g_free (due_date);
557  g_free (account_posted);
558  g_free (owner_id);
559  }
560  else on_first_row_of_invoice = FALSE;
561 
562  g_free (date);
563  g_free (account);
564  g_free (quantity);
565  g_free (price);
566 
567  row++;
568  }
569 
570  // Deallocate strings.
571  g_string_free (running_id, TRUE);
572 
573 }
574 
575 
597 void
598 gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
599  guint * n_invoices_created,
600  guint * n_invoices_updated,
601  guint * n_rows_ignored,
602  gchar * type, gchar * open_mode, GString * info,
603  GtkWindow *parent)
604 {
605  gboolean valid, on_first_row_of_invoice, invoice_posted;
606  GtkTreeIter iter, first_row_of_invoice;
607  gchar *id = NULL, *date_opened = NULL, *owner_id = NULL, *billing_id = NULL, *notes = NULL;
608  gchar *date = NULL, *desc = NULL, *action = NULL, *account = NULL, *quantity = NULL,
609  *price = NULL, *disc_type = NULL, *disc_how = NULL, *discount = NULL, *taxable = NULL,
610  *taxincluded = NULL, *tax_table = NULL;
611  gchar *date_posted = NULL, *due_date = NULL, *account_posted = NULL, *memo_posted = NULL,
612  *accumulatesplits = NULL;
613  guint dummy;
614  GncInvoice *invoice;
615  GncEntry *entry;
616  gint day, month, year;
617  gnc_numeric value;
618  GncOwner *owner;
619  Account *acc = NULL;
620  enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO, NOT_ASKED = GTK_RESPONSE_NONE} update;
621  GtkWidget *dialog;
622  time64 today;
623  InvoiceWindow *iw;
624  GString *running_id;
625 
626  // these arguments are needed
627  g_return_if_fail (store && book);
628  // logic of this function only works for bills or invoices
629  g_return_if_fail ((g_ascii_strcasecmp (type, "INVOICE") == 0) ||
630  (g_ascii_strcasecmp (type, "BILL") == 0));
631 
632  // allow to call this function without statistics
633  if (!n_invoices_created)
634  n_invoices_created = &dummy;
635  if (!n_invoices_updated)
636  n_invoices_updated = &dummy;
637  *n_invoices_created = 0;
638  *n_invoices_updated = 0;
639 
640  invoice = NULL;
641  update = NOT_ASKED;
642  on_first_row_of_invoice = TRUE;
643  running_id = g_string_new("");
644 
645  g_string_append_printf (info, "\n%s\n", _("Processing…") );
646 
647  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
648  while (valid)
649  {
650  // Walk through the list, reading each row
651  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
652  ID, &id,
653  DATE_OPENED, &date_opened,
654  DATE_POSTED, &date_posted, // if autoposting requested
655  DUE_DATE, &due_date, // if autoposting requested
656  ACCOUNT_POSTED, &account_posted, // if autoposting requested
657  MEMO_POSTED, &memo_posted, // if autoposting requested
658  ACCU_SPLITS, &accumulatesplits, // if autoposting requested
659  OWNER_ID, &owner_id,
660  BILLING_ID, &billing_id,
661  NOTES, &notes,
662  DATE, &date,
663  DESC, &desc,
664  ACTION, &action,
665  ACCOUNT, &account,
666  QUANTITY, &quantity,
667  PRICE, &price,
668  DISC_TYPE, &disc_type,
669  DISC_HOW, &disc_how,
670  DISCOUNT, &discount,
671  TAXABLE, &taxable,
672  TAXINCLUDED, &taxincluded,
673  TAX_TABLE, &tax_table, -1);
674 
675  if (on_first_row_of_invoice)
676  {
677  g_string_assign(running_id, id);
678  first_row_of_invoice = iter;
679 
680  if (g_ascii_strcasecmp (type, "BILL") == 0)
681  invoice = gnc_search_bill_on_id (book, id);
682  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
683  invoice = gnc_search_invoice_on_id (book, id);
684  DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));
685 
686  // If the search is empty then there is no existing invoice so make a new one
687  if (invoice == NULL)
688  {
689  DEBUG( "Creating a new : %s\n", type );
690  // new invoice
691  invoice = gncInvoiceCreate (book);
692  /* Protect against thrashing the DB and trying to write the invoice
693  * record prematurely */
694  gncInvoiceBeginEdit (invoice);
695  gncInvoiceSetID (invoice, id);
696  owner = gncOwnerNew ();
697  if (g_ascii_strcasecmp (type, "BILL") == 0)
698  gncOwnerInitVendor (owner,
699  gnc_search_vendor_on_id (book, owner_id));
700  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
701  gncOwnerInitCustomer (owner,
702  gnc_search_customer_on_id (book, owner_id));
703  gncInvoiceSetOwner (invoice, owner);
704  gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner)); // Set the invoice currency based on the owner
705  qof_scan_date (date_opened, &day, &month, &year);
706  gncInvoiceSetDateOpened (invoice,
707  gnc_dmy2time64 (day, month, year));
708  gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
709  notes = un_escape(notes);
710  gncInvoiceSetNotes (invoice, notes ? notes : "");
711  gncInvoiceSetActive (invoice, TRUE);
712  //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
713  (*n_invoices_created)++;
714  g_string_append_printf (info, _("Invoice %s created.\n"),id);
715 
716  gncInvoiceCommitEdit (invoice);
717  }
718  else // Dealing with an existing invoice.
719  {
720  // For the first existing invoice in the import file,
721  // ask the user to confirm update of existing invoices.
722  if (update == NOT_ASKED)
723  {
724  dialog = gtk_message_dialog_new (parent,
725  GTK_DIALOG_MODAL,
726  GTK_MESSAGE_ERROR,
727  GTK_BUTTONS_YES_NO,
728  "%s",
729  _("Do you want to update existing bills/invoices?"));
730  update = gtk_dialog_run (GTK_DIALOG (dialog));
731  gtk_widget_destroy (dialog);
732  }
733 
734  if (update == NO)
735  {
736  // If the user does not want to update existing invoices, ignore all rows of the invoice.
737  g_string_append_printf (info,_("Invoice %s not updated because it already exists.\n"),id);
738  while (valid && g_strcmp0 (id, running_id->str) == 0)
739  {
740  (*n_rows_ignored)++;
741  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
742  if (valid)
743  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
744  }
745  on_first_row_of_invoice = TRUE;
746  continue;
747  }
748 
749  if (gncInvoiceIsPosted (invoice))
750  {
751  // If the invoice is already posted, ignore all rows of the invoice.
752  g_string_append_printf (info,_("Invoice %s not updated because it is already posted.\n"),id);
753  while (valid && g_strcmp0 (id, running_id->str) == 0)
754  {
755  (*n_rows_ignored)++;
756  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
757  if (valid)
758  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
759  }
760  on_first_row_of_invoice = TRUE;
761  continue;
762  }
763 
764  (*n_invoices_updated)++;
765  g_string_append_printf (info, _("Invoice %s updated.\n"),id);
766  }
767  }
768 
769  // Add entry to invoice/bill
770  entry = gncEntryCreate (book);
771  gncEntryBeginEdit(entry);
772  qof_scan_date (date, &day, &month, &year);
773  {
774  GDate *date = g_date_new_dmy(day, month, year);
775  gncEntrySetDateGDate (entry, date);
776  g_date_free (date);
777  }
778  today = gnc_time (NULL);
779  gncEntrySetDateEntered(entry, today);
780  // Remove escaped quotes
781  desc = un_escape(desc);
782  notes = un_escape(notes);
783  gncEntrySetDescription (entry, desc);
784  gncEntrySetAction (entry, action);
785  value = gnc_numeric_zero();
786  gnc_exp_parser_parse (quantity, &value, NULL);
787  gncEntrySetQuantity (entry, value);
788  acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
789  account);
790 
791  if (g_ascii_strcasecmp (type, "BILL") == 0)
792  {
793  gncEntrySetBillAccount (entry, acc);
794  value = gnc_numeric_zero();
795  gnc_exp_parser_parse (price, &value, NULL);
796  gncEntrySetBillPrice (entry, value);
797  gncEntrySetBillTaxable (entry, text2bool (taxable));
798  gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
799  gncEntrySetBillTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
800  gncBillAddEntry (invoice, entry);
801  }
802  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
803  {
804  gncEntrySetNotes (entry, notes);
805  gncEntrySetInvAccount (entry, acc);
806  value = gnc_numeric_zero();
807  gnc_exp_parser_parse (price, &value, NULL);
808  gncEntrySetInvPrice (entry, value);
809  gncEntrySetInvTaxable (entry, text2bool (taxable));
810  gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
811  gncEntrySetInvTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
812  value = gnc_numeric_zero();
813  gnc_exp_parser_parse (discount, &value, NULL);
814  gncEntrySetInvDiscount (entry, value);
815  gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
816  gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
817  gncInvoiceAddEntry (invoice, entry);
818  }
819  gncEntryCommitEdit(entry);
820  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
821  // handle auto posting of invoices
822 
823  if (valid)
824  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
825  else
826  id = NULL;
827 
828  if (g_strcmp0 (id, running_id->str) == 0) // The next row is for the same invoice.
829  {
830  on_first_row_of_invoice = FALSE;
831  }
832  else // The next row is for a new invoice; try to post the invoice.
833  {
834  // Use posting values from the first row of this invoice.
835  gtk_tree_model_get (GTK_TREE_MODEL (store), &first_row_of_invoice,
836  ID, &id,
837  DATE_POSTED, &date_posted,
838  DUE_DATE, &due_date,
839  ACCOUNT_POSTED, &account_posted,
840  MEMO_POSTED, &memo_posted,
841  ACCU_SPLITS, &accumulatesplits, -1);
842  invoice_posted = FALSE;
843 
844  if (strlen(date_posted) != 0)
845  {
846  // autopost this invoice
847  GHashTable *foreign_currs;
848  gboolean auto_pay;
849  time64 p_date, d_date;
850  guint curr_count;
851  gboolean scan_date_r;
852  scan_date_r = qof_scan_date (date_posted, &day, &month, &year);
853  DEBUG("Invoice %s is marked to be posted because...", id);
854  DEBUG("qof_scan_date = %d", scan_date_r);
855  if (g_ascii_strcasecmp (type, "INVOICE") == 0)
856  auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
857  else
858  auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);
859  // Do we have any foreign currencies to deal with?
860  foreign_currs = gncInvoiceGetForeignCurrencies (invoice);
861  curr_count = g_hash_table_size (foreign_currs);
862  DEBUG("curr_count = %d",curr_count);
863  // Only auto-post if there's a single currency involved
864  if(curr_count == 0)
865  {
867  (gnc_get_current_root_account (), account_posted);
868  // Check if the currencies match
869  if(gncInvoiceGetCurrency(invoice) == gnc_account_get_currency_or_parent(acc))
870  {
871  qof_scan_date (date_posted, &day, &month, &year);
872  p_date = gnc_dmy2time64 (day, month, year);
873  qof_scan_date (due_date, &day, &month, &year);
874  d_date = gnc_dmy2time64 (day, month, year);
875  gncInvoicePostToAccount (invoice, acc, p_date, d_date,
876  memo_posted,
877  text2bool (accumulatesplits),
878  auto_pay);
879  PWARN("Invoice %s posted",id);
880  invoice_posted = TRUE;
881  g_string_append_printf (info, _("Invoice %s posted.\n"),id);
882  }
883  else // No match! Don't post it.
884  {
885  PWARN("Invoice %s NOT posted because currencies don't match", id);
886  g_string_append_printf (info,_("Invoice %s NOT posted because currencies don't match.\n"), id);
887  }
888  }
889  else
890  {
891  PWARN("Invoice %s NOT posted because it requires currency conversion.",id);
892  g_string_append_printf (info,_("Invoice %s NOT posted because it requires currency conversion.\n"),id);
893  }
894  g_hash_table_unref (foreign_currs);
895  }
896  else
897  {
898  PWARN("Invoice %s is NOT marked for posting",id);
899  }
900 
901  // open new bill / invoice in a tab, if requested
902  if (g_ascii_strcasecmp(open_mode, "ALL") == 0
903  || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
904  && !invoice_posted))
905  {
906  iw = gnc_ui_invoice_edit (parent, invoice);
908  }
909 
910  // The next row will be for a new invoice.
911  on_first_row_of_invoice = TRUE;
912  }
913  }
914 
915  if (*n_invoices_updated + *n_invoices_created == 0)
916  g_string_append_printf (info, _("Nothing to process.\n"));
917 
918  // cleanup
919  g_free (id);
920  g_free (date_opened);
921  g_free (owner_id);
922  g_free (billing_id);
923  g_free (notes);
924  g_free (date);
925  g_free (desc);
926  g_free (action);
927  g_free (account);
928  g_free (quantity);
929  g_free (price);
930  g_free (disc_type);
931  g_free (disc_how);
932  g_free (discount);
933  g_free (taxable);
934  g_free (taxincluded);
935  g_free (tax_table);
936  g_free (date_posted);
937  g_free (due_date);
938  g_free (account_posted);
939  g_free (memo_posted);
940  g_free (accumulatesplits);
941 
942  g_string_free (running_id, TRUE);
943 }
944 
945 /* Change any escaped quotes ("") to (")
946  * @param char* String to be modified
947  * @return char* Modified string.
948 */
949 static char*
950 un_escape(char *str)
951 {
952  gchar quote = '"';
953  gchar *newStr = NULL, *tmpstr = str;
954  int n = strlen (str), i;
955  newStr = g_malloc (n + 1);
956  memset (newStr, 0, n + 1);
957 
958  for (i = 0; *tmpstr != '\0'; ++i, ++tmpstr)
959  {
960  newStr[i] = *tmpstr == quote ? *(++tmpstr) : *(tmpstr);
961  if (*tmpstr == '\0')
962  break;
963  }
964  g_free (str);
965  return newStr;
966 }
utility functions for the GnuCash UI
core import functions for invoice import plugin
void gncEntrySetQuantity(GncEntry *entry, gnc_numeric quantity)
Set the internal quantity without any conversion.
Definition: gncEntry.c:551
Date and Time handling routines.
utility functions for the GnuCash UI
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3237
STRUCTS.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
time64 gnc_dmy2time64(gint day, gint month, gint year)
Convert a day, month, and year to a time64, returning the first second of the day.
void gnc_gdate_set_today(GDate *gd)
Set a GDate to the current day.
Definition: gnc-date.cpp:1236
Transaction * gncInvoicePostToAccount(GncInvoice *invoice, Account *acc, time64 post_date, time64 due_date, const char *memo, gboolean accumulatesplits, gboolean autopay)
Post this invoice to an account.
Definition: gncInvoice.c:1496
GHashTable * gncInvoiceGetForeignCurrencies(const GncInvoice *invoice)
Return an overview of amounts on this invoice that will be posted to accounts in currencies that are ...
Definition: gncInvoice.c:1362
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
QofDateFormat qof_date_format_get(void)
The qof_date_format_get routine returns the date format that the date printing will use when printing...
Definition: gnc-date.cpp:427
void gnc_bi_import_fix_bis(GtkListStore *store, guint *n_rows_fixed, guint *n_rows_ignored, GString *info, gchar *type)
Adjusts and validates invoice import data.
bi_import_result gnc_bi_import_read_file(const gchar *filename, const gchar *parser_regexp, GtkListStore *store, guint max_rows, bi_import_stats *stats)
Imports a csv file with invoice data into a GtkListStore.
GncPluginPage * gnc_plugin_page_invoice_new(InvoiceWindow *iw)
Create a new "invoice" plugin page, given a pointer to an InvoiceWindow data structure.
void gncBillAddEntry(GncInvoice *bill, GncEntry *entry)
Call this function when adding an entry to a bill instead of an invoice.
Definition: gncInvoice.c:719
void gnc_bi_import_create_bis(GtkListStore *store, QofBook *book, guint *n_invoices_created, guint *n_invoices_updated, guint *n_rows_ignored, gchar *type, gchar *open_mode, GString *info, GtkWindow *parent)
Creates and updates invoices from validated import data.
void gncEntrySetDateGDate(GncEntry *entry, const GDate *date)
Set the date of this entry.
Definition: gncEntry.c:504
A/P account type.
Definition: Account.h:151
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction&#39;s currency.
Definition: Account.cpp:3378
Account * gnc_account_lookup_for_register(const Account *base_account, const char *name)
Retrieve the account matching the given name starting from the descendants of base_account.
Generic api to store and retrieve preferences.
gboolean qof_scan_date(const char *buff, int *day, int *month, int *year)
qof_scan_date Convert a string into day / month / year integers according to the current dateFormat v...
Definition: gnc-date.cpp:918
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
A/R account type.
Definition: Account.h:149
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
Vendor Interface.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
Business Entry Interface.
an Address object
const gchar * qof_date_format_get_string(QofDateFormat df)
This function returns a strftime formatting string for printing an all numeric date (e...
Definition: gnc-date.cpp:501
GncOwner * gncOwnerNew(void)
These two functions are mainly for the convenience of scheme code.
Definition: gncOwner.c:57