PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/src/plugins/bi_import/bi_import.c

http://github.com/mchochlov/Gnucash
C | 739 lines | 557 code | 64 blank | 118 comment | 77 complexity | 501570f3e6f6b7f70bd6ce983925e7b3 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * bi_import.c --
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of
  7. * the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, contact:
  16. *
  17. * Free Software Foundation Voice: +1-617-542-5942
  18. * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
  19. * Boston, MA 02110-1301, USA gnu@gnu.org
  20. */
  21. /**
  22. * @internal
  23. * @file bi_import.c
  24. * @brief core import functions for invoice import plugin
  25. * @author Copyright (C) 2009 Sebastian Held <sebastian.held@gmx.de>
  26. * @author Mike Evans <mikee@saxicola.co.uk>
  27. * @todo Create an option to import a pre-formed regex when it is present
  28. * to enable the use of custom output csv formats.
  29. * @todo Open the newly created invoice(es).
  30. */
  31. #ifdef HAVE_CONFIG_H
  32. #include "config.h"
  33. #endif
  34. #ifndef HAVE_LOCALTIME_R
  35. #include "localtime_r.h"
  36. #endif
  37. #include <glib/gi18n.h>
  38. #include <regex.h>
  39. #include <glib.h>
  40. #include <glib/gstdio.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-exp-parser.h"
  49. // query
  50. #include "Query.h"
  51. #include "qof.h"
  52. #include "GNCId.h"
  53. #include "gncIDSearch.h"
  54. #include "bi_import.h"
  55. #include "helpers.h"
  56. // To open the invoices for editing
  57. #include "business/business-gnome/gnc-plugin-page-invoice.h"
  58. #include "business/business-gnome/dialog-invoice.h"
  59. //#ifdef HAVE_GLIB_2_14
  60. // glib >= 2.14.0
  61. // perl regular expressions are available
  62. // this helper macro takes a regexp match and fills the model
  63. #define FILL_IN_HELPER(match_name,column) \
  64. temp = g_match_info_fetch_named (match_info, match_name); \
  65. if (temp) \
  66. { \
  67. g_strstrip( temp ); \
  68. gtk_list_store_set (store, &iter, column, temp, -1); \
  69. g_free (temp); \
  70. }
  71. bi_import_result
  72. gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
  73. GtkListStore * store, guint max_rows,
  74. bi_import_stats * stats)
  75. {
  76. // some statistics
  77. bi_import_stats stats_fallback;
  78. FILE *f;
  79. // regexp
  80. char *line;
  81. gchar *line_utf8, *temp;
  82. GMatchInfo *match_info;
  83. GError *err;
  84. GRegex *regexpat;
  85. // model
  86. GtkTreeIter iter;
  87. f = g_fopen (filename, "rt");
  88. if (!f)
  89. {
  90. //gnc_error_dialog( 0, _("File %s cannot be opened."), filename );
  91. return RESULT_OPEN_FAILED;
  92. }
  93. // set up statistics
  94. if (!stats)
  95. stats = &stats_fallback;
  96. // compile the regular expression and check for errors
  97. err = NULL;
  98. regexpat =
  99. g_regex_new (parser_regexp, G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, 0, &err);
  100. if (err != NULL)
  101. {
  102. GtkWidget *dialog;
  103. gchar *errmsg;
  104. errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
  105. parser_regexp, err->message);
  106. g_error_free (err);
  107. err = NULL;
  108. dialog = gtk_message_dialog_new (NULL,
  109. GTK_DIALOG_MODAL,
  110. GTK_MESSAGE_ERROR,
  111. GTK_BUTTONS_OK, "%s", errmsg);
  112. gtk_dialog_run (GTK_DIALOG (dialog));
  113. gtk_widget_destroy (dialog);
  114. g_free (errmsg);
  115. errmsg = 0;
  116. fclose (f);
  117. return RESULT_ERROR_IN_REGEXP;
  118. }
  119. // start the import
  120. stats->n_imported = 0;
  121. stats->n_ignored = 0;
  122. stats->ignored_lines = g_string_new (NULL);
  123. #define buffer_size 1000
  124. line = g_malloc0 (buffer_size);
  125. while (!feof (f)
  126. && ((max_rows == 0)
  127. || (stats->n_imported + stats->n_ignored < max_rows)))
  128. {
  129. int l;
  130. // read one line
  131. if (!fgets (line, buffer_size, f))
  132. break; // eof
  133. // now strip the '\n' from the end of the line
  134. l = strlen (line);
  135. if ((l > 0) && (line[l - 1] == '\n'))
  136. line[l - 1] = 0;
  137. // convert line from locale into utf8
  138. line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL);
  139. // parse the line
  140. match_info = NULL; // it seems, that in contrast to documentation, match_info is not alsways set -> g_match_info_free will segfault
  141. if (g_regex_match (regexpat, line_utf8, 0, &match_info))
  142. {
  143. // match found
  144. stats->n_imported++;
  145. // fill in the values
  146. gtk_list_store_append (store, &iter);
  147. FILL_IN_HELPER ("id", ID); /* FIXME: Should "id" be translated? I don't think so. */
  148. FILL_IN_HELPER ("date_opened", DATE_OPENED);
  149. FILL_IN_HELPER ("owner_id", OWNER_ID);
  150. FILL_IN_HELPER ("biing_id", BILLING_ID);
  151. FILL_IN_HELPER ("notes", NOTES);
  152. FILL_IN_HELPER ("date", DATE);
  153. FILL_IN_HELPER ("desc", DESC);
  154. FILL_IN_HELPER ("action", ACTION);
  155. FILL_IN_HELPER ("account", ACCOUNT);
  156. FILL_IN_HELPER ("quantity", QUANTITY);
  157. FILL_IN_HELPER ("price", PRICE);
  158. FILL_IN_HELPER ("disc_type", DISC_TYPE);
  159. FILL_IN_HELPER ("disc_how", DISC_HOW);
  160. FILL_IN_HELPER ("discount", DISCOUNT);
  161. FILL_IN_HELPER ("taxable", TAXABLE);
  162. FILL_IN_HELPER ("taxincluded", TAXINCLUDED);
  163. FILL_IN_HELPER ("tax_table", TAX_TABLE);
  164. FILL_IN_HELPER ("date_posted", DATE_POSTED);
  165. FILL_IN_HELPER ("due_date", DUE_DATE);
  166. FILL_IN_HELPER ("account_posted", ACCOUNT_POSTED);
  167. FILL_IN_HELPER ("memo_posted", MEMO_POSTED);
  168. FILL_IN_HELPER ("accu_splits", ACCU_SPLITS);
  169. }
  170. else
  171. {
  172. // ignore line
  173. stats->n_ignored++;
  174. g_string_append (stats->ignored_lines, line_utf8);
  175. g_string_append_c (stats->ignored_lines, '\n');
  176. }
  177. g_match_info_free (match_info);
  178. match_info = 0;
  179. g_free (line_utf8);
  180. line_utf8 = 0;
  181. }
  182. g_free (line);
  183. line = 0;
  184. g_regex_unref (regexpat);
  185. regexpat = 0;
  186. fclose (f);
  187. if (stats == &stats_fallback)
  188. // stats are not requested -> free the string
  189. g_string_free (stats->ignored_lines, TRUE);
  190. return RESULT_OK;
  191. }
  192. //! \brief try to fix some common errors in the csv representation of invoices
  193. //! * corrects the date format
  194. //! * corrects ambigous values in multi line invoices
  195. //! * ensures customer exists
  196. //! * if quantity is unset, set to 1
  197. //! * if price is unset, delete row
  198. void
  199. gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
  200. GString * info)
  201. {
  202. GtkTreeIter iter;
  203. gboolean valid, row_deleted, row_fixed;
  204. gchar *id, *date_opened, *date_posted, *owner_id, *date, *quantity, *price;
  205. GString *prev_id, *prev_date_opened, *prev_date_posted, *prev_owner_id, *prev_date; // needed to fix multi line invoices
  206. guint dummy;
  207. // allow the call to this function with only GtkListeStore* specified
  208. if (!fixed)
  209. fixed = &dummy;
  210. if (!deleted)
  211. deleted = &dummy;
  212. *fixed = 0;
  213. *deleted = 0;
  214. // init strings
  215. prev_id = g_string_new ("");
  216. prev_date_opened = g_string_new ("");
  217. prev_date_posted = g_string_new ("");
  218. prev_owner_id = g_string_new ("");
  219. prev_date = g_string_new ("");
  220. valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
  221. while (valid)
  222. {
  223. row_deleted = FALSE;
  224. row_fixed = FALSE;
  225. // Walk through the list, reading each row
  226. gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
  227. ID, &id,
  228. DATE_OPENED, &date_opened,
  229. DATE_POSTED, &date_posted,
  230. OWNER_ID, &owner_id,
  231. DATE, &date,
  232. QUANTITY, &quantity, PRICE, &price, -1);
  233. if (strlen (price) == 0)
  234. {
  235. // invalid row (no price given)
  236. // no fix possible -> delete row
  237. gtk_list_store_remove (store, &iter);
  238. row_deleted = TRUE;
  239. g_string_append_printf (info,
  240. _("ROW DELETED, PRICE_NOT_SET: id=%s\n"),
  241. id);
  242. }
  243. else if (strlen (quantity) == 0)
  244. {
  245. // invalid row (no quantity given)
  246. // no fix possible -> delete row
  247. gtk_list_store_remove (store, &iter);
  248. row_deleted = TRUE;
  249. g_string_append_printf (info, _("ROW DELETED, QTY_NOT_SET: id=%s\n"),
  250. id);
  251. }
  252. else
  253. {
  254. if (strlen (id) == 0)
  255. {
  256. // no invoice id specified
  257. if (prev_id->len == 0)
  258. {
  259. // cannot fix -> delete row
  260. gtk_list_store_remove (store, &iter);
  261. row_deleted = TRUE;
  262. g_string_append_printf (info,
  263. _("ROW DELETED, ID_NOT_SET\n"));
  264. }
  265. else
  266. {
  267. // this is a fixable multi line invoice
  268. gtk_list_store_set (store, &iter, ID, prev_id->str, -1);
  269. row_fixed = TRUE;
  270. }
  271. }
  272. else
  273. {
  274. // remember invoice id (to be able to fix multi line invoices)
  275. g_string_assign (prev_id, id);
  276. // new invoice => reset all other fixable entries
  277. g_string_assign (prev_date_opened, "");
  278. g_string_assign (prev_date_posted, "");
  279. g_string_assign (prev_owner_id, "");
  280. g_string_assign (prev_date, "");
  281. }
  282. }
  283. if (!row_deleted)
  284. {
  285. // the row is valid (price and id are valid)
  286. if (strlen (date_opened) == 0)
  287. {
  288. if (prev_date_opened->len == 0)
  289. {
  290. // fix this by using the current date (why is this so complicated?)
  291. gchar temp[20];
  292. GDate *date;
  293. time_t secs;
  294. struct tm now;
  295. time (&secs);
  296. localtime_r (&secs, &now);
  297. date =
  298. g_date_new_dmy (now.tm_mday, now.tm_mon + 1,
  299. now.tm_year + 1900);
  300. g_date_strftime (temp, 20, "%x", date); // create a locale specific date string
  301. g_string_assign (prev_date_opened, temp);
  302. g_date_free (date);
  303. }
  304. // fix this by using the previous date_opened value (multi line invoice)
  305. gtk_list_store_set (store, &iter, DATE_OPENED,
  306. prev_date_opened->str, -1);
  307. row_fixed = TRUE;
  308. }
  309. else
  310. {
  311. // remember date_opened (to be able to fix multi line invoices)
  312. g_string_assign (prev_date_opened, date_opened);
  313. }
  314. // date_opened is valid
  315. if (strlen (date_posted) == 0)
  316. {
  317. if (prev_date_posted->len == 0)
  318. {
  319. // this invoice will have to get posted manually
  320. }
  321. else
  322. {
  323. // multi line invoice => fix it
  324. gtk_list_store_set (store, &iter, DATE_POSTED,
  325. prev_date_posted->str, -1);
  326. row_fixed = TRUE;
  327. }
  328. }
  329. else
  330. {
  331. // remember date_opened (to be able to fix multi line invoices)
  332. g_string_assign (prev_date_posted, date_posted);
  333. }
  334. // date_posted is valid
  335. if (strlen (quantity) == 0)
  336. {
  337. // quantity is unset => set to 1
  338. gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
  339. row_fixed = TRUE;
  340. }
  341. // quantity is valid
  342. if (strlen (owner_id) == 0)
  343. {
  344. if (prev_owner_id->len == 0)
  345. {
  346. // no customer given and not fixable => delete row
  347. gtk_list_store_remove (store, &iter);
  348. row_deleted = TRUE;
  349. g_string_append_printf (info,
  350. _("ROW DELETED, VENDOR_NOT_SET: id=%s\n"),
  351. id);
  352. }
  353. else
  354. {
  355. gtk_list_store_set (store, &iter, owner_id,
  356. prev_owner_id->str, -1);
  357. row_fixed = TRUE;
  358. }
  359. }
  360. else
  361. {
  362. // remember owner_id
  363. g_string_assign (prev_owner_id, owner_id);
  364. }
  365. // now check, if customer exists
  366. if (!gnc_search_vendor_on_id
  367. (gnc_get_current_book (), prev_owner_id->str))
  368. {
  369. // customer not found => delete row
  370. gtk_list_store_remove (store, &iter);
  371. row_deleted = TRUE;
  372. g_string_append_printf (info,
  373. _("ROW DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"),
  374. id);
  375. }
  376. // owner_id is valid
  377. }
  378. g_free (id);
  379. g_free (date_opened);
  380. g_free (date_posted);
  381. g_free (owner_id);
  382. g_free (date);
  383. g_free (quantity);
  384. g_free (price);
  385. if (row_deleted)
  386. {
  387. (*deleted)++;
  388. // reset all remembered values
  389. g_string_assign (prev_id, "");
  390. g_string_assign (prev_date_opened, "");
  391. g_string_assign (prev_date_posted, "");
  392. g_string_assign (prev_owner_id, "");
  393. g_string_assign (prev_date, "");
  394. }
  395. else if (row_fixed)
  396. (*fixed)++;
  397. valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
  398. }
  399. // deallocate strings
  400. g_string_free (prev_id, TRUE);
  401. g_string_free (prev_date_opened, TRUE);
  402. g_string_free (prev_date_posted, TRUE);
  403. g_string_free (prev_owner_id, TRUE);
  404. g_string_free (prev_date, TRUE);
  405. if (info && (info->len > 0))
  406. {
  407. g_string_prepend (info, "\n\n");
  408. g_string_prepend (info, _("These rows were deleted:"));
  409. }
  410. }
  411. /***********************************************************************
  412. * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...)
  413. * rather than in here? But that is more concerned with ensuring the csv is consistent.
  414. * @param GtkListStore *store
  415. * @param guint *n_invoices_created
  416. * @param guint *n_invoices_updated
  417. * @return void
  418. ***********************************************************************/
  419. void
  420. gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
  421. guint * n_invoices_created,
  422. guint * n_invoices_updated, gchar * type)
  423. {
  424. gboolean valid;
  425. GtkTreeIter iter;
  426. gchar *id, *date_opened, *owner_id, *biing_id, *notes;
  427. gchar *date, *desc, *action, *account, *quantity, *price, *disc_type,
  428. *disc_how, *discount, *taxable, *taxincluded, *tax_table;
  429. gchar *date_posted, *due_date, *account_posted, *memo_posted,
  430. *accumulatesplits;
  431. guint dummy;
  432. GncInvoice *invoice;
  433. GncOrder *order;
  434. GncEntry *entry;
  435. gint day, month, year;
  436. gnc_numeric n;
  437. GncOwner *owner;
  438. Account *acc;
  439. enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
  440. GtkWidget *dialog;
  441. Timespec today;
  442. GncPluginPage *new_page;
  443. InvoiceWindow *iw;
  444. // these arguments are needed
  445. g_return_if_fail (store && book);
  446. // allow to call this function without statistics
  447. if (!n_invoices_created)
  448. n_invoices_created = &dummy;
  449. if (!n_invoices_updated)
  450. n_invoices_updated = &dummy;
  451. *n_invoices_created = 0;
  452. *n_invoices_updated = 0;
  453. invoice = NULL;
  454. order = NULL;
  455. update = NO;
  456. valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
  457. while (valid)
  458. {
  459. // Walk through the list, reading each row
  460. gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, DATE_OPENED, &date_opened, DATE_POSTED, &date_posted, // if autoposting requested
  461. DUE_DATE, &due_date, // if autoposting requested
  462. ACCOUNT_POSTED, &account_posted, // if autoposting requested
  463. MEMO_POSTED, &memo_posted, // if autoposting requested
  464. ACCU_SPLITS, &accumulatesplits, // if autoposting requested
  465. OWNER_ID, &owner_id,
  466. BILLING_ID, &biing_id,
  467. NOTES, &notes,
  468. DATE, &date,
  469. DESC, &desc,
  470. ACTION, &action,
  471. ACCOUNT, &account,
  472. QUANTITY, &quantity,
  473. PRICE, &price,
  474. DISC_TYPE, &disc_type,
  475. DISC_HOW, &disc_how,
  476. DISCOUNT, &discount,
  477. TAXABLE, &taxable,
  478. TAXINCLUDED, &taxincluded,
  479. TAX_TABLE, &tax_table, -1);
  480. // TODO: Assign a new invoice number if one is absent. BUT we don't want to assign a new invoice for every line!!
  481. // so we'd have to flag this up somehow or add an option in the import GUI. The former implies that we make
  482. // an assumption about what the importer (person) wants to do. It seems resonable that a CSV file full of items with
  483. // If an invoice exists then we add to it in this current schema.
  484. // no predefined invoice number is a new invoice that's in need of a new number.
  485. // This was not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all
  486. // It should be easier to copy an invoice with a new ID than to go through all this malarky.
  487. if (g_ascii_strcasecmp (type, "BILL"))
  488. invoice = gnc_search_bill_on_id (book, id);
  489. else if (g_ascii_strcasecmp (type, "INVOICE"))
  490. invoice = gnc_search_invoice_on_id (book, id);
  491. if (!invoice)
  492. {
  493. // new invoice
  494. invoice = gncInvoiceCreate (book);
  495. gncInvoiceSetID (invoice, id);
  496. owner = gncOwnerNew ();
  497. if (g_ascii_strcasecmp (type, "BILL") == 0)
  498. gncOwnerInitVendor (owner,
  499. gnc_search_vendor_on_id (book, owner_id));
  500. else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
  501. gncOwnerInitCustomer (owner,
  502. gnc_search_customer_on_id (book, owner_id));
  503. gncInvoiceSetOwner (invoice, owner);
  504. gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner)); // Set the invoice currency based on the owner
  505. if (!(g_ascii_strcasecmp (type, ""))) // If a date is specified in CSV
  506. {
  507. qof_scan_date (date_opened, &day, &month, &year);
  508. gncInvoiceSetDateOpened (invoice,
  509. gnc_dmy2timespec (day, month, year));
  510. }
  511. else // If no date in CSV
  512. {
  513. time_t now = time (NULL);
  514. Timespec now_timespec;
  515. timespecFromTime_t (&now_timespec, now);
  516. gncInvoiceSetDateOpened (invoice, now_timespec);
  517. }
  518. gncInvoiceSetBillingID (invoice, biing_id);
  519. gncInvoiceSetNotes (invoice, notes);
  520. gncInvoiceSetActive (invoice, TRUE);
  521. //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
  522. (*n_invoices_created)++;
  523. update = YES;
  524. // Open the newly created invoice(s) in a tab. Could be made optional?
  525. iw = gnc_ui_invoice_edit (invoice);
  526. new_page = gnc_plugin_page_invoice_new (iw);
  527. }
  528. // I want to warn the user that an existing billvoice exists, but not every
  529. // time.
  530. // An import can contain many lines usually referring to the same invoice.
  531. // NB: Posted invoices are NEVER updated.
  532. else // if invoice exists
  533. {
  534. if (gncInvoiceIsPosted (invoice)) // Is it already posted?
  535. {
  536. valid =
  537. gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
  538. continue; // If already posted then never import
  539. }
  540. if (update != YES) // Pop up a dialog to ask if updates are the expected action
  541. {
  542. dialog = gtk_message_dialog_new (NULL,
  543. GTK_DIALOG_MODAL,
  544. GTK_MESSAGE_ERROR,
  545. GTK_BUTTONS_YES_NO,
  546. "%s",
  547. _("Are you sure you have bills/invoices to update?"));
  548. update = gtk_dialog_run (GTK_DIALOG (dialog));
  549. gtk_widget_destroy (dialog);
  550. if (update == NO)
  551. {
  552. // Cleanup and leave
  553. g_free (id);
  554. g_free (date_opened);
  555. g_free (owner_id);
  556. g_free (biing_id);
  557. g_free (notes);
  558. g_free (date);
  559. g_free (desc);
  560. g_free (action);
  561. g_free (account);
  562. g_free (quantity);
  563. g_free (price);
  564. g_free (disc_type);
  565. g_free (disc_how);
  566. g_free (discount);
  567. g_free (taxable);
  568. g_free (taxincluded);
  569. g_free (tax_table);
  570. g_free (date_posted);
  571. g_free (due_date);
  572. g_free (account_posted);
  573. g_free (memo_posted);
  574. g_free (accumulatesplits);
  575. return;
  576. }
  577. }
  578. (*n_invoices_updated)++;
  579. }
  580. // add entry to invoice/bill
  581. entry = gncEntryCreate (book);
  582. qof_scan_date (date, &day, &month, &year);
  583. gncEntrySetDate (entry, gnc_dmy2timespec (day, month, year));
  584. timespecFromTime_t (&today, time (NULL)); // set today to the current date
  585. gncEntrySetDateEntered (entry, today);
  586. gncEntrySetDescription (entry, desc);
  587. gncEntrySetAction (entry, action);
  588. n = gnc_numeric_zero ();
  589. gnc_exp_parser_parse (quantity, &n, NULL);
  590. gncEntrySetQuantity (entry, n);
  591. acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
  592. account);
  593. if (g_ascii_strcasecmp (type, "BILL") == 0)
  594. {
  595. gncEntrySetBillAccount (entry, acc);
  596. n = gnc_numeric_zero ();
  597. gnc_exp_parser_parse (price, &n, NULL);
  598. gncEntrySetBillPrice (entry, n);
  599. gncEntrySetBillTaxable (entry, text2bool (taxable));
  600. gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
  601. gncEntrySetBillTaxTable (entry,
  602. gncTaxTableLookupByName (book, tax_table));
  603. n = gnc_numeric_zero ();
  604. gnc_exp_parser_parse (discount, &n, NULL);
  605. gncBillAddEntry (invoice, entry);
  606. }
  607. else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
  608. {
  609. gncEntrySetNotes (entry, notes);
  610. gncEntrySetInvAccount (entry, acc);
  611. n = gnc_numeric_zero ();
  612. gnc_exp_parser_parse (price, &n, NULL);
  613. gncEntrySetInvPrice (entry, n);
  614. gncEntrySetInvTaxable (entry, text2bool (taxable));
  615. gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
  616. gncEntrySetInvTaxTable (entry,
  617. gncTaxTableLookupByName (book, tax_table));
  618. n = gnc_numeric_zero ();
  619. gnc_exp_parser_parse (discount, &n, NULL);
  620. gncEntrySetInvDiscount (entry, n);
  621. gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
  622. gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
  623. gncInvoiceAddEntry (invoice, entry);
  624. }
  625. valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
  626. // handle auto posting of invoices
  627. {
  628. gchar *new_id = NULL;
  629. Transaction *tnx;
  630. if (valid)
  631. gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
  632. if (g_strcmp0 (id, new_id) != 0)
  633. {
  634. // the next invoice id is different => try to autopost this invoice
  635. if (qof_scan_date (date_posted, &day, &month, &year))
  636. {
  637. // autopost this invoice
  638. Timespec d1, d2;
  639. d1 = gnc_dmy2timespec (day, month, year);
  640. qof_scan_date (due_date, &day, &month, &year); // obtains the due date, or leaves it at date_posted
  641. d2 = gnc_dmy2timespec (day, month, year);
  642. acc = gnc_account_lookup_for_register
  643. (gnc_get_current_root_account (), account_posted);
  644. tnx = gncInvoicePostToAccount (invoice, acc, &d1, &d2,
  645. memo_posted,
  646. text2bool (accumulatesplits));
  647. }
  648. }
  649. g_free (new_id);
  650. }
  651. // cleanup
  652. g_free (id);
  653. g_free (date_opened);
  654. g_free (owner_id);
  655. g_free (biing_id);
  656. g_free (notes);
  657. g_free (date);
  658. g_free (desc);
  659. g_free (action);
  660. g_free (account);
  661. g_free (quantity);
  662. g_free (price);
  663. g_free (disc_type);
  664. g_free (disc_how);
  665. g_free (discount);
  666. g_free (taxable);
  667. g_free (taxincluded);
  668. g_free (tax_table);
  669. g_free (date_posted);
  670. g_free (due_date);
  671. g_free (account_posted);
  672. g_free (memo_posted);
  673. g_free (accumulatesplits);
  674. }
  675. }