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