34 #include <glib/gi18n.h> 37 #include <glib/gstdio.h> 43 #include "gnc-gui-query.h" 45 #include "gncVendorP.h" 50 #include "gnc-exp-parser.h" 55 #include "gncIDSearch.h" 57 #include "dialog-bi-import-helper.h" 61 #include "dialog-invoice.h" 62 #include "business-gnome-utils.h" 65 #define FILL_IN_HELPER(match_name,column) \ 66 temp = g_match_info_fetch_named (match_info, match_name); \ 70 gtk_list_store_set (store, &iter, column, temp, -1); \ 72 } else gtk_list_store_set (store, &iter, column, "", -1); 75 static char * un_escape(
char *str);
99 GtkListStore * store, guint max_rows,
100 bi_import_stats * stats)
103 bi_import_stats stats_fallback;
108 gchar *line_utf8 = NULL;
110 GMatchInfo *match_info;
117 f = g_fopen (filename,
"rt");
121 return RESULT_OPEN_FAILED;
126 stats = &stats_fallback;
131 g_regex_new (parser_regexp, G_REGEX_EXTENDED | G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES, 0, &err);
137 errmsg = g_strdup_printf (_(
"Error in regular expression '%s':\n%s"),
138 parser_regexp, err->message);
142 dialog = gtk_message_dialog_new (NULL,
145 GTK_BUTTONS_OK,
"%s", errmsg);
146 gtk_dialog_run (GTK_DIALOG (dialog));
147 gtk_widget_destroy (dialog);
152 return RESULT_ERROR_IN_REGEXP;
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);
163 || (stats->n_imported + stats->n_ignored < max_rows)))
167 if (!fgets (line, buffer_size, f))
171 if ((l > 0) && (line[l - 1] ==
'\n'))
176 if (g_utf8_validate(line, -1, NULL))
179 line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL);
186 if (g_regex_match (regexpat, line_utf8, 0, &match_info))
192 gtk_list_store_append (store, &iter);
193 FILL_IN_HELPER (
"id", ID);
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);
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);
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);
222 g_string_append (stats->ignored_lines, line_utf8);
223 g_string_append_c (stats->ignored_lines,
'\n');
226 g_match_info_free (match_info);
227 if (line_utf8 != line)
233 g_regex_unref (regexpat);
237 if (stats == &stats_fallback)
239 g_string_free (stats->ignored_lines, TRUE);
277 GString * info, gchar *type)
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;
286 gint row = 1, fixed_for_invoice = 0, invoice_line = 0;
289 DEBUG(
"date_format_string: %s",date_format_string);
292 n_rows_fixed = &dummy;
294 n_rows_ignored = &dummy;
300 running_id = g_string_new(
"");
301 ignore_invoice = FALSE;
302 on_first_row_of_invoice = TRUE;
304 g_string_append_printf (info, _(
"Validation…\n") );
307 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
314 if (on_first_row_of_invoice)
316 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
318 DATE_OPENED, &date_opened,
319 DATE_POSTED, &date_posted,
321 ACCOUNT_POSTED, &account_posted,
322 OWNER_ID, &owner_id, -1);
324 g_string_assign (running_id,
id);
325 first_row_of_invoice = iter;
328 if (strlen (
id) == 0)
332 ignore_invoice = TRUE;
333 g_string_append_printf (info,
334 _(
"Row %d: no invoice ID in first row of import file.\n"), row);
338 if (strlen (owner_id) == 0)
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);
346 if (g_ascii_strcasecmp (type,
"BILL") == 0)
348 if (!gnc_search_vendor_on_id
349 (gnc_get_current_book (), owner_id))
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);
358 else if (g_ascii_strcasecmp (type,
"INVOICE") == 0)
360 if (!gnc_search_customer_on_id
361 (gnc_get_current_book (), owner_id))
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);
371 if (strlen(date_posted) != 0)
374 if (!isDateValid(date_posted))
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);
383 if (!isDateValid(due_date))
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);
394 if (!isDateValid(due_date))
397 gtk_list_store_set (store, &iter, DUE_DATE,
406 (gnc_get_current_root_account (), account_posted);
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);
416 if (g_ascii_strcasecmp (type,
"BILL") == 0)
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);
427 else if (g_ascii_strcasecmp (type,
"INVOICE") == 0)
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);
441 if(!isDateValid(date_opened))
446 g_date_clear (&date, 1);
448 g_date_strftime (temp, 20, date_format_string, &date);
449 gtk_list_store_set (store, &iter, DATE_OPENED,
458 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
466 if (strlen (price) == 0)
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);
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);
491 if (strlen (quantity) == 0)
494 gtk_list_store_set (store, &iter, QUANTITY,
"1", -1);
499 if(!isDateValid(date))
502 gtk_list_store_set (store, &iter, DATE,
508 if (row_fixed) ++fixed_for_invoice;
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);
516 if (valid && strlen(
id) == 0)
518 strcpy(
id, running_id->str);
519 gtk_list_store_set (store, &iter, ID,
id, -1);
523 if (!valid || (valid && g_strcmp0 (
id, running_id->str) != 0))
528 iter = first_row_of_invoice;
532 valid = gtk_list_store_remove (store, &iter);
533 if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &
id, -1);
535 while (valid && (g_strcmp0 (
id, running_id->str) == 0));
537 if (running_id->len != 0)
539 g_string_append_printf (info,
540 _(
"Error(s) in invoice %s, all rows of this invoice ignored.\n"),
545 g_string_append_printf (info,
546 _(
"Error(s) in invoice without id, all rows of this invoice ignored.\n"));
550 fixed_for_invoice = 0;
552 ignore_invoice = FALSE;
555 on_first_row_of_invoice = TRUE;
556 (*n_rows_fixed) += fixed_for_invoice;
557 fixed_for_invoice = 0;
561 g_free (date_opened);
562 g_free (date_posted);
564 g_free (account_posted);
567 else on_first_row_of_invoice = FALSE;
578 g_string_free (running_id, TRUE);
606 guint * n_invoices_created,
607 guint * n_invoices_updated,
608 guint * n_rows_ignored,
609 gchar * type, gchar * open_mode, GString * info,
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;
623 gint day, month, year;
627 enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO, NOT_ASKED = GTK_RESPONSE_NONE} update;
634 g_return_if_fail (store && book);
636 g_return_if_fail ((g_ascii_strcasecmp (type,
"INVOICE") == 0) ||
637 (g_ascii_strcasecmp (type,
"BILL") == 0));
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;
649 on_first_row_of_invoice = TRUE;
650 running_id = g_string_new(
"");
652 g_string_append_printf (info,
"\n%s\n", _(
"Processing…") );
654 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
658 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
660 DATE_OPENED, &date_opened,
661 DATE_POSTED, &date_posted,
663 ACCOUNT_POSTED, &account_posted,
664 MEMO_POSTED, &memo_posted,
665 ACCU_SPLITS, &accumulatesplits,
667 BILLING_ID, &billing_id,
675 DISC_TYPE, &disc_type,
679 TAXINCLUDED, &taxincluded,
680 TAX_TABLE, &tax_table, -1);
682 if (on_first_row_of_invoice)
684 g_string_assign(running_id,
id);
685 first_row_of_invoice = iter;
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));
696 DEBUG(
"Creating a new : %s\n", type );
698 invoice = gncInvoiceCreate (book);
701 gncInvoiceBeginEdit (invoice);
702 gncInvoiceSetID (invoice,
id);
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));
713 gncInvoiceSetDateOpened (invoice,
715 gncInvoiceSetBillingID (invoice, billing_id ? billing_id :
"");
716 notes = un_escape(notes);
717 gncInvoiceSetNotes (invoice, notes ? notes :
"");
718 gncInvoiceSetActive (invoice, TRUE);
720 (*n_invoices_created)++;
721 g_string_append_printf (info, _(
"Invoice %s created.\n"),
id);
723 gncInvoiceCommitEdit (invoice);
729 if (update == NOT_ASKED)
731 dialog = gtk_message_dialog_new (parent,
736 _(
"Do you want to update existing bills/invoices?"));
737 update = gtk_dialog_run (GTK_DIALOG (dialog));
738 gtk_widget_destroy (dialog);
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)
748 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
750 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &
id, -1);
752 on_first_row_of_invoice = TRUE;
756 if (gncInvoiceIsPosted (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)
763 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
765 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &
id, -1);
767 on_first_row_of_invoice = TRUE;
771 (*n_invoices_updated)++;
772 g_string_append_printf (info, _(
"Invoice %s updated.\n"),
id);
777 entry = gncEntryCreate (book);
778 gncEntryBeginEdit(entry);
781 GDate *date = g_date_new_dmy(day, month, year);
786 gncEntrySetDateEntered(entry, today);
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);
798 if (g_ascii_strcasecmp (type,
"BILL") == 0)
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));
809 else if (g_ascii_strcasecmp (type,
"INVOICE") == 0)
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);
826 gncEntryCommitEdit(entry);
827 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
831 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &
id, -1);
835 if (g_strcmp0 (
id, running_id->str) == 0)
837 on_first_row_of_invoice = FALSE;
842 gtk_tree_model_get (GTK_TREE_MODEL (store), &first_row_of_invoice,
844 DATE_POSTED, &date_posted,
846 ACCOUNT_POSTED, &account_posted,
847 MEMO_POSTED, &memo_posted,
848 ACCU_SPLITS, &accumulatesplits, -1);
849 invoice_posted = FALSE;
851 if (strlen(date_posted) != 0)
854 GHashTable *foreign_currs;
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)
868 curr_count = g_hash_table_size (foreign_currs);
869 DEBUG(
"curr_count = %d",curr_count);
874 (gnc_get_current_root_account (), account_posted);
884 text2bool (accumulatesplits),
886 PWARN(
"Invoice %s posted",
id);
887 invoice_posted = TRUE;
888 g_string_append_printf (info, _(
"Invoice %s posted.\n"),
id);
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);
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);
901 g_hash_table_unref (foreign_currs);
905 PWARN(
"Invoice %s is NOT marked for posting",
id);
909 if (g_ascii_strcasecmp(open_mode,
"ALL") == 0
910 || (g_ascii_strcasecmp(open_mode,
"NOT_POSTED") == 0
913 iw = gnc_ui_invoice_edit (parent, invoice);
918 on_first_row_of_invoice = TRUE;
922 if (*n_invoices_updated + *n_invoices_created == 0)
923 g_string_append_printf (info, _(
"Nothing to process.\n"));
927 g_free (date_opened);
941 g_free (taxincluded);
943 g_free (date_posted);
945 g_free (account_posted);
946 g_free (memo_posted);
947 g_free (accumulatesplits);
949 g_string_free (running_id, TRUE);
960 gchar *newStr = NULL, *tmpstr = str;
961 int n = strlen (str), i;
962 newStr = g_malloc (n + 1);
963 memset (newStr, 0, n + 1);
965 for (i = 0; *tmpstr !=
'\0'; ++i, ++tmpstr)
967 newStr[i] = *tmpstr == quote ? *(++tmpstr) : *(tmpstr);
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.
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's account type.
#define DEBUG(format, args...)
Print a debugging message.
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.
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.
GHashTable * gncInvoiceGetForeignCurrencies(const GncInvoice *invoice)
Return an overview of amounts on this invoice that will be posted to accounts in currencies that are ...
#define PWARN(format, args...)
Log a warning.
QofDateFormat qof_date_format_get(void)
The qof_date_format_get routine returns the date format that the date printing will use when printing...
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.
void gncEntrySetDateGDate(GncEntry *entry, const GDate *date)
Set the date of this entry.
void gnc_utf8_strip_invalid(gchar *str)
Strip any non-UTF-8 characters from a string.
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction's currency.
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...
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
time64 gnc_time(time64 *tbuf)
get the current time
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Business Entry Interface.
const gchar * qof_date_format_get_string(QofDateFormat df)
This function returns a strftime formatting string for printing an all numeric date (e...
GncOwner * gncOwnerNew(void)
These two functions are mainly for the convenience of scheme code.