GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 [email protected]
20  */
21 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <glib/gi18n.h>
38 #include <regex.h>
39 #include <glib.h>
40 #include <glib/gstdio.h>
41 
42 #include "gnc-ui.h"
43 #include "gnc-ui-util.h"
44 #include "gnc-gui-query.h"
45 #include "gncAddress.h"
46 #include "gncVendorP.h"
47 #include "gncVendor.h"
48 #include "gncEntry.h"
49 #include "gnc-prefs.h"
50 
51 #include "gnc-exp-parser.h"
52 
53 // query
54 #include "Query.h"
55 #include "qof.h"
56 #include "gncIDSearch.h"
57 #include "dialog-bi-import.h"
58 #include "dialog-bi-import-helper.h"
59 #include <gnc-gdate-utils.h>
60 
61 // To open the invoices for editing
63 #include "business/business-gnome/dialog-invoice.h"
64 #include "business/business-gnome/business-gnome-utils.h"
65 
66 
67 //#ifdef HAVE_GLIB_2_14
68 // glib >= 2.14.0
69 // perl regular expressions are available
70 
71 // this helper macro takes a regexp match and fills the model
72 #define FILL_IN_HELPER(match_name,column) \
73  temp = g_match_info_fetch_named (match_info, match_name); \
74  if (temp) \
75  { \
76  g_strstrip( temp ); \
77  gtk_list_store_set (store, &iter, column, temp, -1); \
78  g_free (temp); \
79  }
80 
81 static QofLogModule log_module = G_LOG_DOMAIN; //G_LOG_BUSINESS;
82 
83 bi_import_result
84 gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
85  GtkListStore * store, guint max_rows,
86  bi_import_stats * stats)
87 {
88  // some statistics
89  bi_import_stats stats_fallback;
90  FILE *f;
91 
92  // regexp
93  char *line;
94  gchar *line_utf8, *temp;
95  GMatchInfo *match_info;
96  GError *err;
97  GRegex *regexpat;
98 
99  // model
100  GtkTreeIter iter;
101 
102  f = g_fopen (filename, "rt");
103  if (!f)
104  {
105  //gnc_error_dialog( 0, _("File %s cannot be opened."), filename );
106  return RESULT_OPEN_FAILED;
107  }
108 
109  // set up statistics
110  if (!stats)
111  stats = &stats_fallback;
112 
113  // compile the regular expression and check for errors
114  err = NULL;
115  regexpat =
116  g_regex_new (parser_regexp, G_REGEX_EXTENDED | G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES, 0, &err);
117  if (err != NULL)
118  {
119  GtkWidget *dialog;
120  gchar *errmsg;
121 
122  errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
123  parser_regexp, err->message);
124  g_error_free (err);
125  err = NULL;
126 
127  dialog = gtk_message_dialog_new (NULL,
128  GTK_DIALOG_MODAL,
129  GTK_MESSAGE_ERROR,
130  GTK_BUTTONS_OK, "%s", errmsg);
131  gtk_dialog_run (GTK_DIALOG (dialog));
132  gtk_widget_destroy (dialog);
133  g_free (errmsg);
134  errmsg = 0;
135 
136  fclose (f);
137  return RESULT_ERROR_IN_REGEXP;
138  }
139 
140  // start the import
141  stats->n_imported = 0;
142  stats->n_ignored = 0;
143  stats->ignored_lines = g_string_new (NULL);
144 #define buffer_size 1000
145  line = g_malloc0 (buffer_size);
146  while (!feof (f)
147  && ((max_rows == 0)
148  || (stats->n_imported + stats->n_ignored < max_rows)))
149  {
150  int l;
151  // read one line
152  if (!fgets (line, buffer_size, f))
153  break; // eof
154  // now strip the '\n' from the end of the line
155  l = strlen (line);
156  if ((l > 0) && (line[l - 1] == '\n'))
157  line[l - 1] = 0;
158 
159  // convert line from locale into utf8
160  line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL);
161 
162  // parse the line
163  match_info = NULL; // it seems, that in contrast to documentation, match_info is not alsways set -> g_match_info_free will segfault
164  if (g_regex_match (regexpat, line_utf8, 0, &match_info))
165  {
166  // match found
167  stats->n_imported++;
168 
169  // fill in the values
170  gtk_list_store_append (store, &iter);
171  FILL_IN_HELPER ("id", ID); /* FIXME: Should "id" be translated? I don't think so. */
172  FILL_IN_HELPER ("date_opened", DATE_OPENED);
173  FILL_IN_HELPER ("owner_id", OWNER_ID);
174  FILL_IN_HELPER ("billing_id", BILLING_ID);
175  FILL_IN_HELPER ("notes", NOTES);
176 
177  FILL_IN_HELPER ("date", DATE);
178  FILL_IN_HELPER ("desc", DESC);
179  FILL_IN_HELPER ("action", ACTION);
180  FILL_IN_HELPER ("account", ACCOUNT);
181  FILL_IN_HELPER ("quantity", QUANTITY);
182  FILL_IN_HELPER ("price", PRICE);
183  FILL_IN_HELPER ("disc_type", DISC_TYPE);
184  FILL_IN_HELPER ("disc_how", DISC_HOW);
185  FILL_IN_HELPER ("discount", DISCOUNT);
186  FILL_IN_HELPER ("taxable", TAXABLE);
187  FILL_IN_HELPER ("taxincluded", TAXINCLUDED);
188  FILL_IN_HELPER ("tax_table", TAX_TABLE);
189 
190  FILL_IN_HELPER ("date_posted", DATE_POSTED);
191  FILL_IN_HELPER ("due_date", DUE_DATE);
192  FILL_IN_HELPER ("account_posted", ACCOUNT_POSTED);
193  FILL_IN_HELPER ("memo_posted", MEMO_POSTED);
194  FILL_IN_HELPER ("accu_splits", ACCU_SPLITS);
195  }
196  else
197  {
198  // ignore line
199  stats->n_ignored++;
200  g_string_append (stats->ignored_lines, line_utf8);
201  g_string_append_c (stats->ignored_lines, '\n');
202  }
203 
204  g_match_info_free (match_info);
205  match_info = 0;
206  g_free (line_utf8);
207  line_utf8 = 0;
208  }
209  g_free (line);
210  line = 0;
211 
212  g_regex_unref (regexpat);
213  regexpat = 0;
214  fclose (f);
215 
216  if (stats == &stats_fallback)
217  // stats are not requested -> free the string
218  g_string_free (stats->ignored_lines, TRUE);
219 
220  return RESULT_OK;
221 }
222 
223 
230 void
231 gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
232  GString * info, gchar *type)
233 {
234  GtkTreeIter iter;
235  gboolean valid, row_deleted, row_fixed;
236  gchar *id, *date_opened, *date_posted, *due_date, *owner_id, *date, *quantity, *price;
237  GString *prev_id, *prev_date_opened, *prev_date_posted, *prev_owner_id, *prev_date; // needed to fix multi line invoices
238  guint dummy;
239  gint row = 1;
240  const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
241 
242 
243  //date_format_string = qof_date_format_get_string (qof_date_format_get());
244 
245  DEBUG("date_format_string: %s",date_format_string);
246  // allow the call to this function with only GtkListeStore* specified
247  if (!fixed)
248  fixed = &dummy;
249  if (!deleted)
250  deleted = &dummy;
251 
252  *fixed = 0;
253  *deleted = 0;
254 
255  // init strings
256  prev_id = g_string_new ("");
257  prev_date_opened = g_string_new ("");
258  prev_date_posted = g_string_new ("");
259  prev_owner_id = g_string_new ("");
260  prev_date = g_string_new ("");
261 
262  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
263  while (valid)
264  {
265  row_deleted = FALSE;
266  row_fixed = FALSE;
267 
268  // Walk through the list, reading each row
269  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
270  ID, &id,
271  DATE_OPENED, &date_opened,
272  DATE_POSTED, &date_posted,
273  DUE_DATE, &due_date,
274  OWNER_ID, &owner_id,
275  DATE, &date,
276  QUANTITY, &quantity, PRICE, &price, -1);
277 
278  if (strlen (price) == 0)
279  {
280  // invalid row (no price given)
281  // no fix possible -> delete row
282  valid = gtk_list_store_remove (store, &iter);
283  row_deleted = TRUE;
284  g_string_append_printf (info,
285  _("ROW %d DELETED, PRICE_NOT_SET: id=%s\n"),
286  row, id);
287  }
288  // TODO: QTY get set to 1 later if field is empty. Delete this section?
289  else if (strlen (quantity) == 0)
290  {
291  // invalid row (no quantity given)
292  // no fix possible -> delete row
293  valid = gtk_list_store_remove (store, &iter);
294  row_deleted = TRUE;
295  g_string_append_printf (info, _("ROW %d DELETED, QTY_NOT_SET: id=%s\n"),
296  row, id);
297  }
298  else
299  { // TODO: If id is empty get the next one in the series. Bug 731105
300  if (strlen (id) == 0)
301  {
302  // no invoice id specified
303  if (prev_id->len == 0)
304  {
305  // cannot fix -> delete row
306  valid = gtk_list_store_remove (store, &iter);
307  row_deleted = TRUE;
308  g_string_append_printf (info,
309  _("ROW %d DELETED, ID_NOT_SET\n"), row);
310  }
311  else
312  {
313  // this is a fixable multi line invoice
314  gtk_list_store_set (store, &iter, ID, prev_id->str, -1);
315  row_fixed = TRUE;
316  }
317  }
318  else
319  {
320  // remember invoice id (to be able to fix multi line invoices)
321  g_string_assign (prev_id, id);
322  // new invoice => reset all other fixable entries
323  g_string_assign (prev_date_opened, "");
324  g_string_assign (prev_date_posted, "");
325  g_string_assign (prev_owner_id, "");
326  g_string_assign (prev_date, "");
327  }
328  }
329 
330  if (!row_deleted)
331  {
332  // the row is valid (price and id are valid)
333 
334  if(!isDateValid(date_opened))
335  {
336  if (prev_date_opened->len == 0)
337  {
338  // fix this by using the current date
339  gchar temp[20];
340  GDate date;
341  g_date_clear (&date, 1);
342  gnc_gdate_set_today (&date);
343  g_date_strftime (temp, 20, date_format_string, &date); // Create a user specified date string.
344  g_string_assign (prev_date_opened, temp);
345  }
346  // fix this by using the previous date_opened value (multi line invoice)
347  gtk_list_store_set (store, &iter, DATE_OPENED,
348  prev_date_opened->str, -1);
349  row_fixed = TRUE;
350  }
351  else
352  {
353  // remember date_opened (to be able to fix multi line invoices)
354  g_string_assign (prev_date_opened, date_opened);
355  }
356 
357  // date_opened is valid
358 
359  if(!isDateValid(date_posted))
360  {
361  if (prev_date_posted->len == 0)
362  {
363  // this invoice will have to get posted manually
364  }
365  else
366  {
367  // multi line invoice => fix it
368  gtk_list_store_set (store, &iter, DATE_POSTED,
369  prev_date_posted->str, -1);
370  row_fixed = TRUE;
371  }
372  }
373  else
374  {
375  // remember date_opened (to be able to fix multi line invoices)
376  g_string_assign (prev_date_posted, date_posted);
377  }
378 
379  // date_posted is valid
380  /*
381  // Check if due date is valid. Set it to date_posted if not valid or missing.
382  if(!isDateValid(due_date))
383  {
384  gtk_list_store_set (store, &iter, DUE_DATE,
385  date_posted, -1);
386  row_fixed = TRUE;
387 
388  }
389 
390  // due_date is valid
391  */
392  if (strlen (quantity) == 0)
393  {
394  // quantity is unset => set to 1
395  gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
396  row_fixed = TRUE;
397  }
398 
399 
400  // quantity is valid
401 
402  if (strlen (owner_id) == 0)
403  {
404  if (prev_owner_id->len == 0)
405  {
406  // no customer given and not fixable => delete row
407  valid = gtk_list_store_remove (store, &iter);
408  row_deleted = TRUE;
409  g_string_append_printf (info,
410  _("ROW %d DELETED, OWNER_NOT_SET: id=%s\n"),
411  row, id);
412  }
413  else
414  {
415  gtk_list_store_set (store, &iter, owner_id,
416  prev_owner_id->str, -1);
417  row_fixed = TRUE;
418  }
419  }
420  else
421  {
422  // remember owner_id
423  g_string_assign (prev_owner_id, owner_id);
424  }
425  if (g_ascii_strcasecmp (type, "BILL") == 0)
426  {
427  // BILL: check, if vendor exists
428  if (!gnc_search_vendor_on_id
429  (gnc_get_current_book (), prev_owner_id->str))
430  {
431  // vendor not found => delete row
432  valid = gtk_list_store_remove (store, &iter);
433  row_deleted = TRUE;
434  g_string_append_printf (info,
435  _("ROW %d DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"),
436  row, id);
437  }
438  }
439  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
440  {
441  // INVOICE: check, if customer exists
442  if (!gnc_search_customer_on_id
443  (gnc_get_current_book (), prev_owner_id->str))
444  {
445  // customer not found => delete row
446  valid = gtk_list_store_remove (store, &iter);
447  row_deleted = TRUE;
448  g_string_append_printf (info,
449  _("ROW %d DELETED, CUSTOMER_DOES_NOT_EXIST: id=%s\n"),
450  row, id);
451  }
452  }
453 
454  // owner_id is valid
455  }
456 
457  g_free (id);
458  g_free (date_opened);
459  g_free (date_posted);
460  g_free (owner_id);
461  g_free (date);
462  g_free (quantity);
463  g_free (price);
464  if (row_deleted)
465  {
466  (*deleted)++;
467  // reset all remembered values
468  g_string_assign (prev_id, "");
469  g_string_assign (prev_date_opened, "");
470  g_string_assign (prev_date_posted, "");
471  g_string_assign (prev_owner_id, "");
472  g_string_assign (prev_date, "");
473  }
474  else if (row_fixed)
475  (*fixed)++;
476 
477  if (!row_deleted)
478  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
479 
480  row++;
481  }
482 
483  // deallocate strings
484  g_string_free (prev_id, TRUE);
485  g_string_free (prev_date_opened, TRUE);
486  g_string_free (prev_date_posted, TRUE);
487  g_string_free (prev_owner_id, TRUE);
488  g_string_free (prev_date, TRUE);
489 
490  if (info && (info->len > 0))
491  {
492  g_string_prepend (info, "\n\n");
493  g_string_prepend (info, _("These rows were deleted:"));
494  }
495 }
496 
497 
498 /***********************************************************************
499  * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...)
500  * rather than in here? But that is more concerned with ensuring the csv is consistent.
501  * @param GtkListStore *store
502  * @param guint *n_invoices_created
503  * @param guint *n_invoices_updated
504  * @return void
505  ***********************************************************************/
506 void
507 gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
508  guint * n_invoices_created,
509  guint * n_invoices_updated,
510  gchar * type, gchar * open_mode, GString * info)
511 {
512  gboolean valid;
513  GtkTreeIter iter;
514  gchar *id, *date_opened, *owner_id, *billing_id, *notes;
515  gchar *date, *desc, *action, *account, *quantity, *price, *disc_type,
516  *disc_how, *discount, *taxable, *taxincluded, *tax_table;
517  gchar *date_posted, *due_date, *account_posted, *memo_posted,
518  *accumulatesplits;
519  guint dummy;
520  GncInvoice *invoice;
521  GncEntry *entry;
522  gint day, month, year;
523  gnc_numeric value;
524  GncOwner *owner;
525  Account *acc;
526  enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
527  GtkWidget *dialog;
528  Timespec today;
529  InvoiceWindow *iw;
530  gchar *new_id = NULL;
531  gint64 denom = 0;
532  gnc_commodity *currency;
533 
534  // these arguments are needed
535  g_return_if_fail (store && book);
536  // logic of this function only works for bills or invoices
537  g_return_if_fail ((g_ascii_strcasecmp (type, "INVOICE") == 0) ||
538  (g_ascii_strcasecmp (type, "BILL") == 0));
539 
540  // allow to call this function without statistics
541  if (!n_invoices_created)
542  n_invoices_created = &dummy;
543  if (!n_invoices_updated)
544  n_invoices_updated = &dummy;
545  *n_invoices_created = 0;
546  *n_invoices_updated = 0;
547 
548  invoice = NULL;
549  update = NO;
550 
551  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
552  while (valid)
553  {
554  // Walk through the list, reading each row
555  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
556  ID, &id,
557  DATE_OPENED, &date_opened,
558  DATE_POSTED, &date_posted, // if autoposting requested
559  DUE_DATE, &due_date, // if autoposting requested
560  ACCOUNT_POSTED, &account_posted, // if autoposting requested
561  MEMO_POSTED, &memo_posted, // if autoposting requested
562  ACCU_SPLITS, &accumulatesplits, // if autoposting requested
563  OWNER_ID, &owner_id,
564  BILLING_ID, &billing_id,
565  NOTES, &notes,
566  DATE, &date,
567  DESC, &desc,
568  ACTION, &action,
569  ACCOUNT, &account,
570  QUANTITY, &quantity,
571  PRICE, &price,
572  DISC_TYPE, &disc_type,
573  DISC_HOW, &disc_how,
574  DISCOUNT, &discount,
575  TAXABLE, &taxable,
576  TAXINCLUDED, &taxincluded,
577  TAX_TABLE, &tax_table, -1);
578 
579  // TODO: Assign a new invoice number if one is absent. BUT we don't want to assign a new invoice for every line!!
580  // so we'd have to flag this up somehow or add an option in the import GUI. The former implies that we make
581  // an assumption about what the importer (person) wants to do. It seems reasonable that a CSV file full of items with
582  // If an invoice exists then we add to it in this current schema.
583  // no predefined invoice number is a new invoice that's in need of a new number.
584  // This was not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all
585  // It should be easier to copy an invoice with a new ID than to go through all this malarky.
586  if (g_ascii_strcasecmp (type, "BILL") == 0)
587  invoice = gnc_search_bill_on_id (book, id);
588  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
589  invoice = gnc_search_invoice_on_id (book, id);
590  DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));
591 
592  // If the search is empty then there is no existing invoice so make a new one
593  if (invoice == NULL)
594  {
595  DEBUG( "Creating a new : %s\n", type );
596  // new invoice
597  invoice = gncInvoiceCreate (book);
598  /* Protect against thrashing the DB and trying to write the invoice
599  * record prematurely */
600  gncInvoiceBeginEdit (invoice);
601  gncInvoiceSetID (invoice, id);
602  owner = gncOwnerNew ();
603  if (g_ascii_strcasecmp (type, "BILL") == 0)
604  gncOwnerInitVendor (owner,
605  gnc_search_vendor_on_id (book, owner_id));
606  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
607  gncOwnerInitCustomer (owner,
608  gnc_search_customer_on_id (book, owner_id));
609  gncInvoiceSetOwner (invoice, owner);
610  gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner)); // Set the invoice currency based on the owner
611  if (strlen (date_opened) != 0) // If a date is specified in CSV
612  {
613  // FIXME: Must check for the return value of qof_scan_date!
614  qof_scan_date (date_opened, &day, &month, &year);
615  gncInvoiceSetDateOpened (invoice,
616  gnc_dmy2timespec (day, month, year));
617  }
618  else // If no date in CSV
619  {
620  time64 now = gnc_time (NULL);
621  Timespec now_timespec;
622  timespecFromTime64 (&now_timespec, now);
623  gncInvoiceSetDateOpened (invoice, now_timespec);
624  }
625  gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
626  gncInvoiceSetNotes (invoice, notes ? notes : "");
627  gncInvoiceSetActive (invoice, TRUE);
628  //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
629  (*n_invoices_created)++;
630  update = YES;
631 
632  // open new bill / invoice in a tab, if requested
633  if (g_ascii_strcasecmp(open_mode, "ALL") == 0
634  || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
635  && strlen(date_posted) == 0))
636  {
637  iw = gnc_ui_invoice_edit (invoice);
639  }
640  gncInvoiceCommitEdit (invoice);
641  }
642 // I want to warn the user that an existing billvoice exists, but not every
643 // time.
644 // An import can contain many lines usually referring to the same invoice.
645 // NB: Posted invoices are NEVER updated.
646  else // if invoice exists
647  {
648  if (gncInvoiceIsPosted (invoice)) // Is it already posted?
649  {
650  valid =
651  gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
652  continue; // If already posted then never import
653  }
654  if (update != YES) // Pop up a dialog to ask if updates are the expected action
655  {
656  dialog = gtk_message_dialog_new (NULL,
657  GTK_DIALOG_MODAL,
658  GTK_MESSAGE_ERROR,
659  GTK_BUTTONS_YES_NO,
660  "%s",
661  _("Are you sure you have bills/invoices to update?"));
662  update = gtk_dialog_run (GTK_DIALOG (dialog));
663  gtk_widget_destroy (dialog);
664  if (update == NO)
665  {
666  // Cleanup and leave
667  g_free (id);
668  g_free (date_opened);
669  g_free (owner_id);
670  g_free (billing_id);
671  g_free (notes);
672  g_free (date);
673  g_free (desc);
674  g_free (action);
675  g_free (account);
676  g_free (quantity);
677  g_free (price);
678  g_free (disc_type);
679  g_free (disc_how);
680  g_free (discount);
681  g_free (taxable);
682  g_free (taxincluded);
683  g_free (tax_table);
684  g_free (date_posted);
685  g_free (due_date);
686  g_free (account_posted);
687  g_free (memo_posted);
688  g_free (accumulatesplits);
689  return;
690  }
691  }
692  (*n_invoices_updated)++;
693  }
694 
695 
696  // add entry to invoice/bill
697  entry = gncEntryCreate (book);
698  gncEntryBeginEdit(entry);
699  currency = gncInvoiceGetCurrency(invoice);
700  if (currency) denom = gnc_commodity_get_fraction(currency);
701  // FIXME: Must check for the return value of qof_scan_date!
702  qof_scan_date (date, &day, &month, &year);
703  {
704  GDate *date = g_date_new_dmy(day, month, year);
705  gncEntrySetDateGDate (entry, date);
706  g_date_free (date);
707  }
708  timespecFromTime64 (&today, gnc_time (NULL)); // set today to the current date
709  gncEntrySetDateEntered (entry, today);
710  gncEntrySetDescription (entry, desc);
711  gncEntrySetAction (entry, action);
712 
713  gnc_exp_parser_parse (quantity, &value, NULL);
714  gncEntrySetQuantity (entry, value);
715  acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
716  account);
717 
718  if (g_ascii_strcasecmp (type, "BILL") == 0)
719  {
720  gncEntrySetBillAccount (entry, acc);
721  gnc_exp_parser_parse (price, &value, NULL);
722  gncEntrySetBillPrice (entry, value);
723  gncEntrySetBillTaxable (entry, text2bool (taxable));
724  gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
725  gncEntrySetBillTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
726  gncEntryCommitEdit(entry);
727  gncBillAddEntry (invoice, entry);
728  }
729  else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
730  {
731  gncEntrySetNotes (entry, notes);
732  gncEntrySetInvAccount (entry, acc);
733  gnc_exp_parser_parse (price, &value, NULL);
734  gncEntrySetInvPrice (entry, value);
735  gncEntrySetInvTaxable (entry, text2bool (taxable));
736  gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
737  gncEntrySetInvTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
738  gnc_exp_parser_parse (discount, &value, NULL);
739  gncEntrySetInvDiscount (entry, value);
740  gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
741  gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
742  gncEntryCommitEdit(entry);
743  gncInvoiceAddEntry (invoice, entry);
744  }
745  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
746 
747  // handle auto posting of invoices
748 
749 
750  if (valid)
751  gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
752  if (g_strcmp0 (id, new_id) != 0)
753  {
754  // the next invoice id is different => try to autopost this invoice
755  if (qof_scan_date (date_posted, &day, &month, &year))
756  {
757  // autopost this invoice
758  gboolean auto_pay;
759  Timespec d1, d2;
760 
761  if (g_ascii_strcasecmp (type, "INVOICE") == 0)
762  auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
763  else
764  auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);
765 
766  d1 = gnc_dmy2timespec (day, month, year);
767  // FIXME: Must check for the return value of qof_scan_date!
768  qof_scan_date (due_date, &day, &month, &year); // obtains the due date, or leaves it at date_posted
769  d2 = gnc_dmy2timespec (day, month, year);
771  (gnc_get_current_root_account (), account_posted);
772  gncInvoicePostToAccount (invoice, acc, &d1, &d2,
773  memo_posted,
774  text2bool (accumulatesplits),
775  auto_pay);
776  }
777 
778  }
779 
780 
781  }
782  // cleanup
783  g_free (new_id);
784  g_free (id);
785  g_free (date_opened);
786  g_free (owner_id);
787  g_free (billing_id);
788  g_free (notes);
789  g_free (date);
790  g_free (desc);
791  g_free (action);
792  g_free (account);
793  g_free (quantity);
794  g_free (price);
795  g_free (disc_type);
796  g_free (disc_how);
797  g_free (discount);
798  g_free (taxable);
799  g_free (taxincluded);
800  g_free (tax_table);
801  g_free (date_posted);
802  g_free (due_date);
803  g_free (account_posted);
804  g_free (memo_posted);
805  g_free (accumulatesplits);
806 
807 }
void gnc_gdate_set_today(GDate *gd)
utility functions for the GnuCash UI
core import functions for invoice import plugin
int gnc_commodity_get_fraction(const gnc_commodity *cm)
void gncEntrySetQuantity(GncEntry *entry, gnc_numeric quantity)
Definition: gncEntry.c:552
utility functions for the GnuCash UI
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define DEBUG(format, args...)
Definition: qoflog.h:255
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
Timespec gnc_dmy2timespec(gint day, gint month, gint year)
void gnc_bi_import_fix_bis(GtkListStore *store, guint *fixed, guint *deleted, GString *info, gchar *type)
try to fix some common errors in the csv representation of invoices
GncPluginPage * gnc_plugin_page_invoice_new(InvoiceWindow *iw)
void gncBillAddEntry(GncInvoice *bill, GncEntry *entry)
Definition: gncInvoice.c:686
const gchar * qof_date_format_get_string(QofDateFormat df)
void gncEntrySetDateGDate(GncEntry *entry, const GDate *date)
Definition: gncEntry.c:505
Transaction * gncInvoicePostToAccount(GncInvoice *invoice, Account *acc, Timespec *post_date, Timespec *due_date, const char *memo, gboolean accumulatesplits, gboolean autopay)
Definition: gncInvoice.c:1363
Generic api to store and retrieve preferences.
GDate helper routines.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
gboolean qof_scan_date(const char *buff, int *day, int *month, int *year)
QofDateFormat qof_date_format_get(void)
time64 gnc_time(time64 *tbuf)
get the current local time
Vendor Interface.
gint64 time64
Definition: gnc-date.h:83
Business Entry Interface.
an Address object
Account * gnc_account_lookup_for_register(const Account *base_account, const gchar *name)
GncOwner * gncOwnerNew(void)
Definition: gncOwner.c:58
const gchar * QofLogModule
Definition: qofid.h:89
void timespecFromTime64(Timespec *ts, time64 t)