GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-invoice.c
1 /*
2  * dialog-invoice.c -- Dialog for Invoice entry
3  * Copyright (C) 2001,2002,2006 Derek Atkins
4  * Author: Derek Atkins <[email protected]>
5  *
6  * Copyright (c) 2005,2006 David Hampton <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, contact:
20  *
21  * Free Software Foundation Voice: +1-617-542-5942
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
23  * Boston, MA 02110-1301, USA [email protected]
24  */
25 
26 #include "config.h"
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <libguile.h>
31 #include "swig-runtime.h"
32 
33 #include "qof.h"
34 
35 #include <gnc-gdate-utils.h>
36 #include "dialog-utils.h"
37 #include "gnc-component-manager.h"
38 #include "gnc-ui.h"
39 #include "gnc-gui-query.h"
40 #include "gnc-prefs.h"
41 #include "gnc-ui-util.h"
42 #include "gnc-date-edit.h"
43 #include "gnc-amount-edit.h"
44 #include "gnucash-sheet.h"
45 #include "window-report.h"
46 #include "dialog-search.h"
47 #include "search-param.h"
48 #include "gnc-session.h"
49 #include "gncOwner.h"
50 #include "gncInvoice.h"
51 #include "gncInvoiceP.h"
52 
53 #include "gncEntryLedger.h"
54 
55 #include "gnc-plugin-page.h"
56 #include "gnc-general-search.h"
57 #include "dialog-date-close.h"
58 #include "dialog-invoice.h"
59 #include "dialog-job.h"
60 #include "business-gnome-utils.h"
61 #include "dialog-payment.h"
62 #include "dialog-tax-table.h"
63 #include "dialog-billterms.h"
64 #include "dialog-account.h"
65 #include "guile-mappings.h"
66 #include "dialog-dup-trans.h"
67 
68 #include "dialog-query-view.h"
69 
70 #include "gnc-plugin-business.h"
72 #include "gnc-main-window.h"
73 
74 #include "dialog-transfer.h"
75 
76 /* Disable -Waddress. GCC 4.2 warns (and fails to compile with -Werror) when
77  * passing the address of a guid on the stack to QOF_BOOK_LOOKUP_ENTITY via
78  * gncInvoiceLookup and friends. When the macro gets inlined, the compiler
79  * emits a warning that the guid null pointer test is always true.
80  */
81 #if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 2)
82 # pragma GCC diagnostic warning "-Waddress"
83 #endif
84 
85 #define DIALOG_NEW_INVOICE_CM_CLASS "dialog-new-invoice"
86 #define DIALOG_VIEW_INVOICE_CM_CLASS "dialog-view-invoice"
87 
88 #define GNC_PREFS_GROUP_SEARCH "dialogs.business.invoice-search"
89 #define GNC_PREF_NOTIFY_WHEN_DUE "notify-when-due"
90 #define GNC_PREF_ACCUM_SPLITS "accumulate-splits"
91 #define GNC_PREF_DAYS_IN_ADVANCE "days-in-advance"
92 
93 void gnc_invoice_window_ok_cb (GtkWidget *widget, gpointer data);
94 void gnc_invoice_window_cancel_cb (GtkWidget *widget, gpointer data);
95 void gnc_invoice_window_help_cb (GtkWidget *widget, gpointer data);
96 void gnc_invoice_type_toggled_cb (GtkWidget *widget, gpointer data);
97 void gnc_invoice_id_changed_cb (GtkWidget *widget, gpointer data);
98 void gnc_invoice_terms_changed_cb (GtkWidget *widget, gpointer data);
99 
100 #define ENUM_INVOICE_TYPE(_) \
101  _(NEW_INVOICE, ) \
102  _(MOD_INVOICE, ) \
103  _(DUP_INVOICE, ) \
104  _(EDIT_INVOICE, ) \
105  _(VIEW_INVOICE, )
106 
107 DEFINE_ENUM(InvoiceDialogType, ENUM_INVOICE_TYPE)
108 AS_STRING_DEC(InvoiceDialogType, ENUM_INVOICE_TYPE)
109 FROM_STRING_DEC(InvoiceDialogType, ENUM_INVOICE_TYPE)
110 
111 FROM_STRING_FUNC(InvoiceDialogType, ENUM_INVOICE_TYPE)
112 AS_STRING_FUNC(InvoiceDialogType, ENUM_INVOICE_TYPE)
113 
115 {
116  QofBook * book;
117  GncOwner * owner;
118  QofQuery * q;
119  GncOwner owner_def;
120 };
121 
122 
129 {
130  GtkBuilder * builder;
131 
132  GtkWidget * dialog; /* Used by 'New Invoice Window' */
133  GncPluginPage *page; /* Used by 'Edit Invoice' Page */
134 
135  /* Summary Bar Widgets */
136  GtkWidget * total_label;
137  GtkWidget * total_cash_label;
138  GtkWidget * total_charge_label;
139  GtkWidget * total_subtotal_label;
140  GtkWidget * total_tax_label;
141 
142  /* Data Widgets */
143  GtkWidget * info_label; /*Default in glade is "Invoice Information"*/
144  GtkWidget * id_label; /* Default in glade is Invoice ID */
145  GtkWidget * type_label;
146  GtkWidget * type_hbox;
147  GtkWidget * type_choice;
148  GtkWidget * id_entry;
149  GtkWidget * notes_text;
150  GtkWidget * opened_date;
151  GtkWidget * posted_date_hbox;
152  GtkWidget * posted_date;
153  GtkWidget * active_check;
154 
155  GtkWidget * owner_box;
156  GtkWidget * owner_label;
157  GtkWidget * owner_choice;
158  GtkWidget * job_label;
159  GtkWidget * job_box;
160  GtkWidget * job_choice;
161  GtkWidget * billing_id_entry;
162  GtkWidget * terms_menu;
163 
164  /* Project Widgets (used for Bills only) */
165  GtkWidget * proj_frame;
166  GtkWidget * proj_cust_box;
167  GtkWidget * proj_cust_choice;
168  GtkWidget * proj_job_box;
169  GtkWidget * proj_job_choice;
170 
171  /* Expense Voucher Widgets */
172  GtkWidget * to_charge_frame;
173  GtkWidget * to_charge_edit;
174 
175  gint width;
176 
177  GncBillTerm * terms;
178  GnucashRegister * reg;
179  GncEntryLedger * ledger;
180 
181  invoice_sort_type_t last_sort;
182 
183  InvoiceDialogType dialog_type;
184  GncGUID invoice_guid;
185  gboolean is_credit_note;
186  gint component_id;
187  QofBook * book;
188  GncInvoice * created_invoice;
189  GncOwner owner;
190  GncOwner job;
191 
192  GncOwner proj_cust;
193  GncOwner proj_job;
194 
195  /* for Unposting */
196  gboolean reset_tax_tables;
197 };
198 
199 /* Forward definitions for CB functions */
200 void gnc_invoice_window_closeCB (GtkWidget *widget, gpointer data);
201 void gnc_invoice_window_active_toggled_cb (GtkWidget *widget, gpointer data);
202 gboolean gnc_invoice_window_leave_notes_cb (GtkWidget *widget, GdkEventFocus *event, gpointer data);
203 
204 #define INV_WIDTH_PREFIX "invoice_reg"
205 #define BILL_WIDTH_PREFIX "bill_reg"
206 #define VOUCHER_WIDTH_PREFIX "voucher_reg"
207 
208 static void gnc_invoice_update_window (InvoiceWindow *iw, GtkWidget *widget);
209 static InvoiceWindow * gnc_ui_invoice_modify (GncInvoice *invoice);
210 
211 /*******************************************************************************/
212 /* FUNCTIONS FOR ACCESSING DATA STRUCTURE FIELDS */
213 
214 static GtkWidget *
215 iw_get_window (InvoiceWindow *iw)
216 {
217  if (iw->page)
218  return gnc_plugin_page_get_window(iw->page);
219  return iw->dialog;
220 }
221 
222 GtkWidget *
223 gnc_invoice_get_register(InvoiceWindow *iw)
224 {
225  if (iw)
226  return (GtkWidget *)iw->reg;
227  return NULL;
228 }
229 
230 /*******************************************************************************/
231 /* FUNCTIONS FOR UNPOSTING */
232 
233 static gboolean
234 iw_ask_unpost (InvoiceWindow *iw)
235 {
236  GtkWidget *dialog;
237  GtkToggleButton *toggle;
238  GtkBuilder *builder;
239  gint response;
240 
241  builder = gtk_builder_new();
242  gnc_builder_add_from_file (builder, "dialog-invoice.glade", "Unpost Message Dialog");
243  dialog = GTK_WIDGET (gtk_builder_get_object (builder, "Unpost Message Dialog"));
244  toggle = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder, "yes_tt_reset"));
245 
246  gtk_window_set_transient_for (GTK_WINDOW(dialog),
247  GTK_WINDOW(iw_get_window(iw)));
248 
249  iw->reset_tax_tables = FALSE;
250 
251  gtk_widget_show_all(dialog);
252 
253  response = gtk_dialog_run(GTK_DIALOG(dialog));
254  if (response == GTK_RESPONSE_OK)
255  iw->reset_tax_tables =
256  gtk_toggle_button_get_active(toggle);
257 
258  gtk_widget_destroy(dialog);
259  g_object_unref(G_OBJECT(builder));
260 
261  return (response == GTK_RESPONSE_OK);
262 }
263 
264 /*******************************************************************************/
265 /* INVOICE WINDOW */
266 
267 static GncInvoice *
268 iw_get_invoice (InvoiceWindow *iw)
269 {
270  if (!iw)
271  return NULL;
272 
273  return gncInvoiceLookup (iw->book, &iw->invoice_guid);
274 }
275 
276 static void
277 set_gncEntry_switch_type (gpointer data, gpointer user_data)
278 {
279  GncEntry *entry = data;
280  //g_warning("Modifying date for entry with desc=\"%s\"", gncEntryGetDescription(entry));
281 
283 }
284 
285 static void gnc_ui_to_invoice (InvoiceWindow *iw, GncInvoice *invoice)
286 {
287  GtkTextBuffer* text_buffer;
288  GtkTextIter start, end;
289  gchar *text;
290  Timespec ts;
291  gboolean is_credit_note = gncInvoiceGetIsCreditNote (invoice);
292 
293  if (iw->dialog_type == VIEW_INVOICE)
294  return;
295 
296  gnc_suspend_gui_refresh ();
297 
298  gncInvoiceBeginEdit (invoice);
299 
300  if (iw->active_check)
301  gncInvoiceSetActive (invoice, gtk_toggle_button_get_active
302  (GTK_TOGGLE_BUTTON (iw->active_check)));
303 
304  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(iw->notes_text));
305  gtk_text_buffer_get_bounds (text_buffer, &start, &end);
306  text = gtk_text_buffer_get_text (text_buffer, &start, &end, FALSE);
307  gncInvoiceSetNotes (invoice, text);
308 
309  if (iw->to_charge_edit)
310  gncInvoiceSetToChargeAmount (invoice,
311  gnc_amount_edit_get_amount
312  (GNC_AMOUNT_EDIT (iw->to_charge_edit)));
313 
314  /* Only set these values for NEW/MOD INVOICE types */
315  if (iw->dialog_type != EDIT_INVOICE)
316  {
317  gncInvoiceSetID (invoice, gtk_editable_get_chars
318  (GTK_EDITABLE (iw->id_entry), 0, -1));
319  gncInvoiceSetBillingID (invoice, gtk_editable_get_chars
320  (GTK_EDITABLE (iw->billing_id_entry), 0, -1));
321  gncInvoiceSetTerms (invoice, iw->terms);
322 
323  ts = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (iw->opened_date));
324  gncInvoiceSetDateOpened (invoice, ts);
325 
326  gnc_owner_get_owner (iw->owner_choice, &(iw->owner));
327  if (iw->job_choice)
328  gnc_owner_get_owner (iw->job_choice, &(iw->job));
329 
330  /* Only set the job if we've actually got one */
331  if (gncOwnerGetJob (&(iw->job)))
332  gncInvoiceSetOwner (invoice, &(iw->job));
333  else
334  gncInvoiceSetOwner (invoice, &(iw->owner));
335 
336  /* Set the invoice currency based on the owner */
337  gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (&iw->owner));
338 
339  /* Only set the BillTo if we've actually got one */
340  if (gncOwnerGetJob (&iw->proj_job))
341  gncInvoiceSetBillTo (invoice, &iw->proj_job);
342  else
343  gncInvoiceSetBillTo (invoice, &iw->proj_cust);
344  }
345 
346  /* Document type can only be modified for a new or duplicated invoice/credit note */
347  if (iw->dialog_type == NEW_INVOICE || iw->dialog_type == DUP_INVOICE)
348  gncInvoiceSetIsCreditNote (invoice, iw->is_credit_note);
349 
350  /* If the document type changed on a duplicated invoice,
351  * its entries should be updated
352  */
353  if (iw->dialog_type == DUP_INVOICE && iw->is_credit_note != is_credit_note)
354  {
355  g_list_foreach(gncInvoiceGetEntries(invoice),
356  &set_gncEntry_switch_type, NULL);
357  }
358 
359  gncInvoiceCommitEdit (invoice);
360  gnc_resume_gui_refresh ();
361 }
362 
363 static gboolean
364 gnc_invoice_window_verify_ok (InvoiceWindow *iw)
365 {
366  const char *res;
367  gchar *string;
368 
369  /* save the current entry in the ledger? */
370  if (!gnc_entry_ledger_check_close (iw_get_window(iw), iw->ledger))
371  return FALSE;
372 
373  /* Check the Owner */
374  gnc_owner_get_owner (iw->owner_choice, &(iw->owner));
375  res = gncOwnerGetName (&(iw->owner));
376  if (res == NULL || g_strcmp0 (res, "") == 0)
377  {
378  gnc_error_dialog (iw_get_window(iw), "%s",
379  /* Translators: In this context,
380  * 'Billing information' maps to the
381  * label in the frame and means
382  * e.g. customer i.e. the company being
383  * invoiced. */
384  _("You need to supply Billing Information."));
385  return FALSE;
386  }
387 
388  /* Check the ID; set one if necessary */
389  res = gtk_entry_get_text (GTK_ENTRY (iw->id_entry));
390  if (g_strcmp0 (res, "") == 0)
391  {
392  /* Invoices and bills have separate counters.
393  Therefore we pass the GncOwer to gncInvoiceNextID
394  so it knows whether we are creating a bill
395  or an invoice. */
396  string = gncInvoiceNextID(iw->book, &(iw->owner));
397  gtk_entry_set_text (GTK_ENTRY (iw->id_entry), string);
398  g_free(string);
399  }
400 
401  return TRUE;
402 }
403 
404 static gboolean
405 gnc_invoice_window_ok_save (InvoiceWindow *iw)
406 {
407  if (!gnc_invoice_window_verify_ok (iw))
408  return FALSE;
409 
410  {
411  GncInvoice *invoice = iw_get_invoice (iw);
412  if (invoice)
413  {
414  gnc_ui_to_invoice (iw, invoice);
415  }
416  /* Save the invoice to return it later. */
417  iw->created_invoice = invoice;
418  }
419  return TRUE;
420 }
421 
422 void
423 gnc_invoice_window_ok_cb (GtkWidget *widget, gpointer data)
424 {
425  InvoiceWindow *iw = data;
426 
427  if (!gnc_invoice_window_ok_save (iw))
428  return;
429 
430  /* Ok, we don't need this anymore */
431  iw->invoice_guid = *guid_null ();
432 
433  /* if this is a new or duplicated invoice, and created_invoice is NON-NULL,
434  * then open up a new window with the invoice. This used to be done
435  * in gnc_ui_invoice_new() but cannot be done anymore
436  */
437  if ((iw->dialog_type == NEW_INVOICE || iw->dialog_type == DUP_INVOICE)
438  && iw->created_invoice)
439  gnc_ui_invoice_edit (iw->created_invoice);
440 
441  gnc_close_gui_component (iw->component_id);
442 }
443 
444 void
445 gnc_invoice_window_cancel_cb (GtkWidget *widget, gpointer data)
446 {
447  InvoiceWindow *iw = data;
448 
449  gnc_close_gui_component (iw->component_id);
450 }
451 
452 void
453 gnc_invoice_window_help_cb (GtkWidget *widget, gpointer data)
454 {
455  gnc_gnome_help(HF_HELP, HL_USAGE_INVOICE);
456 }
457 
458 void
459 gnc_invoice_window_destroy_cb (GtkWidget *widget, gpointer data)
460 {
461  InvoiceWindow *iw = data;
462  GncInvoice *invoice = iw_get_invoice (iw);
463 
464  gnc_suspend_gui_refresh ();
465 
466  if ((iw->dialog_type == NEW_INVOICE || iw->dialog_type == DUP_INVOICE)
467  && invoice != NULL)
468  {
469  gncInvoiceRemoveEntries (invoice);
470  gncInvoiceBeginEdit (invoice);
471  gncInvoiceDestroy (invoice);
472  iw->invoice_guid = *guid_null ();
473  }
474 
475  gnc_entry_ledger_destroy (iw->ledger);
476  gnc_unregister_gui_component (iw->component_id);
477  gtk_widget_destroy(widget);
478  gnc_resume_gui_refresh ();
479 
480  g_free (iw);
481 }
482 
483 void
484 gnc_invoice_window_closeCB (GtkWidget *widget, gpointer data)
485 {
486  gnc_invoice_window_ok_cb (widget, data);
487 }
488 
489 void
490 gnc_invoice_window_editCB (GtkWidget *widget, gpointer data)
491 {
492  InvoiceWindow *iw = data;
493  GncInvoice *invoice = iw_get_invoice (iw);
494 
495  if (invoice)
496  gnc_ui_invoice_modify (invoice);
497 }
498 
499 void
500 gnc_invoice_window_duplicateInvoiceCB (GtkWidget *widget, gpointer data)
501 {
502  InvoiceWindow *iw = data;
503  GncInvoice *invoice = iw_get_invoice (iw);
504 
505  if (invoice)
506  gnc_ui_invoice_duplicate (invoice, TRUE, NULL);
507 }
508 
509 void gnc_invoice_window_entryUpCB (GtkWidget *widget, gpointer data)
510 {
511  InvoiceWindow *iw = data;
512  if (!iw || !iw->ledger)
513  return;
514 
515  gnc_entry_ledger_move_current_entry_updown(iw->ledger, TRUE);
516 }
517 void gnc_invoice_window_entryDownCB (GtkWidget *widget, gpointer data)
518 {
519  InvoiceWindow *iw = data;
520  if (!iw || !iw->ledger)
521  return;
522 
523  gnc_entry_ledger_move_current_entry_updown(iw->ledger, FALSE);
524 }
525 
526 void
527 gnc_invoice_window_recordCB (GtkWidget *widget, gpointer data)
528 {
529  InvoiceWindow *iw = data;
530 
531  if (!iw || !iw->ledger)
532  return;
533 
534  if (!gnc_entry_ledger_commit_entry (iw->ledger))
535  return;
536 
537  gnucash_register_goto_next_virt_row (iw->reg);
538 }
539 
540 void
541 gnc_invoice_window_cancelCB (GtkWidget *widget, gpointer data)
542 {
543  InvoiceWindow *iw = data;
544 
545  if (!iw || !iw->ledger)
546  return;
547 
548  gnc_entry_ledger_cancel_cursor_changes (iw->ledger);
549 }
550 
551 void
552 gnc_invoice_window_deleteCB (GtkWidget *widget, gpointer data)
553 {
554  InvoiceWindow *iw = data;
555  GncEntry *entry;
556 
557  if (!iw || !iw->ledger)
558  return;
559 
560  /* get the current entry based on cursor position */
561  entry = gnc_entry_ledger_get_current_entry (iw->ledger);
562  if (!entry)
563  {
564  gnc_entry_ledger_cancel_cursor_changes (iw->ledger);
565  return;
566  }
567 
568  /* deleting the blank entry just cancels */
569  if (entry == gnc_entry_ledger_get_blank_entry (iw->ledger))
570  {
571  gnc_entry_ledger_cancel_cursor_changes (iw->ledger);
572  return;
573  }
574 
575  /* Verify that the user really wants to delete this entry */
576  {
577  const char *message = _("Are you sure you want to delete the "
578  "selected entry?");
579  const char *order_warn = _("This entry is attached to an order and "
580  "will be deleted from that as well!");
581  char *msg;
582  gboolean result;
583 
584  if (gncEntryGetOrder (entry))
585  msg = g_strconcat (message, "\n\n", order_warn, (char *)NULL);
586  else
587  msg = g_strdup (message);
588 
589  result = gnc_verify_dialog (iw_get_window(iw), FALSE, "%s", msg);
590  g_free (msg);
591 
592  if (!result)
593  return;
594  }
595 
596  /* Yep, let's delete */
597  gnc_entry_ledger_delete_current_entry (iw->ledger);
598  return;
599 }
600 
601 void
602 gnc_invoice_window_duplicateCB (GtkWidget *widget, gpointer data)
603 {
604  InvoiceWindow *iw = data;
605 
606  if (!iw || !iw->ledger)
607  return;
608 
609  gnc_entry_ledger_duplicate_current_entry (iw->ledger);
610 }
611 
612 void
613 gnc_invoice_window_blankCB (GtkWidget *widget, gpointer data)
614 {
615  InvoiceWindow *iw = data;
616 
617  if (!iw || !iw->ledger)
618  return;
619 
620  if (!gnc_entry_ledger_commit_entry (iw->ledger))
621  return;
622 
623  {
624  VirtualCellLocation vcell_loc;
625  GncEntry *blank;
626 
627  blank = gnc_entry_ledger_get_blank_entry (iw->ledger);
628  if (blank == NULL)
629  return;
630 
631  if (gnc_entry_ledger_get_entry_virt_loc (iw->ledger, blank, &vcell_loc))
632  gnucash_register_goto_virt_cell (iw->reg, vcell_loc);
633  }
634 }
635 
636 static void
637 gnc_invoice_window_print_invoice(GncInvoice *invoice)
638 {
639  SCM func, arg, arg2;
640  SCM args = SCM_EOL;
641  int report_id;
642  const char *reportname = gnc_plugin_business_get_invoice_printreport();
643 
644  g_return_if_fail (invoice);
645  if (!reportname)
646  reportname = "5123a759ceb9483abf2182d01c140e8d"; // fallback if the option lookup failed
647 
648  func = scm_c_eval_string ("gnc:invoice-report-create");
649  g_return_if_fail (scm_is_procedure (func));
650 
651  arg = SWIG_NewPointerObj(invoice, SWIG_TypeQuery("_p__gncInvoice"), 0);
652  arg2 = scm_from_utf8_string(reportname);
653  args = scm_cons2 (arg, arg2, args);
654 
655  /* scm_gc_protect_object(func); */
656 
657  arg = scm_apply (func, args, SCM_EOL);
658  g_return_if_fail (scm_is_exact (arg));
659  report_id = scm_to_int (arg);
660 
661  /* scm_gc_unprotect_object(func); */
662  if (report_id >= 0)
663  reportWindow (report_id);
664 }
665 void
666 gnc_invoice_window_printCB (GtkWidget *unused_widget, gpointer data)
667 {
668  InvoiceWindow *iw = data;
669  gnc_invoice_window_print_invoice(iw_get_invoice (iw));
670 }
671 
672 static gboolean
673 gnc_dialog_post_invoice(InvoiceWindow *iw, char *message,
674  Timespec *ddue, Timespec *postdate,
675  char **memo, Account **acc, gboolean *accumulate)
676 {
677  GncInvoice *invoice;
678  char *ddue_label, *post_label, *acct_label, *question_label;
679  GList * acct_types = NULL;
680  GList * acct_commodities = NULL;
681  QofInstance *owner_inst;
682  EntryList *entries, *entries_iter;
683 
684  invoice = iw_get_invoice (iw);
685  if (!invoice)
686  return FALSE;
687 
688  ddue_label = _("Due Date");
689  post_label = _("Post Date");
690  acct_label = _("Post to Account");
691  question_label = _("Accumulate Splits?");
692 
693  /* Determine the type of account to post to */
694  acct_types = gncOwnerGetAccountTypesList (&(iw->owner));
695 
696  /* Determine which commodity we're working with */
697  acct_commodities = gncOwnerGetCommoditiesList(&(iw->owner));
698 
699  /* Get the invoice entries */
700  entries = gncInvoiceGetEntries (invoice);
701 
702  /* Find the most suitable post date.
703  * For Customer Invoices that would be today.
704  * For Vendor Bills and Employee Vouchers
705  * that would be the date of the most recent invoice entry.
706  * Failing that, today is used as a fallback */
707  *postdate = timespec_now();
708 
709  if (entries && ((gncInvoiceGetOwnerType (invoice) == GNC_OWNER_VENDOR) ||
710  (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE)))
711  {
712  *postdate = gncEntryGetDate ((GncEntry*)entries->data);
713  for (entries_iter = entries; entries_iter != NULL; entries_iter = g_list_next(entries_iter))
714  {
715  Timespec entrydate;
716 
717  entrydate = gncEntryGetDate ((GncEntry*)entries_iter->data);
718  if (timespec_cmp(&entrydate, postdate) > 0)
719  *postdate = entrydate;
720  }
721  }
722 
723  /* Get the due date and posted account */
724  *ddue = *postdate;
725  *memo = NULL;
726  {
727  GncGUID *guid = NULL;
728  owner_inst = qofOwnerGetOwner (gncOwnerGetEndOwner (&(iw->owner)));
729  qof_instance_get (owner_inst,
730  "invoice-last-posted-account", &guid,
731  NULL);
732  *acc = xaccAccountLookup (guid, iw->book);
733  }
734  /* Get the default for the accumulate option */
735  *accumulate = gnc_prefs_get_bool(GNC_PREFS_GROUP_INVOICE, GNC_PREF_ACCUM_SPLITS);
736 
737  if (!gnc_dialog_dates_acct_question_parented (iw_get_window(iw), message, ddue_label,
738  post_label, acct_label, question_label, TRUE, TRUE,
739  acct_types, acct_commodities, iw->book, iw->terms,
740  ddue, postdate, memo, acc, accumulate))
741  return FALSE;
742 
743  return TRUE;
744 }
745 
747 {
748  Timespec ddue; /* Due date */
749  Timespec postdate; /* Date posted */
750  char *memo; /* Memo for posting transaction */
751  Account *acc; /* Account to post to */
752  gboolean accumulate; /* Whether to accumulate splits */
753 };
754 
755 static void
756 gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params)
757 {
758  GncInvoice *invoice;
759  char *message, *memo;
760  Account *acc = NULL;
761  Timespec ddue, postdate;
762  gboolean accumulate;
763  QofInstance *owner_inst;
764  const char *text;
765  GHashTable *foreign_currs;
766  GHashTableIter foreign_currs_iter;
767  gpointer key,value;
768  gboolean is_cust_doc, auto_pay;
769  gboolean show_dialog = TRUE;
770  gboolean post_ok = TRUE;
771 
772  /* Make sure the invoice is ok */
773  if (!gnc_invoice_window_verify_ok (iw))
774  return;
775 
776  invoice = iw_get_invoice (iw);
777  if (!invoice)
778  return;
779 
780  /* Check that there is at least one Entry */
781  if (gncInvoiceGetEntries (invoice) == NULL)
782  {
783  gnc_error_dialog (iw_get_window(iw), "%s",
784  _("The Invoice must have at least one Entry."));
785  return;
786  }
787 
788  is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
789 
790  /* Ok, we can post this invoice. Ask for verification, set the due date,
791  * post date, and posted account
792  */
793  if (post_params)
794  {
795  ddue = post_params->ddue;
796  postdate = post_params->postdate;
797  // Dup it since it will free it below
798  memo = g_strdup (post_params->memo);
799  acc = post_params->acc;
800  accumulate = post_params->accumulate;
801  }
802  else
803  {
804  message = _("Do you really want to post the invoice?");
805  if (!gnc_dialog_post_invoice(iw, message,
806  &ddue, &postdate, &memo, &acc, &accumulate))
807  return;
808  }
809 
810  /* Yep, we're posting. So, save the invoice...
811  * Note that we can safely ignore the return value; we checked
812  * the verify_ok earlier, so we know it's ok.
813  */
814  gnc_suspend_gui_refresh ();
815  gncInvoiceBeginEdit (invoice);
816  gnc_invoice_window_ok_save (iw);
817 
818  /* Fill in the conversion prices with feedback from the user */
819  text = _("One or more of the entries are for accounts different from the invoice/bill currency. You will be asked a conversion rate for each.");
820 
821  /* Ask the user for conversion rates for all foreign currencies
822  * (relative to the invoice currency) */
823  foreign_currs = gncInvoiceGetForeignCurrencies (invoice);
824  g_hash_table_iter_init (&foreign_currs_iter, foreign_currs);
825  while (g_hash_table_iter_next (&foreign_currs_iter, &key, &value))
826  {
827  GNCPrice *convprice;
828  gnc_commodity *account_currency = (gnc_commodity*)key;
829  gnc_numeric *amount = (gnc_numeric*)value;
830  Timespec pricedate;
831 
832  convprice = gncInvoiceGetPrice (invoice, account_currency);
833  if (convprice)
834  pricedate = gnc_price_get_time (convprice);
835  if (!convprice || !timespec_equal (&postdate, &pricedate))
836  {
837  XferDialog *xfer;
838  gnc_numeric exch_rate;
839 
840  /* Explain to the user we're about to ask for an exchange rate.
841  * Only show this dialog once, right before the first xfer dialog pops up.
842  */
843  if (show_dialog)
844  {
845  gnc_info_dialog(iw_get_window(iw), "%s", text);
846  show_dialog = FALSE;
847  }
848 
849  /* Note some twisted logic here:
850  * We ask the exchange rate
851  * FROM invoice currency
852  * TO other account currency
853  * Because that's what happens logically.
854  * But the internal posting logic works backwards:
855  * It searches for an exchange rate
856  * FROM other account currency
857  * TO invoice currency
858  * So we will store the inverted exchange rate
859  */
860 
861  /* create the exchange-rate dialog */
862  xfer = gnc_xfer_dialog (iw_get_window(iw), acc);
863  gnc_xfer_dialog_is_exchange_dialog(xfer, &exch_rate);
864  gnc_xfer_dialog_select_to_currency(xfer, account_currency);
865  gnc_xfer_dialog_set_date (xfer, timespecToTime64 (postdate));
866  /* Even if amount is 0 ask for an exchange rate. It's required
867  * for the transaction generating code. Use an amount of 1 in
868  * that case as the dialog won't allow to specify an exchange
869  * rate for 0. */
870  gnc_xfer_dialog_set_amount(xfer, gnc_numeric_zero_p (*amount) ?
871  (gnc_numeric){1, 1} : *amount);
872 
873  /* All we want is the exchange rate so prevent the user from thinking
874  it makes sense to mess with other stuff */
875  gnc_xfer_dialog_set_from_show_button_active(xfer, FALSE);
876  gnc_xfer_dialog_set_to_show_button_active(xfer, FALSE);
877  gnc_xfer_dialog_hide_from_account_tree(xfer);
878  gnc_xfer_dialog_hide_to_account_tree(xfer);
879  if (gnc_xfer_dialog_run_until_done(xfer))
880  {
881  /* User finished the transfer dialog successfully */
882 
883  /* Invert the exchange rate as explained above */
884  if (!gnc_numeric_zero_p (exch_rate))
885  exch_rate = gnc_numeric_div ((gnc_numeric){1, 1}, exch_rate,
887  convprice = gnc_price_create(iw->book);
888  gnc_price_begin_edit (convprice);
889  gnc_price_set_commodity (convprice, account_currency);
890  gnc_price_set_currency (convprice, gncInvoiceGetCurrency (invoice));
891  gnc_price_set_time (convprice, postdate);
892  gnc_price_set_source (convprice, "user:invoice-post");
893 
894  /* Yes, magic strings are evil but I can't find any defined constants
895  for this..*/
896  gnc_price_set_typestr (convprice, "last");
897  gnc_price_set_value (convprice, exch_rate);
898  gncInvoiceAddPrice(invoice, convprice);
899  gnc_price_commit_edit (convprice);
900  }
901  else
902  {
903  /* User canceled the transfer dialog, abort posting */
904  post_ok = FALSE;
905  goto cleanup;
906  }
907  }
908  }
909 
910 
911  /* Save account as last used account in the owner's
912  * invoice-last-posted-account property.
913  */
914  owner_inst = qofOwnerGetOwner (gncOwnerGetEndOwner (&(iw->owner)));
915  {
916  const GncGUID *guid = qof_instance_get_guid (QOF_INSTANCE (acc));
917  qof_begin_edit (owner_inst);
918  qof_instance_set (owner_inst,
919  "invoice-last-posted-account", guid,
920  NULL);
921  qof_commit_edit (owner_inst);
922  }
923 
924  /* ... post it ... */
925  if (is_cust_doc)
926  auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
927  else
928  auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);
929 
930  gncInvoicePostToAccount (invoice, acc, &postdate, &ddue, memo, accumulate, auto_pay);
931 
932 cleanup:
933  gncInvoiceCommitEdit (invoice);
934  g_hash_table_unref (foreign_currs);
935  gnc_resume_gui_refresh ();
936 
937  if (memo)
938  g_free (memo);
939 
940  if (post_ok)
941  {
942  /* Reset the type; change to read-only! */
943  iw->dialog_type = VIEW_INVOICE;
944  gnc_entry_ledger_set_readonly (iw->ledger, TRUE);
945  }
946  else
947  {
948  text = _("The post action was canceled because not all exchange rates were given.");
949  gnc_info_dialog(iw_get_window(iw), "%s", text);
950  }
951 
952  /* ... and redisplay here. */
953  gnc_invoice_update_window (iw, NULL);
954  gnc_table_refresh_gui (gnc_entry_ledger_get_table (iw->ledger), FALSE);
955 }
956 
957 void
958 gnc_invoice_window_postCB (GtkWidget *unused_widget, gpointer data)
959 {
960  InvoiceWindow *iw =data;
961  gnc_invoice_post(iw, NULL);
962 }
963 
964 void
965 gnc_invoice_window_unpostCB (GtkWidget *widget, gpointer data)
966 {
967  InvoiceWindow *iw = data;
968  GncInvoice *invoice;
969  gboolean result;
970 
971  invoice = iw_get_invoice (iw);
972  if (!invoice)
973  return;
974 
975  /* make sure the user REALLY wants to do this! */
976  result = iw_ask_unpost(iw);
977  if (!result) return;
978 
979  /* Attempt to unpost the invoice */
980  gnc_suspend_gui_refresh ();
981  result = gncInvoiceUnpost (invoice, iw->reset_tax_tables);
982  gnc_resume_gui_refresh ();
983  if (!result) return;
984 
985  /* if we get here, we succeeded in unposting -- reset the ledger and redisplay */
986  iw->dialog_type = EDIT_INVOICE;
987  gnc_entry_ledger_set_readonly (iw->ledger, FALSE);
988  gnc_invoice_update_window (iw, NULL);
989  gnc_table_refresh_gui (gnc_entry_ledger_get_table (iw->ledger), FALSE);
990 }
991 
992 void gnc_invoice_window_cut_cb (GtkWidget *widget, gpointer data)
993 {
994  InvoiceWindow *iw = data;
995  gnucash_register_cut_clipboard (iw->reg);
996 }
997 
998 void gnc_invoice_window_copy_cb (GtkWidget *widget, gpointer data)
999 {
1000  InvoiceWindow *iw = data;
1001  gnucash_register_copy_clipboard (iw->reg);
1002 }
1003 
1004 void gnc_invoice_window_paste_cb (GtkWidget *widget, gpointer data)
1005 {
1006  InvoiceWindow *iw = data;
1007  gnucash_register_paste_clipboard (iw->reg);
1008 }
1009 
1010 void gnc_invoice_window_new_invoice_cb (GtkWidget *widget, gpointer data)
1011 {
1012  InvoiceWindow *iw = data;
1013  if (gncOwnerGetJob (&iw->job))
1014  {
1015  gnc_ui_invoice_new (&iw->job, iw->book);
1016  }
1017  else
1018  {
1019  gnc_ui_invoice_new (&iw->owner, iw->book);
1020  }
1021 }
1022 
1023 void gnc_business_call_owner_report (GncOwner *owner, Account *acc)
1024 {
1025  int id;
1026  SCM args;
1027  SCM func;
1028  SCM arg;
1029 
1030  g_return_if_fail (owner);
1031 
1032  args = SCM_EOL;
1033 
1034  func = scm_c_eval_string ("gnc:owner-report-create");
1035  g_return_if_fail (scm_is_procedure (func));
1036 
1037  if (acc)
1038  {
1039  swig_type_info * qtype = SWIG_TypeQuery("_p_Account");
1040  g_return_if_fail (qtype);
1041 
1042  arg = SWIG_NewPointerObj(acc, qtype, 0);
1043  g_return_if_fail (arg != SCM_UNDEFINED);
1044  args = scm_cons (arg, args);
1045  }
1046  else
1047  {
1048  args = scm_cons (SCM_BOOL_F, args);
1049  }
1050 
1051  arg = SWIG_NewPointerObj(owner, SWIG_TypeQuery("_p__gncOwner"), 0);
1052  g_return_if_fail (arg != SCM_UNDEFINED);
1053  args = scm_cons (arg, args);
1054 
1055  /* Apply the function to the args */
1056  arg = scm_apply (func, args, SCM_EOL);
1057  g_return_if_fail (scm_is_exact (arg));
1058  id = scm_to_int (arg);
1059 
1060  if (id >= 0)
1061  reportWindow (id);
1062 }
1063 
1064 void gnc_invoice_window_report_owner_cb (GtkWidget *widget, gpointer data)
1065 {
1066  InvoiceWindow *iw = data;
1067  gnc_business_call_owner_report (&iw->owner, NULL);
1068 }
1069 
1070 void gnc_invoice_window_payment_cb (GtkWidget *widget, gpointer data)
1071 {
1072  InvoiceWindow *iw = data;
1073  GncInvoice *invoice = iw_get_invoice(iw);
1074 
1075  if (gncOwnerGetJob (&iw->job))
1076  gnc_ui_payment_new_with_invoice (&iw->job, iw->book, invoice);
1077  else
1078  gnc_ui_payment_new_with_invoice (&iw->owner, iw->book, invoice);
1079 }
1080 
1081 /* Sorting callbacks */
1082 
1083 void
1084 gnc_invoice_window_sort (InvoiceWindow *iw, invoice_sort_type_t sort_code)
1085 {
1086  QofQuery *query = gnc_entry_ledger_get_query (iw->ledger);
1087  GSList *p1 = NULL, *p2 = NULL, *p3 = NULL, *standard;
1088 
1089  if (iw->last_sort == sort_code)
1090  return;
1091 
1092  standard = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
1093 
1094  switch (sort_code)
1095  {
1096  case INVSORT_BY_STANDARD:
1097  p1 = standard;
1098  break;
1099  case INVSORT_BY_DATE:
1100  p1 = g_slist_prepend (p1, ENTRY_DATE);
1101  p2 = standard;
1102  break;
1103  case INVSORT_BY_DATE_ENTERED:
1104  p1 = g_slist_prepend (p1, ENTRY_DATE_ENTERED);
1105  p2 = standard;
1106  break;
1107  case INVSORT_BY_DESC:
1108  p1 = g_slist_prepend (p1, ENTRY_DESC);
1109  p2 = standard;
1110  break;
1111  case INVSORT_BY_QTY:
1112  p1 = g_slist_prepend (p1, ENTRY_QTY);
1113  p2 = standard;
1114  break;
1115  case INVSORT_BY_PRICE:
1116  p1 = g_slist_prepend (p1, ((iw->owner.type == GNC_OWNER_CUSTOMER) ?
1117  ENTRY_IPRICE : ENTRY_BPRICE));
1118  p2 = standard;
1119  break;
1120  default:
1121  g_slist_free (standard);
1122  g_return_if_fail (FALSE);
1123  break;
1124  }
1125 
1126  qof_query_set_sort_order (query, p1, p2, p3);
1127  iw->last_sort = sort_code;
1128  gnc_entry_ledger_display_refresh (iw->ledger);
1129 }
1130 
1131 /* Window configuration callbacks */
1132 
1133 void
1134 gnc_invoice_window_active_toggled_cb (GtkWidget *widget, gpointer data)
1135 {
1136  InvoiceWindow *iw = data;
1137  GncInvoice *invoice = iw_get_invoice(iw);
1138 
1139  if (!invoice) return;
1140 
1141  gncInvoiceSetActive (invoice,
1142  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
1143 }
1144 
1145 gboolean
1146 gnc_invoice_window_leave_notes_cb (GtkWidget *widget, GdkEventFocus *event,
1147  gpointer data)
1148 {
1149  InvoiceWindow *iw = data;
1150  GncInvoice *invoice = iw_get_invoice(iw);
1151  GtkTextBuffer* text_buffer;
1152  GtkTextIter start, end;
1153  gchar *text;
1154 
1155  if (!invoice) return FALSE;
1156 
1157  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(iw->notes_text));
1158  gtk_text_buffer_get_bounds (text_buffer, &start, &end);
1159  text = gtk_text_buffer_get_text (text_buffer, &start, &end, FALSE);
1160  gncInvoiceSetNotes (invoice, text);
1161  return FALSE;
1162 }
1163 
1164 static gboolean
1165 gnc_invoice_window_leave_to_charge_cb (GtkWidget *widget, GdkEventFocus *event,
1166  gpointer data)
1167 {
1168  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (widget));
1169  return FALSE;
1170 }
1171 
1172 static void
1173 gnc_invoice_window_changed_to_charge_cb (GtkWidget *widget, gpointer data)
1174 {
1175  InvoiceWindow *iw = data;
1176  GncInvoice *invoice = iw_get_invoice(iw);
1177 
1178  if (!invoice) return;
1179 
1180  gncInvoiceSetToChargeAmount (invoice, gnc_amount_edit_get_amount
1181  (GNC_AMOUNT_EDIT (widget)));
1182 }
1183 
1184 static GtkWidget *
1185 add_summary_label (GtkWidget *summarybar, const char *label_str)
1186 {
1187  GtkWidget *hbox;
1188  GtkWidget *label;
1189 
1190  hbox = gtk_hbox_new(FALSE, 2);
1191  gtk_box_pack_start (GTK_BOX(summarybar), hbox, FALSE, FALSE, 5);
1192 
1193  label = gtk_label_new (label_str);
1194  gtk_misc_set_alignment (GTK_MISC(label), 1.0, 0.5);
1195  gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
1196 
1197  label = gtk_label_new ("");
1198  gtk_misc_set_alignment (GTK_MISC(label), 1.0, 0.5);
1199  gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
1200 
1201  return label;
1202 }
1203 
1204 GtkWidget *
1205 gnc_invoice_window_create_summary_bar (InvoiceWindow *iw)
1206 {
1207  GtkWidget *summarybar;
1208 
1209  iw->total_label = NULL;
1210  iw->total_cash_label = NULL;
1211  iw->total_charge_label = NULL;
1212  iw->total_subtotal_label = NULL;
1213  iw->total_tax_label = NULL;
1214 
1215  summarybar = gtk_hbox_new (FALSE, 4);
1216 
1217  iw->total_label = add_summary_label (summarybar, _("Total:"));
1218 
1219  switch (gncOwnerGetType (&iw->owner))
1220  {
1221  case GNC_OWNER_CUSTOMER:
1222  case GNC_OWNER_VENDOR:
1223  iw->total_subtotal_label = add_summary_label (summarybar, _("Subtotal:"));
1224  iw->total_tax_label = add_summary_label (summarybar, _("Tax:"));
1225  break;
1226 
1227  case GNC_OWNER_EMPLOYEE:
1228  iw->total_cash_label = add_summary_label (summarybar, _("Total Cash:"));
1229  iw->total_charge_label = add_summary_label (summarybar, _("Total Charge:"));
1230  break;
1231 
1232  default:
1233  break;
1234  }
1235 
1236  gtk_widget_show_all(summarybar);
1237  return summarybar;
1238 }
1239 
1240 static int
1241 gnc_invoice_job_changed_cb (GtkWidget *widget, gpointer data)
1242 {
1243  InvoiceWindow *iw = data;
1244  char const *msg = "";
1245 
1246  if (!iw)
1247  return FALSE;
1248 
1249  if (iw->dialog_type == VIEW_INVOICE)
1250  return FALSE;
1251 
1252  gnc_owner_get_owner (iw->job_choice, &(iw->job));
1253 
1254  if (iw->dialog_type == EDIT_INVOICE)
1255  return FALSE;
1256 
1257  msg = gncJobGetReference (gncOwnerGetJob (&(iw->job)));
1258  gtk_entry_set_text (GTK_ENTRY (iw->billing_id_entry), msg ? msg : "");
1259 
1260  return FALSE;
1261 
1262 }
1263 
1264 static GNCSearchWindow *
1265 gnc_invoice_select_job_cb (gpointer jobp, gpointer user_data)
1266 {
1267  GncJob *j = jobp;
1268  InvoiceWindow *iw = user_data;
1269  GncOwner owner, *ownerp;
1270 
1271  if (!iw) return NULL;
1272 
1273  if (j)
1274  {
1275  ownerp = gncJobGetOwner (j);
1276  gncOwnerCopy (ownerp, &owner);
1277  }
1278  else
1279  gncOwnerCopy (&(iw->owner), &owner);
1280 
1281  return gnc_job_search (j, &owner, iw->book);
1282 }
1283 
1284 static void
1285 gnc_invoice_update_job_choice (InvoiceWindow *iw)
1286 {
1287  if (iw->job_choice)
1288  gtk_container_remove (GTK_CONTAINER (iw->job_box), iw->job_choice);
1289 
1290  /* If we don't have a real owner, then we obviously can't have a job */
1291  if (iw->owner.owner.undefined == NULL)
1292  {
1293  iw->job_choice = NULL;
1294  }
1295  else
1296  switch (iw->dialog_type)
1297  {
1298  case VIEW_INVOICE:
1299  case EDIT_INVOICE:
1300  iw->job_choice =
1301  gnc_owner_edit_create (NULL, iw->job_box, iw->book, &(iw->job));
1302  break;
1303  case NEW_INVOICE:
1304  case MOD_INVOICE:
1305  case DUP_INVOICE:
1306  iw->job_choice =
1307  gnc_general_search_new (GNC_JOB_MODULE_NAME, _("Select..."), TRUE,
1308  gnc_invoice_select_job_cb, iw, iw->book);
1309 
1310  gnc_general_search_set_selected (GNC_GENERAL_SEARCH (iw->job_choice),
1311  gncOwnerGetJob (&iw->job));
1312  gnc_general_search_allow_clear (GNC_GENERAL_SEARCH (iw->job_choice),
1313  TRUE);
1314  gtk_box_pack_start (GTK_BOX (iw->job_box), iw->job_choice,
1315  TRUE, TRUE, 0);
1316 
1317  g_signal_connect (G_OBJECT (iw->job_choice), "changed",
1318  G_CALLBACK (gnc_invoice_job_changed_cb), iw);
1319  break;
1320  }
1321 
1322  if (iw->job_choice)
1323  gtk_widget_show_all (iw->job_choice);
1324 }
1325 
1326 static GNCSearchWindow *
1327 gnc_invoice_select_proj_job_cb (gpointer jobp, gpointer user_data)
1328 {
1329  GncJob *j = jobp;
1330  InvoiceWindow *iw = user_data;
1331  GncOwner owner, *ownerp;
1332 
1333  if (!iw) return NULL;
1334 
1335  if (j)
1336  {
1337  ownerp = gncJobGetOwner (j);
1338  gncOwnerCopy (ownerp, &owner);
1339  }
1340  else
1341  gncOwnerCopy (&(iw->proj_cust), &owner);
1342 
1343  return gnc_job_search (j, &owner, iw->book);
1344 }
1345 
1346 static int
1347 gnc_invoice_proj_job_changed_cb (GtkWidget *widget, gpointer data)
1348 {
1349  InvoiceWindow *iw = data;
1350 
1351  if (!iw)
1352  return FALSE;
1353 
1354  if (iw->dialog_type == VIEW_INVOICE)
1355  return FALSE;
1356 
1357  gnc_owner_get_owner (iw->proj_job_choice, &(iw->proj_job));
1358  return FALSE;
1359 }
1360 
1361 static void
1362 gnc_invoice_update_proj_job (InvoiceWindow *iw)
1363 {
1364  if (iw->proj_job_choice)
1365  gtk_container_remove (GTK_CONTAINER (iw->proj_job_box),
1366  iw->proj_job_choice);
1367 
1368  switch (iw->dialog_type)
1369  {
1370  case VIEW_INVOICE:
1371  case EDIT_INVOICE:
1372  iw->proj_job_choice =
1373  gnc_owner_edit_create (NULL, iw->proj_job_box, iw->book, &(iw->proj_job));
1374  break;
1375  case NEW_INVOICE:
1376  case MOD_INVOICE:
1377  case DUP_INVOICE:
1378  if (iw->proj_cust.owner.undefined == NULL)
1379  {
1380  iw->proj_job_choice = NULL;
1381  }
1382  else
1383  {
1384  iw->proj_job_choice =
1385  gnc_general_search_new (GNC_JOB_MODULE_NAME, _("Select..."), TRUE,
1386  gnc_invoice_select_proj_job_cb, iw, iw->book);
1387 
1388  gnc_general_search_set_selected (GNC_GENERAL_SEARCH(iw->proj_job_choice),
1389  gncOwnerGetJob (&iw->proj_job));
1390  gnc_general_search_allow_clear (GNC_GENERAL_SEARCH (iw->proj_job_choice),
1391  TRUE);
1392  gtk_box_pack_start (GTK_BOX (iw->proj_job_box), iw->proj_job_choice,
1393  TRUE, TRUE, 0);
1394 
1395  g_signal_connect (G_OBJECT (iw->proj_job_choice), "changed",
1396  G_CALLBACK (gnc_invoice_proj_job_changed_cb), iw);
1397  }
1398  break;
1399  }
1400 
1401  if (iw->proj_job_choice)
1402  gtk_widget_show_all (iw->proj_job_choice);
1403 }
1404 
1405 static int
1406 gnc_invoice_owner_changed_cb (GtkWidget *widget, gpointer data)
1407 {
1408  InvoiceWindow *iw = data;
1409  GncBillTerm *term = NULL;
1410  GncOwner owner;
1411 
1412  if (!iw)
1413  return FALSE;
1414 
1415  if (iw->dialog_type == VIEW_INVOICE)
1416  return FALSE;
1417 
1418  gncOwnerCopy (&(iw->owner), &owner);
1419  gnc_owner_get_owner (iw->owner_choice, &owner);
1420 
1421  /* If this owner really changed, then reset ourselves */
1422  if (!gncOwnerEqual (&owner, &(iw->owner)))
1423  {
1424  gncOwnerCopy (&owner, &(iw->owner));
1425  gncOwnerInitJob (&(iw->job), NULL);
1426  gnc_entry_ledger_reset_query (iw->ledger);
1427  }
1428 
1429  if (iw->dialog_type == EDIT_INVOICE)
1430  return FALSE;
1431 
1432  switch (gncOwnerGetType (&(iw->owner)))
1433  {
1434  case GNC_OWNER_CUSTOMER:
1435  term = gncCustomerGetTerms (gncOwnerGetCustomer (&(iw->owner)));
1436  break;
1437  case GNC_OWNER_VENDOR:
1438  term = gncVendorGetTerms (gncOwnerGetVendor (&(iw->owner)));
1439  break;
1440  case GNC_OWNER_EMPLOYEE:
1441  term = NULL;
1442  break;
1443  default:
1444  g_warning ("Unknown owner type: %d\n", gncOwnerGetType (&(iw->owner)));
1445  break;
1446  }
1447 
1448  /* XXX: I'm not sure -- should we change the terms if this happens? */
1449  iw->terms = term;
1450  gnc_simple_combo_set_value (GTK_COMBO_BOX(iw->terms_menu), iw->terms);
1451 
1452  gnc_invoice_update_job_choice (iw);
1453 
1454  return FALSE;
1455 }
1456 
1457 static int
1458 gnc_invoice_proj_cust_changed_cb (GtkWidget *widget, gpointer data)
1459 {
1460  InvoiceWindow *iw = data;
1461  GncOwner owner;
1462 
1463  if (!iw)
1464  return FALSE;
1465 
1466  if (iw->dialog_type == VIEW_INVOICE)
1467  return FALSE;
1468 
1469  gncOwnerCopy (&(iw->proj_cust), &owner);
1470  gnc_owner_get_owner (iw->proj_cust_choice, &owner);
1471 
1472  /* If this owner really changed, then reset ourselves */
1473  if (!gncOwnerEqual (&owner, &(iw->proj_cust)))
1474  {
1475  gncOwnerCopy (&owner, &(iw->proj_cust));
1476  gncOwnerInitJob (&(iw->proj_job), NULL);
1477  }
1478 
1479  if (iw->dialog_type == EDIT_INVOICE)
1480  return FALSE;
1481 
1482  gnc_invoice_update_proj_job (iw);
1483 
1484  return FALSE;
1485 }
1486 
1487 static void
1488 gnc_invoice_dialog_close_handler (gpointer user_data)
1489 {
1490  InvoiceWindow *iw = user_data;
1491 
1492  if (iw)
1493  {
1494  gtk_widget_destroy (iw->dialog);
1495  }
1496 }
1497 
1498 static void
1499 gnc_invoice_window_close_handler (gpointer user_data)
1500 {
1501  InvoiceWindow *iw = user_data;
1502 
1503  if (iw)
1504  {
1505  gnc_main_window_close_page(iw->page);
1506  iw->page = NULL;
1507  }
1508 }
1509 
1510 static void
1511 gnc_invoice_reset_total_label (GtkLabel *label, gnc_numeric amt, gnc_commodity *com)
1512 {
1513  char string[256];
1514 
1516  xaccSPrintAmount (string, amt, gnc_commodity_print_info (com, TRUE));
1517  gtk_label_set_text (label, string);
1518 }
1519 
1520 static void
1521 gnc_invoice_redraw_all_cb (GnucashRegister *g_reg, gpointer data)
1522 {
1523  InvoiceWindow *iw = data;
1524  GncInvoice * invoice;
1525  gnc_commodity * currency;
1526  gnc_numeric amount, to_charge_amt = gnc_numeric_zero();
1527 
1528  if (!iw)
1529  return;
1530 
1531  // if (iw)
1532  // gnc_invoice_update_window (iw, NULL);
1533 
1534  invoice = iw_get_invoice (iw);
1535  if (!invoice)
1536  return;
1537 
1538  currency = gncInvoiceGetCurrency (invoice);
1539 
1540  if (iw->total_label)
1541  {
1542  amount = gncInvoiceGetTotal (invoice);
1543  gnc_invoice_reset_total_label (GTK_LABEL (iw->total_label), amount, currency);
1544  }
1545 
1546  if (iw->total_subtotal_label)
1547  {
1548  amount = gncInvoiceGetTotalSubtotal (invoice);
1549  gnc_invoice_reset_total_label (GTK_LABEL (iw->total_subtotal_label), amount, currency);
1550  }
1551 
1552  if (iw->total_tax_label)
1553  {
1554  amount = gncInvoiceGetTotalTax (invoice);
1555  gnc_invoice_reset_total_label (GTK_LABEL (iw->total_tax_label), amount, currency);
1556  }
1557 
1558  /* Deal with extra items for the expense voucher */
1559 
1560  if (iw->to_charge_edit)
1561  {
1562  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (iw->to_charge_edit));
1563  to_charge_amt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(iw->to_charge_edit));
1564  }
1565 
1566  if (iw->total_cash_label)
1567  {
1568  amount = gncInvoiceGetTotalOf (invoice, GNC_PAYMENT_CASH);
1569  amount = gnc_numeric_sub (amount, to_charge_amt,
1571  gnc_invoice_reset_total_label (GTK_LABEL (iw->total_cash_label), amount, currency);
1572  }
1573 
1574  if (iw->total_charge_label)
1575  {
1576  amount = gncInvoiceGetTotalOf (invoice, GNC_PAYMENT_CARD);
1577  amount = gnc_numeric_add (amount, to_charge_amt,
1579  gnc_invoice_reset_total_label (GTK_LABEL (iw->total_charge_label), amount, currency);
1580  }
1581 }
1582 
1583 void
1584 gnc_invoice_window_changed (InvoiceWindow *iw, GtkWidget *window)
1585 {
1586  gnc_entry_ledger_set_parent(iw->ledger, window);
1587 }
1588 
1589 gchar *
1590 gnc_invoice_get_help (InvoiceWindow *iw)
1591 {
1592  if (!iw)
1593  return NULL;
1594 
1595  return gnc_table_get_help (gnc_entry_ledger_get_table (iw->ledger));
1596 }
1597 
1598 static void
1599 gnc_invoice_window_refresh_handler (GHashTable *changes, gpointer user_data)
1600 {
1601  InvoiceWindow *iw = user_data;
1602  const EventInfo *info;
1603  GncInvoice *invoice = iw_get_invoice (iw);
1604  const GncOwner *owner;
1605 
1606  /* If there isn't an invoice behind us, close down */
1607  if (!invoice)
1608  {
1609  gnc_close_gui_component (iw->component_id);
1610  return;
1611  }
1612 
1613  /* Next, close if this is a destroy event */
1614  if (changes)
1615  {
1616  info = gnc_gui_get_entity_events (changes, &iw->invoice_guid);
1617  if (info && (info->event_mask & QOF_EVENT_DESTROY))
1618  {
1619  gnc_close_gui_component (iw->component_id);
1620  return;
1621  }
1622  }
1623 
1624  /* Check the owners, and see if they have changed */
1625  owner = gncInvoiceGetOwner (invoice);
1626 
1627  /* Copy the owner information into our window */
1628  gncOwnerCopy (gncOwnerGetEndOwner (owner), &(iw->owner));
1629  gncOwnerInitJob (&(iw->job), gncOwnerGetJob (owner));
1630 
1631  /* Copy the billto information into our window */
1632  owner = gncInvoiceGetBillTo (invoice);
1633  gncOwnerCopy (gncOwnerGetEndOwner (owner), &iw->proj_cust);
1634  gncOwnerInitJob (&iw->proj_job, gncOwnerGetJob (owner));
1635 
1636  /* Ok, NOW let's refresh ourselves */
1637  gnc_invoice_update_window (iw, NULL);
1638 }
1639 
1650 static void
1651 gnc_invoice_update_window (InvoiceWindow *iw, GtkWidget *widget)
1652 {
1653  GtkWidget *acct_entry;
1654  GncInvoice *invoice;
1655  gboolean is_posted = FALSE;
1656  gboolean can_unpost = FALSE;
1657 
1658  invoice = iw_get_invoice (iw);
1659 
1660  if (iw->owner_choice)
1661  gtk_container_remove (GTK_CONTAINER (iw->owner_box), iw->owner_choice);
1662 
1663  if (iw->proj_cust_choice)
1664  gtk_container_remove (GTK_CONTAINER (iw->proj_cust_box),
1665  iw->proj_cust_choice);
1666 
1667  switch (iw->dialog_type)
1668  {
1669  case VIEW_INVOICE:
1670  case EDIT_INVOICE:
1671  iw->owner_choice =
1672  gnc_owner_edit_create (iw->owner_label, iw->owner_box, iw->book,
1673  &(iw->owner));
1674  iw->proj_cust_choice =
1675  gnc_owner_edit_create (NULL, iw->proj_cust_box, iw->book,
1676  &(iw->proj_cust));
1677  break;
1678  case NEW_INVOICE:
1679  case MOD_INVOICE:
1680  case DUP_INVOICE:
1681  iw->owner_choice =
1682  gnc_owner_select_create (iw->owner_label, iw->owner_box, iw->book,
1683  &(iw->owner));
1684  iw->proj_cust_choice =
1685  gnc_owner_select_create (NULL, iw->proj_cust_box, iw->book,
1686  &(iw->proj_cust));
1687 
1688  g_signal_connect (G_OBJECT (iw->owner_choice), "changed",
1689  G_CALLBACK (gnc_invoice_owner_changed_cb), iw);
1690 
1691  g_signal_connect (G_OBJECT (iw->proj_cust_choice), "changed",
1692  G_CALLBACK (gnc_invoice_proj_cust_changed_cb), iw);
1693 
1694  break;
1695  }
1696 
1697  /* Set the type label */
1698  gtk_label_set_text (GTK_LABEL(iw->type_label), iw->is_credit_note ? _("Credit Note")
1699  : gtk_label_get_text (GTK_LABEL(iw->type_label)));
1700 
1701  if (iw->owner_choice)
1702  gtk_widget_show_all (iw->owner_choice);
1703  if (iw->proj_cust_choice)
1704  gtk_widget_show_all (iw->proj_cust_choice);
1705 
1706  gnc_invoice_update_job_choice (iw);
1707  gnc_invoice_update_proj_job (iw);
1708 
1709  /* Hide the project frame for customer invoices */
1710  if (iw->owner.type == GNC_OWNER_CUSTOMER)
1711  gtk_widget_hide (iw->proj_frame);
1712 
1713  /* Hide the "job" label and entry for employee invoices */
1714  if (iw->owner.type == GNC_OWNER_EMPLOYEE)
1715  {
1716  gtk_widget_hide (iw->job_label);
1717  gtk_widget_hide (iw->job_box);
1718  }
1719 
1720  acct_entry = GTK_WIDGET (gtk_builder_get_object (iw->builder, "acct_entry"));
1721 
1722  /* We know that "invoice" (and "owner") exist now */
1723  {
1724  GtkTextBuffer* text_buffer;
1725  const char *string;
1726  gchar * tmp_string;
1727  Timespec ts, ts_zero = {0, 0};
1728  Account *acct;
1729 
1730  gtk_entry_set_text (GTK_ENTRY (iw->id_entry), gncInvoiceGetID (invoice));
1731 
1732  gtk_entry_set_text (GTK_ENTRY (iw->billing_id_entry),
1733  gncInvoiceGetBillingID (invoice));
1734 
1735  string = gncInvoiceGetNotes (invoice);
1736  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(iw->notes_text));
1737  gtk_text_buffer_set_text (text_buffer, string, -1);
1738 
1739  if (iw->active_check)
1740  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (iw->active_check),
1741  gncInvoiceGetActive (invoice));
1742 
1743  ts = gncInvoiceGetDateOpened (invoice);
1744  if (timespec_equal (&ts, &ts_zero))
1745  {
1746  gnc_date_edit_set_time (GNC_DATE_EDIT (iw->opened_date),
1747  gnc_time (NULL));
1748  }
1749  else
1750  {
1751  gnc_date_edit_set_time_ts (GNC_DATE_EDIT (iw->opened_date), ts);
1752  }
1753 
1754  /* fill in the terms menu */
1755  iw->terms = gncInvoiceGetTerms (invoice);
1756  gnc_simple_combo_set_value (GTK_COMBO_BOX(iw->terms_menu), iw->terms);
1757 
1758  /*
1759  * Next, figure out if we've been posted, and if so set the
1760  * appropriate bits of information.. Then work on hiding or
1761  * showing as necessary.
1762  */
1763 
1764  acct = gncInvoiceGetPostedAcc (invoice);
1765  if (acct)
1766  {
1767  /* Ok, it's definitely posted. Setup the 'posted-invoice' fields now */
1768  is_posted = TRUE;
1769 
1770  /* Can we unpost this invoice?
1771  * XXX: right now we always can, but there
1772  * may be times in the future when we cannot.
1773  */
1774  can_unpost = TRUE;
1775 
1776  ts = gncInvoiceGetDatePosted (invoice);
1777  gnc_date_edit_set_time_ts (GNC_DATE_EDIT (iw->posted_date), ts);
1778 
1779  tmp_string = gnc_account_get_full_name (acct);
1780  gtk_entry_set_text (GTK_ENTRY (acct_entry), tmp_string);
1781  g_free(tmp_string);
1782  }
1783  }
1784 
1785  gnc_invoice_id_changed_cb(NULL, iw);
1786  if (iw->dialog_type == NEW_INVOICE ||
1787  iw->dialog_type == DUP_INVOICE ||
1788  iw->dialog_type == MOD_INVOICE)
1789  {
1790  if (widget)
1791  gtk_widget_show (widget);
1792  else
1793  gtk_widget_show (iw_get_window(iw));
1794  return;
1795  }
1796 
1797  /* Fill in the to_charge amount (only in VIEW/EDIT modes) */
1798  {
1799  gnc_numeric amount;
1800 
1801  amount = gncInvoiceGetToChargeAmount (invoice);
1802  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (iw->to_charge_edit), amount);
1803  }
1804 
1805  /* Hide/show the appropriate widgets based on our posted/paid state */
1806 
1807  {
1808  GtkWidget *hide, *show;
1809 
1810  if (is_posted == TRUE)
1811  {
1812  hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide3"));
1813  gtk_widget_hide (hide);
1814  hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide4"));
1815  gtk_widget_hide (hide);
1816 
1817  show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "posted_label"));
1818  gtk_widget_show (show);
1819  gtk_widget_show (iw->posted_date_hbox);
1820  show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "acct_label"));
1821  gtk_widget_show (show);
1822  gtk_widget_show (acct_entry);
1823 
1824  show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide1"));
1825  gtk_widget_show (show);
1826  show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide2"));
1827  gtk_widget_show (show);
1828  }
1829  else /* ! posted */
1830  {
1831  hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "posted_label"));
1832  gtk_widget_hide (hide);
1833  gtk_widget_hide (iw->posted_date_hbox);
1834 
1835  hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "acct_label"));
1836  gtk_widget_hide (hide);
1837  gtk_widget_hide (acct_entry);
1838 
1839  hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide1"));
1840  gtk_widget_hide (hide);
1841  hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide2"));
1842  gtk_widget_hide (hide);
1843  }
1844  }
1845 
1846  /* Set the toolbar widgets sensitivity */
1847  if (iw->page)
1848  gnc_plugin_page_invoice_update_menus(iw->page, is_posted, can_unpost);
1849 
1850  /* Set the to_charge widget */
1851  gtk_widget_set_sensitive (iw->to_charge_edit, !is_posted);
1852 
1853  /* Hide the to_charge frame for all non-employee invoices,
1854  * or set insensitive if the employee does not have a charge card
1855  */
1856  if (iw->owner.type == GNC_OWNER_EMPLOYEE)
1857  {
1858  if (!gncEmployeeGetCCard (gncOwnerGetEmployee(&iw->owner)))
1859  gtk_widget_set_sensitive (iw->to_charge_edit, FALSE);
1860  }
1861  else
1862  {
1863  gtk_widget_hide (iw->to_charge_frame);
1864  }
1865 
1866  if (is_posted)
1867  {
1868  // GtkWidget *hide;
1869 
1870  /* Setup viewer for read-only access */
1871  /*
1872  gtk_widget_set_sensitive (iw->id_entry, FALSE);
1873  gtk_widget_set_sensitive (iw->terms_menu, FALSE);
1874  gtk_widget_set_sensitive (iw->notes_text, FALSE); *//* XXX: should notes remain writable? */
1875  }
1876 
1877  if (widget)
1878  gtk_widget_show (widget);
1879  else
1880  gtk_widget_show (iw_get_window(iw));
1881 }
1882 
1883 gchar *
1884 gnc_invoice_get_title (InvoiceWindow *iw)
1885 {
1886  char *wintitle = NULL;
1887  const char *id = NULL;
1888 
1889  if (!iw) return NULL;
1890 
1891  switch (gncOwnerGetType (&iw->owner))
1892  {
1893  case GNC_OWNER_CUSTOMER:
1894  switch (iw->dialog_type)
1895  {
1896  case NEW_INVOICE:
1897  wintitle = iw->is_credit_note ? _("New Credit Note")
1898  : _("New Invoice");
1899  break;
1900  case MOD_INVOICE:
1901  case DUP_INVOICE:
1902  case EDIT_INVOICE:
1903  wintitle = iw->is_credit_note ? _("Edit Credit Note")
1904  : _("Edit Invoice");
1905  break;
1906  case VIEW_INVOICE:
1907  wintitle = iw->is_credit_note ? _("View Credit Note")
1908  : _("View Invoice");
1909  break;
1910  }
1911  break;
1912  case GNC_OWNER_VENDOR:
1913  switch (iw->dialog_type)
1914  {
1915  case NEW_INVOICE:
1916  wintitle = iw->is_credit_note ? _("New Credit Note")
1917  : _("New Bill");
1918  break;
1919  case MOD_INVOICE:
1920  case DUP_INVOICE:
1921  case EDIT_INVOICE:
1922  wintitle = iw->is_credit_note ? _("Edit Credit Note")
1923  : _("Edit Bill");
1924  break;
1925  case VIEW_INVOICE:
1926  wintitle = iw->is_credit_note ? _("View Credit Note")
1927  : _("View Bill");
1928  break;
1929  }
1930  break;
1931  case GNC_OWNER_EMPLOYEE:
1932  switch (iw->dialog_type)
1933  {
1934  case NEW_INVOICE:
1935  wintitle = iw->is_credit_note ? _("New Credit Note")
1936  : _("New Expense Voucher");
1937  break;
1938  case MOD_INVOICE:
1939  case DUP_INVOICE:
1940  case EDIT_INVOICE:
1941  wintitle = iw->is_credit_note ? _("Edit Credit Note")
1942  : _("Edit Expense Voucher");
1943  break;
1944  case VIEW_INVOICE:
1945  wintitle = iw->is_credit_note ? _("View Credit Note")
1946  : _("View Expense Voucher");
1947  break;
1948  }
1949  break;
1950  default:
1951  break;
1952  }
1953 
1954  if (iw->id_entry)
1955  id = gtk_entry_get_text (GTK_ENTRY (iw->id_entry));
1956  if (id && *id)
1957  return g_strconcat (wintitle, " - ", id, (char *)NULL);
1958  return g_strdup (wintitle);
1959 }
1960 
1961 void
1962 gnc_invoice_type_toggled_cb (GtkWidget *widget, gpointer data)
1963 {
1964  InvoiceWindow *iw = data;
1965 
1966  if (!iw) return;
1967  iw->is_credit_note = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1968 }
1969 
1970 void
1971 gnc_invoice_id_changed_cb (GtkWidget *unused, gpointer data)
1972 {
1973  InvoiceWindow *iw = data;
1974  gchar *title;
1975 
1976  if (!iw) return;
1977  if (iw->page)
1978  {
1980  }
1981  else
1982  {
1983  title = gnc_invoice_get_title (iw);
1984  gtk_window_set_title (GTK_WINDOW (iw->dialog), title);
1985  g_free (title);
1986  }
1987 }
1988 
1989 void
1990 gnc_invoice_terms_changed_cb (GtkWidget *widget, gpointer data)
1991 {
1992  GtkComboBox *cbox = GTK_COMBO_BOX (widget);
1993  InvoiceWindow *iw = data;
1994 
1995  if (!iw) return;
1996  if (!cbox) return;
1997 
1998  iw->terms = gnc_simple_combo_get_value (cbox);
1999 }
2000 
2001 
2002 static gboolean
2003 find_handler (gpointer find_data, gpointer user_data)
2004 {
2005  const GncGUID *invoice_guid = find_data;
2006  InvoiceWindow *iw = user_data;
2007 
2008  return(iw && guid_equal(&iw->invoice_guid, invoice_guid));
2009 }
2010 
2011 static InvoiceWindow *
2012 gnc_invoice_new_page (QofBook *bookp, InvoiceDialogType type,
2013  GncInvoice *invoice, const GncOwner *owner,
2014  GncMainWindow *window)
2015 {
2016  InvoiceWindow *iw;
2017  GncOwner *billto;
2018  GncPluginPage *new_page;
2019 
2020  g_assert (type != NEW_INVOICE && type != MOD_INVOICE && type != DUP_INVOICE);
2021  g_assert (invoice != NULL);
2022 
2023  /*
2024  * Find an existing window for this invoice. If found, bring it to
2025  * the front.
2026  */
2027  if (invoice)
2028  {
2029  GncGUID invoice_guid;
2030 
2031  invoice_guid = *gncInvoiceGetGUID (invoice);
2032  iw = gnc_find_first_gui_component (DIALOG_VIEW_INVOICE_CM_CLASS,
2033  find_handler, &invoice_guid);
2034  if (iw)
2035  {
2036  gnc_main_window_display_page(iw->page);
2037  return(iw);
2038  }
2039  }
2040 
2041  /*
2042  * No existing invoice window found. Build a new one.
2043  */
2044  iw = g_new0 (InvoiceWindow, 1);
2045  iw->book = bookp;
2046  iw->dialog_type = type;
2047  iw->invoice_guid = *gncInvoiceGetGUID (invoice);
2048  iw->is_credit_note = gncInvoiceGetIsCreditNote (invoice);
2049  iw->width = -1;
2050 
2051  /* Save this for later */
2052  gncOwnerCopy (gncOwnerGetEndOwner (owner), &(iw->owner));
2053  gncOwnerInitJob (&(iw->job), gncOwnerGetJob (owner));
2054 
2055  billto = gncInvoiceGetBillTo (invoice);
2056  gncOwnerCopy (gncOwnerGetEndOwner (billto), &(iw->proj_cust));
2057  gncOwnerInitJob (&iw->proj_job, gncOwnerGetJob (billto));
2058 
2059  /* Now create the plugin page for this invoice and display it. */
2060  new_page = gnc_plugin_page_invoice_new (iw);
2061  if (window)
2062  gnc_plugin_page_set_use_new_window (new_page, FALSE);
2063  else
2064  window = gnc_plugin_business_get_window ();
2065 
2066  gnc_main_window_open_page (window, new_page);
2067 
2068  /* Initialize the summary bar */
2069  gnc_invoice_redraw_all_cb(iw->reg, iw);
2070 
2071  return iw;
2072 }
2073 
2074 #define KEY_INVOICE_TYPE "InvoiceType"
2075 #define KEY_INVOICE_GUID "InvoiceGUID"
2076 #define KEY_OWNER_TYPE "OwnerType"
2077 #define KEY_OWNER_GUID "OwnerGUID"
2078 
2079 GncPluginPage *
2080 gnc_invoice_recreate_page (GncMainWindow *window,
2081  GKeyFile *key_file,
2082  const gchar *group_name)
2083 {
2084  InvoiceWindow *iw;
2085  GError *error = NULL;
2086  char *tmp_string = NULL, *owner_type = NULL;
2087  InvoiceDialogType type;
2088  GncInvoice *invoice;
2089  GncGUID guid;
2090  QofBook *book;
2091  GncOwner owner = { 0 };
2092 
2093  /* Get Invoice Type */
2094  tmp_string = g_key_file_get_string(key_file, group_name,
2095  KEY_INVOICE_TYPE, &error);
2096  if (error)
2097  {
2098  g_warning("Error reading group %s key %s: %s.",
2099  group_name, KEY_INVOICE_TYPE, error->message);
2100  goto give_up;
2101  }
2102  type = InvoiceDialogTypefromString(tmp_string);
2103  g_free(tmp_string);
2104 
2105  /* Get Invoice GncGUID */
2106  tmp_string = g_key_file_get_string(key_file, group_name,
2107  KEY_INVOICE_GUID, &error);
2108  if (error)
2109  {
2110  g_warning("Error reading group %s key %s: %s.",
2111  group_name, KEY_INVOICE_GUID, error->message);
2112  goto give_up;
2113  }
2114  if (!string_to_guid(tmp_string, &guid))
2115  {
2116  g_warning("Invalid invoice guid: %s.", tmp_string);
2117  goto give_up;
2118  }
2119  book = gnc_get_current_book();
2120  invoice = gncInvoiceLookup(gnc_get_current_book(), &guid);
2121  if (invoice == NULL)
2122  {
2123  g_warning("Can't find invoice %s in current book.", tmp_string);
2124  goto give_up;
2125  }
2126  g_free(tmp_string);
2127 
2128  /* Get Owner Type */
2129  owner_type = g_key_file_get_string(key_file, group_name,
2130  KEY_OWNER_TYPE, &error);
2131  if (error)
2132  {
2133  g_warning("Error reading group %s key %s: %s.",
2134  group_name, KEY_OWNER_TYPE, error->message);
2135  goto give_up;
2136  }
2137 
2138  /* Get Owner GncGUID */
2139  tmp_string = g_key_file_get_string(key_file, group_name,
2140  KEY_OWNER_GUID, &error);
2141  if (error)
2142  {
2143  g_warning("Error reading group %s key %s: %s.",
2144  group_name, KEY_OWNER_GUID, error->message);
2145  goto give_up;
2146  }
2147  if (!string_to_guid(tmp_string, &guid))
2148  {
2149  g_warning("Invalid owner guid: %s.", tmp_string);
2150  goto give_up;
2151  }
2152 
2153  if (!gncOwnerGetOwnerFromTypeGuid(book, &owner, owner_type, &guid))
2154  {
2155  g_warning("Can't find owner %s in current book.", tmp_string);
2156  goto give_up;
2157  }
2158  g_free(tmp_string);
2159  g_free(owner_type);
2160 
2161  iw = gnc_invoice_new_page (book, type, invoice, &owner, window);
2162  return iw->page;
2163 
2164 give_up:
2165  g_warning("Giving up on restoring '%s'.", group_name);
2166  if (error)
2167  g_error_free(error);
2168  if (tmp_string)
2169  g_free(tmp_string);
2170  if (owner_type)
2171  g_free(owner_type);
2172  return NULL;
2173 }
2174 
2175 void
2176 gnc_invoice_save_page (InvoiceWindow *iw,
2177  GKeyFile *key_file,
2178  const gchar *group_name)
2179 {
2180  gchar guidstr[GUID_ENCODING_LENGTH+1];
2181  guid_to_string_buff(&iw->invoice_guid, guidstr);
2182  g_key_file_set_string(key_file, group_name, KEY_INVOICE_TYPE,
2183  InvoiceDialogTypeasString(iw->dialog_type));
2184  g_key_file_set_string(key_file, group_name, KEY_INVOICE_GUID, guidstr);
2185 
2186  if (gncOwnerGetJob (&(iw->job)))
2187  {
2188  g_key_file_set_string(key_file, group_name, KEY_OWNER_TYPE,
2189  qofOwnerGetType(&iw->job));
2190  guid_to_string_buff(gncOwnerGetGUID(&iw->job), guidstr);
2191  g_key_file_set_string(key_file, group_name, KEY_OWNER_GUID, guidstr);
2192  }
2193  else
2194  {
2195  g_key_file_set_string(key_file, group_name, KEY_OWNER_TYPE,
2196  qofOwnerGetType(&iw->owner));
2197  guid_to_string_buff(gncOwnerGetGUID(&iw->owner), guidstr);
2198  g_key_file_set_string(key_file, group_name, KEY_OWNER_GUID, guidstr);
2199  }
2200 }
2201 
2202 GtkWidget *
2203 gnc_invoice_create_page (InvoiceWindow *iw, gpointer page)
2204 {
2205  GncInvoice *invoice;
2206  GtkBuilder *builder;
2207  GtkWidget *id_label;
2208  GtkWidget *dialog, *hbox;
2209  GncEntryLedger *entry_ledger = NULL;
2210  GncOwnerType owner_type;
2211  GncEntryLedgerType ledger_type;
2212  const gchar *prefs_group = NULL;
2213  gboolean is_credit_note = FALSE;
2214 
2215  invoice = gncInvoiceLookup (iw->book, &iw->invoice_guid);
2216  is_credit_note = gncInvoiceGetIsCreditNote (invoice);
2217 
2218  iw->page = page;
2219 
2220  /* Find the dialog */
2221  iw->builder = builder = gtk_builder_new();
2222  gnc_builder_add_from_file (builder, "dialog-invoice.glade", "terms_store");
2223  gnc_builder_add_from_file (builder, "dialog-invoice.glade", "invoice_entry_vbox");
2224  dialog = GTK_WIDGET (gtk_builder_get_object (builder, "invoice_entry_vbox"));
2225 
2226  /* Autoconnect all the signals */
2227  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, iw);
2228 
2229  /* Grab the widgets */
2230  iw->id_label = GTK_WIDGET (gtk_builder_get_object (builder, "label3"));
2231  iw->type_label = GTK_WIDGET (gtk_builder_get_object (builder, "page_type_label"));
2232  iw->info_label = GTK_WIDGET (gtk_builder_get_object (builder, "label25"));
2233  iw->id_entry = GTK_WIDGET (gtk_builder_get_object (builder, "page_id_entry"));
2234  iw->billing_id_entry = GTK_WIDGET (gtk_builder_get_object (builder, "page_billing_id_entry"));
2235  iw->terms_menu = GTK_WIDGET (gtk_builder_get_object (builder, "page_terms_menu"));
2236  iw->notes_text = GTK_WIDGET (gtk_builder_get_object (builder, "page_notes_text"));
2237  iw->active_check = GTK_WIDGET (gtk_builder_get_object (builder, "active_check"));
2238  iw->owner_box = GTK_WIDGET (gtk_builder_get_object (builder, "page_owner_hbox"));
2239  iw->owner_label = GTK_WIDGET (gtk_builder_get_object (builder, "page_owner_label"));
2240  iw->job_label = GTK_WIDGET (gtk_builder_get_object (builder, "page_job_label"));
2241  iw->job_box = GTK_WIDGET (gtk_builder_get_object (builder, "page_job_hbox"));
2242 
2243  /* grab the project widgets */
2244  iw->proj_frame = GTK_WIDGET (gtk_builder_get_object (builder, "page_proj_frame"));
2245  iw->proj_cust_box = GTK_WIDGET (gtk_builder_get_object (builder, "page_proj_cust_hbox"));
2246  iw->proj_job_box = GTK_WIDGET (gtk_builder_get_object (builder, "page_proj_job_hbox"));
2247 
2248  /* grab the to_charge widgets */
2249  {
2250  GtkWidget *edit;
2251 
2252  gnc_commodity *currency = gncInvoiceGetCurrency (invoice);
2253  GNCPrintAmountInfo print_info;
2254 
2255  iw->to_charge_frame = GTK_WIDGET (gtk_builder_get_object (builder, "to_charge_frame"));
2256  edit = gnc_amount_edit_new();
2257  print_info = gnc_commodity_print_info (currency, FALSE);
2258  gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (edit), TRUE);
2259  gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (edit), print_info);
2260  gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (edit),
2261  gnc_commodity_get_fraction (currency));
2262  iw->to_charge_edit = edit;
2263  gtk_widget_show (edit);
2264  hbox = GTK_WIDGET (gtk_builder_get_object (builder, "to_charge_box"));
2265  gtk_box_pack_start (GTK_BOX (hbox), edit, TRUE, TRUE, 0);
2266 
2267  g_signal_connect(G_OBJECT(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(edit))),
2268  "focus-out-event",
2269  G_CALLBACK(gnc_invoice_window_leave_to_charge_cb), iw);
2270  g_signal_connect(G_OBJECT(edit), "amount_changed",
2271  G_CALLBACK(gnc_invoice_window_changed_to_charge_cb), iw);
2272  }
2273 
2274  hbox = GTK_WIDGET (gtk_builder_get_object (builder, "page_date_opened_hbox"));
2275  iw->opened_date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
2276  gtk_widget_show(iw->opened_date);
2277  gtk_box_pack_start (GTK_BOX(hbox), iw->opened_date, TRUE, TRUE, 0);
2278 
2279  iw->posted_date_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "date_posted_hbox"));
2280  iw->posted_date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
2281  gtk_widget_show(iw->posted_date);
2282  gtk_box_pack_start (GTK_BOX(iw->posted_date_hbox), iw->posted_date,
2283  TRUE, TRUE, 0);
2284 
2285  /* Make the opened and posted dates insensitive in this window */
2286  gtk_widget_set_sensitive (iw->opened_date, FALSE);
2287  gtk_widget_set_sensitive (iw->posted_date, FALSE);
2288 
2289  /* Build the ledger */
2290  ledger_type = GNCENTRY_INVOICE_VIEWER;
2291  owner_type = gncOwnerGetType (&iw->owner);
2292  switch (iw->dialog_type)
2293  {
2294  case EDIT_INVOICE:
2295  switch (owner_type)
2296  {
2297  case GNC_OWNER_CUSTOMER:
2298  ledger_type = is_credit_note ? GNCENTRY_CUST_CREDIT_NOTE_ENTRY
2299  : GNCENTRY_INVOICE_ENTRY;
2300  break;
2301  case GNC_OWNER_VENDOR:
2302  ledger_type = is_credit_note ? GNCENTRY_VEND_CREDIT_NOTE_ENTRY
2303  : GNCENTRY_BILL_ENTRY;
2304  break;
2305  case GNC_OWNER_EMPLOYEE:
2306  ledger_type = is_credit_note ? GNCENTRY_EMPL_CREDIT_NOTE_ENTRY
2307  : GNCENTRY_EXPVOUCHER_ENTRY;
2308  break;
2309  default:
2310  g_warning ("Invalid owner type");
2311  break;
2312  }
2313  break;
2314  case VIEW_INVOICE:
2315  default:
2316  switch (owner_type)
2317  {
2318  case GNC_OWNER_CUSTOMER:
2319  ledger_type = is_credit_note ? GNCENTRY_CUST_CREDIT_NOTE_VIEWER
2320  : GNCENTRY_INVOICE_VIEWER;
2321  prefs_group = GNC_PREFS_GROUP_INVOICE;
2322  break;
2323  case GNC_OWNER_VENDOR:
2324  ledger_type = is_credit_note ? GNCENTRY_VEND_CREDIT_NOTE_VIEWER
2325  : GNCENTRY_BILL_VIEWER;
2326  prefs_group = GNC_PREFS_GROUP_BILL;
2327  break;
2328  case GNC_OWNER_EMPLOYEE:
2329  ledger_type = is_credit_note ? GNCENTRY_EMPL_CREDIT_NOTE_VIEWER
2330  : GNCENTRY_EXPVOUCHER_VIEWER;
2331  prefs_group = GNC_PREFS_GROUP_BILL;
2332  break;
2333  default:
2334  g_warning ("Invalid owner type");
2335  break;
2336  }
2337  break;
2338  }
2339  /* Default labels are for invoices, change them if they are anything else. */
2340  switch (owner_type)
2341  {
2342  case GNC_OWNER_VENDOR:
2343  gtk_label_set_text (GTK_LABEL(iw->info_label), _("Bill Information"));
2344  gtk_label_set_text (GTK_LABEL(iw->type_label), _("Bill"));
2345  gtk_label_set_text (GTK_LABEL(iw->id_label), _("Bill ID"));
2346  break;
2347  case GNC_OWNER_EMPLOYEE:
2348  gtk_label_set_text (GTK_LABEL(iw->info_label), _("Voucher Information"));
2349  gtk_label_set_text (GTK_LABEL(iw->type_label), _("Voucher"));
2350  gtk_label_set_text (GTK_LABEL(iw->id_label), _("Voucher ID"));
2351  default:
2352  break;
2353  }
2354 
2355  entry_ledger = gnc_entry_ledger_new (iw->book, ledger_type);
2356 
2357  /* Save the ledger... */
2358  iw->ledger = entry_ledger;
2359  /* window will be updated in a callback */
2360 
2361  /* Set the entry_ledger's invoice */
2362  gnc_entry_ledger_set_default_invoice (entry_ledger, invoice);
2363 
2364  /* Set the preferences group */
2365  gnc_entry_ledger_set_prefs_group (entry_ledger, prefs_group);
2366 
2367  /* Setup initial values */
2368  iw->component_id =
2369  gnc_register_gui_component (DIALOG_VIEW_INVOICE_CM_CLASS,
2370  gnc_invoice_window_refresh_handler,
2371  gnc_invoice_window_close_handler,
2372  iw);
2373 
2374  gnc_gui_component_watch_entity_type (iw->component_id,
2375  GNC_INVOICE_MODULE_NAME,
2376  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
2377 
2378  /* Create the register */
2379  {
2380  GtkWidget *regWidget, *frame, *window;
2381 
2382  /* Watch the order of operations, here... */
2383  regWidget = gnucash_register_new (gnc_entry_ledger_get_table
2384  (entry_ledger));
2385  gtk_widget_show(regWidget);
2386  gnc_table_init_gui( regWidget, NULL);
2387 
2388  frame = GTK_WIDGET (gtk_builder_get_object (builder, "ledger_frame"));
2389  gtk_container_add (GTK_CONTAINER (frame), regWidget);
2390 
2391  iw->reg = GNUCASH_REGISTER (regWidget);
2392  window = gnc_plugin_page_get_window(iw->page);
2393  gnucash_sheet_set_window (gnucash_register_get_sheet (iw->reg), window);
2394 
2395  g_signal_connect (G_OBJECT (regWidget), "activate_cursor",
2396  G_CALLBACK (gnc_invoice_window_recordCB), iw);
2397  g_signal_connect (G_OBJECT (regWidget), "redraw_all",
2398  G_CALLBACK (gnc_invoice_redraw_all_cb), iw);
2399  }
2400 
2401  gnc_table_realize_gui (gnc_entry_ledger_get_table (entry_ledger));
2402 
2403  /* Now fill in a lot of the pieces and display properly */
2404  gnc_billterms_combo (GTK_COMBO_BOX(iw->terms_menu), iw->book, TRUE, iw->terms);
2405  gnc_invoice_update_window (iw, dialog);
2406 
2407  gnc_table_refresh_gui (gnc_entry_ledger_get_table (iw->ledger), TRUE);
2408 
2409  /* Show the dialog */
2410  // gtk_widget_show_all (dialog);
2411 
2412  return dialog;
2413 }
2414 
2415 static InvoiceWindow *
2416 gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
2417  const GncOwner *owner, GncInvoice *invoice)
2418 {
2419  InvoiceWindow *iw;
2420  GtkBuilder *builder;
2421  GtkWidget *hbox;
2422  GtkWidget *invoice_radio;
2423  GncOwner *billto;
2424  const GncOwner *start_owner;
2425  GncBillTerm *owner_terms = NULL;
2426  GncOwnerType owner_type;
2427 
2428  g_assert (dialog_type == NEW_INVOICE || dialog_type == MOD_INVOICE || dialog_type == DUP_INVOICE);
2429 
2430  if (invoice)
2431  {
2432  /*
2433  * Try to find an existing window for this invoice. If found,
2434  * bring it to the front.
2435  */
2436  GncGUID invoice_guid;
2437 
2438  invoice_guid = *gncInvoiceGetGUID (invoice);
2439  iw = gnc_find_first_gui_component (DIALOG_NEW_INVOICE_CM_CLASS,
2440  find_handler, &invoice_guid);
2441  if (iw)
2442  {
2443  gtk_window_present (GTK_WINDOW(iw->dialog));
2444  return(iw);
2445  }
2446  }
2447 
2448  /*
2449  * No existing invoice window found. Build a new one.
2450  */
2451 
2452  iw = g_new0 (InvoiceWindow, 1);
2453  iw->dialog_type = dialog_type;
2454 
2455  switch (dialog_type)
2456  {
2457  case NEW_INVOICE:
2458  g_assert (bookp);
2459 
2460  invoice = gncInvoiceCreate (bookp);
2461  gncInvoiceSetCurrency (invoice, gnc_default_currency ());
2462  iw->book = bookp;
2463  start_owner = owner;
2464  switch (gncOwnerGetType (gncOwnerGetEndOwner (owner)))
2465  {
2466  case GNC_OWNER_CUSTOMER:
2467  owner_terms = gncCustomerGetTerms (gncOwnerGetCustomer (gncOwnerGetEndOwner (owner)));
2468  break;
2469  case GNC_OWNER_VENDOR:
2470  owner_terms = gncVendorGetTerms (gncOwnerGetVendor (gncOwnerGetEndOwner (owner)));
2471  break;
2472  default:
2473  break;
2474  }
2475  if (owner_terms)
2476  gncInvoiceSetTerms (invoice, owner_terms);
2477  break;
2478 
2479  case MOD_INVOICE:
2480  case DUP_INVOICE:
2481  start_owner = gncInvoiceGetOwner (invoice);
2482  iw->book = gncInvoiceGetBook (invoice);
2483  break;
2484  default:
2485  /* The assert at the beginning of this function should prevent this switch case ! */
2486  return NULL;
2487  }
2488 
2489  /* Save this for later */
2490  gncOwnerCopy (gncOwnerGetEndOwner(start_owner), &(iw->owner));
2491  gncOwnerInitJob (&(iw->job), gncOwnerGetJob (start_owner));
2492 
2493  billto = gncInvoiceGetBillTo (invoice);
2494  gncOwnerCopy (gncOwnerGetEndOwner (billto), &(iw->proj_cust));
2495  gncOwnerInitJob (&iw->proj_job, gncOwnerGetJob (billto));
2496 
2497  /* Find the glade page layout */
2498  iw->builder = builder = gtk_builder_new();
2499  gnc_builder_add_from_file (builder, "dialog-invoice.glade", "terms_store");
2500  gnc_builder_add_from_file (builder, "dialog-invoice.glade", "New Invoice Dialog");
2501  iw->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "New Invoice Dialog"));
2502 
2503  g_object_set_data (G_OBJECT (iw->dialog), "dialog_info", iw);
2504 
2505  /* Grab the widgets */
2506  iw->type_label = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_label"));
2507  iw->id_label = GTK_WIDGET (gtk_builder_get_object (builder, "label14"));
2508  iw->info_label = GTK_WIDGET (gtk_builder_get_object (builder, "label1"));
2509  invoice_radio = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_invoice_type"));
2510 
2511  iw->type_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_choice_hbox"));
2512  iw->type_choice = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_invoice"));
2513 
2514  /* The default GUI lables are for invoices, so change them if it isn't. */
2515  owner_type = gncOwnerGetType (&iw->owner);
2516  switch(owner_type)
2517  {
2518  case GNC_OWNER_VENDOR:
2519  gtk_label_set_text (GTK_LABEL(iw->info_label), _("Bill Information"));
2520  gtk_label_set_text (GTK_LABEL(iw->type_label), _("Bill"));
2521  gtk_button_set_label (GTK_BUTTON(invoice_radio), _("Bill"));
2522  gtk_label_set_text (GTK_LABEL(iw->id_label), _("Bill ID"));
2523 
2524  break;
2525  case GNC_OWNER_EMPLOYEE:
2526  gtk_label_set_text (GTK_LABEL(iw->info_label), _("Voucher Information"));
2527  gtk_label_set_text (GTK_LABEL(iw->type_label), _("Voucher"));
2528  gtk_button_set_label (GTK_BUTTON(invoice_radio), _("Voucher"));
2529  gtk_label_set_text (GTK_LABEL(iw->id_label), _("Voucher ID"));
2530  default:
2531  break;
2532  }
2533 
2534  /* configure the type related widgets based on dialog type and invoice type */
2535  switch (dialog_type)
2536  {
2537  case NEW_INVOICE:
2538  case DUP_INVOICE:
2539  gtk_widget_show_all (iw->type_hbox);
2540  gtk_widget_hide (iw->type_label);
2541  break;
2542  case MOD_INVOICE:
2543  gtk_widget_hide (iw->type_hbox);
2544  gtk_widget_show (iw->type_label);
2545  break;
2546  default:
2547  break;
2548  }
2549 
2550  if (dialog_type == DUP_INVOICE)
2551  {
2552  GtkWidget *invoice_radio = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_invoice_type"));
2553  GtkWidget *cn_radio = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_creditnote_type"));
2554 
2555  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(cn_radio), gncInvoiceGetIsCreditNote (invoice));
2556  }
2557 
2558  iw->id_entry = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_id_entry"));
2559  iw->billing_id_entry = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_billing_id_entry"));
2560  iw->terms_menu = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_terms_menu"));
2561  iw->notes_text = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_notes_text"));
2562  iw->owner_box = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_owner_hbox"));
2563  iw->owner_label = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_owner_label"));
2564  iw->job_label = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_job_label"));
2565  iw->job_box = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_job_hbox"));
2566 
2567  /* Grab the project widgets */
2568  iw->proj_frame = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_proj_frame"));
2569  iw->proj_cust_box = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_proj_cust_hbox"));
2570  iw->proj_job_box = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_proj_job_hbox"));
2571 
2572  hbox = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_date_opened_hbox"));
2573  iw->opened_date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
2574  gtk_widget_show(iw->opened_date);
2575  gtk_box_pack_start (GTK_BOX(hbox), iw->opened_date, TRUE, TRUE, 0);
2576 
2577  /* If this is a New Invoice, reset the Notes file to read/write */
2578  gtk_widget_set_sensitive (iw->notes_text, (iw->dialog_type == NEW_INVOICE));
2579 
2580  /* Setup signals */
2581  gtk_builder_connect_signals_full( builder,
2582  gnc_builder_connect_full_func,
2583  iw);
2584 
2585  /* Setup initial values */
2586  iw->invoice_guid = *gncInvoiceGetGUID (invoice);
2587  iw->is_credit_note = gncInvoiceGetIsCreditNote (invoice);
2588 
2589  iw->component_id =
2590  gnc_register_gui_component (DIALOG_NEW_INVOICE_CM_CLASS,
2591  gnc_invoice_window_refresh_handler,
2592  gnc_invoice_dialog_close_handler,
2593  iw);
2594 
2595  gnc_gui_component_watch_entity_type (iw->component_id,
2596  GNC_INVOICE_MODULE_NAME,
2597  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
2598 
2599  /* Now fill in a lot of the pieces and display properly */
2600  gnc_billterms_combo (GTK_COMBO_BOX(iw->terms_menu), iw->book, TRUE, iw->terms);
2601  gnc_invoice_update_window (iw, iw->dialog);
2602  gnc_table_refresh_gui (gnc_entry_ledger_get_table (iw->ledger), TRUE);
2603 
2604  // The customer choice widget should have keyboard focus
2605  if (GNC_IS_GENERAL_SEARCH(iw->owner_choice))
2606  {
2607  gnc_general_search_grab_focus(GNC_GENERAL_SEARCH(iw->owner_choice));
2608  }
2609 
2610  return iw;
2611 }
2612 
2613 InvoiceWindow *
2614 gnc_ui_invoice_edit (GncInvoice *invoice)
2615 {
2616  InvoiceWindow *iw;
2617  InvoiceDialogType type;
2618 
2619  if (!invoice) return NULL;
2620 
2621  /* Immutable once we've been posted */
2622  if (gncInvoiceGetPostedAcc (invoice))
2623  type = VIEW_INVOICE;
2624  else
2625  type = EDIT_INVOICE;
2626 
2627  iw = gnc_invoice_new_page (gncInvoiceGetBook(invoice), type,
2628  invoice, gncInvoiceGetOwner (invoice), NULL);
2629 
2630  return iw;
2631 }
2632 
2633 static InvoiceWindow *
2634 gnc_ui_invoice_modify (GncInvoice *invoice)
2635 {
2636  InvoiceWindow *iw;
2637  if (!invoice) return NULL;
2638 
2639  iw = gnc_invoice_window_new_invoice (MOD_INVOICE, NULL, NULL, invoice);
2640  return iw;
2641 }
2642 
2643 static void
2644 set_gncEntry_date(gpointer data, gpointer user_data)
2645 {
2646  GncEntry *entry = data;
2647  const GDate* new_date = user_data;
2648  //g_warning("Modifying date for entry with desc=\"%s\"", gncEntryGetDescription(entry));
2649 
2650  gncEntrySetDateGDate(entry, new_date);
2651  /*gncEntrySetDateEntered(entry, *new_date); - don't modify this
2652  * because apparently it defines the ordering of the entries,
2653  * which we don't want to change. */
2654 }
2655 
2656 
2657 InvoiceWindow * gnc_ui_invoice_duplicate (GncInvoice *old_invoice, gboolean open_properties, const GDate *new_date)
2658 {
2659  InvoiceWindow *iw;
2660  GncInvoice *new_invoice = NULL;
2661  gchar *new_id;
2662  GDate new_date_gdate;
2663 
2664  g_assert(old_invoice);
2665 
2666  // Create a deep copy of the old invoice
2667  new_invoice = gncInvoiceCopy(old_invoice);
2668 
2669  // The new invoice is for sure active
2670  gncInvoiceSetActive(new_invoice, TRUE);
2671 
2672  // and unposted
2673  if (gncInvoiceIsPosted (new_invoice))
2674  {
2675  gboolean result = gncInvoiceUnpost(new_invoice, TRUE);
2676  if (!result)
2677  {
2678  g_warning("Oops, error when unposting the copied invoice; ignoring.");
2679  }
2680  }
2681 
2682  // Set a new id from the respective counter
2683  new_id = gncInvoiceNextID(gnc_get_current_book(),
2684  gncInvoiceGetOwner(new_invoice));
2685  gncInvoiceSetID(new_invoice, new_id);
2686  g_free(new_id);
2687 
2688  // Modify the date to today
2689  if (new_date)
2690  {
2691  new_date_gdate = *new_date;
2692  }
2693  else
2694  {
2695  GDate *tmp = gnc_g_date_new_today();
2696  new_date_gdate = *tmp;
2697  g_date_free(tmp);
2698  }
2699  gncInvoiceSetDateOpenedGDate(new_invoice, &new_date_gdate);
2700 
2701  // Also modify the date of all entries to today
2702  //g_warning("We have %d entries", g_list_length(gncInvoiceGetEntries(new_invoice)));
2703  g_list_foreach(gncInvoiceGetEntries(new_invoice),
2704  &set_gncEntry_date, &new_date_gdate);
2705 
2706 
2707  if (open_properties)
2708  {
2709  // Open the "properties" pop-up for the invoice...
2710  iw = gnc_invoice_window_new_invoice (DUP_INVOICE, NULL, NULL, new_invoice);
2711  }
2712  else
2713  {
2714  // Open the newly created invoice in the "edit" window
2715  iw = gnc_ui_invoice_edit (new_invoice);
2716  }
2717 
2718  return iw;
2719 }
2720 
2721 InvoiceWindow *
2722 gnc_ui_invoice_new (GncOwner *ownerp, QofBook *bookp)
2723 {
2724  InvoiceWindow *iw;
2725  GncOwner owner;
2726 
2727  if (ownerp)
2728  {
2729  gncOwnerCopy (ownerp, &owner);
2730  }
2731  else
2732  gncOwnerInitCustomer (&owner, NULL); /* XXX: pass in the owner type? */
2733 
2734  /* Make sure required options exist */
2735  if (!bookp) return NULL;
2736 
2737  iw = gnc_invoice_window_new_invoice (NEW_INVOICE, bookp, &owner, NULL);
2738 
2739  return iw;
2740 }
2741 
2742 /* Functions for invoice selection widgets */
2743 
2744 static void
2745 edit_invoice_direct (gpointer invoice, gpointer user_data)
2746 {
2747  g_return_if_fail (invoice);
2748  gnc_ui_invoice_edit (invoice);
2749 }
2750 
2751 static void
2752 edit_invoice_cb (gpointer *invoice_p, gpointer user_data)
2753 {
2754  g_return_if_fail (invoice_p && user_data);
2755  if (! *invoice_p)
2756  return;
2757  edit_invoice_direct (*invoice_p, user_data);
2758 }
2759 
2760 static void
2761 pay_invoice_direct (gpointer inv, gpointer user_data)
2762 {
2763  GncInvoice *invoice = inv;
2764 
2765  g_return_if_fail (invoice);
2766  gnc_ui_payment_new_with_invoice (gncInvoiceGetOwner (invoice),
2767  gncInvoiceGetBook (invoice), invoice);
2768 }
2769 
2770 static void
2771 pay_invoice_cb (gpointer *invoice_p, gpointer user_data)
2772 {
2773  g_return_if_fail (invoice_p && user_data);
2774  if (! *invoice_p)
2775  return;
2776  pay_invoice_direct (*invoice_p, user_data);
2777 }
2779 {
2780  GDate date;
2781 };
2782 
2783 static void multi_duplicate_invoice_one(gpointer data, gpointer user_data)
2784 {
2785  GncInvoice *old_invoice = data;
2786  struct multi_duplicate_invoice_data *dup_user_data = user_data;
2787 
2788  g_assert(dup_user_data);
2789  if (old_invoice)
2790  {
2791  GncInvoice *new_invoice;
2792  // In this simplest form, we just use the existing duplication
2793  // algorithm, only without opening the "edit invoice" window for editing
2794  // the number etc. for each of the invoices.
2795  InvoiceWindow *iw = gnc_ui_invoice_duplicate(old_invoice, FALSE, &dup_user_data->date);
2796  // FIXME: Now we could use this invoice and manipulate further data.
2797  g_assert(iw);
2798  new_invoice = iw_get_invoice(iw);
2799  g_assert(new_invoice);
2800  }
2801 }
2802 
2803 static void
2804 multi_duplicate_invoice_cb (GList *invoice_list, gpointer user_data)
2805 {
2806  g_return_if_fail (invoice_list);
2807  switch (g_list_length(invoice_list))
2808  {
2809  case 0:
2810  return;
2811  case 1:
2812  {
2813  // Duplicate exactly one invoice
2814  GncInvoice *old_invoice = invoice_list->data;
2815  gnc_ui_invoice_duplicate(old_invoice, TRUE, NULL);
2816  return;
2817  }
2818  default:
2819  {
2820  // Duplicate multiple invoices. We ask for a date first.
2821  struct multi_duplicate_invoice_data dup_user_data;
2822  gboolean dialog_ok;
2823 
2824  // Default date: Today
2825  gnc_gdate_set_time64(&dup_user_data.date, gnc_time (NULL));
2826  dialog_ok = gnc_dup_date_dialog (NULL, _("Date of duplicated entries"), &dup_user_data.date);
2827  if (!dialog_ok)
2828  {
2829  // User pressed cancel, so don't duplicate anything here.
2830  return;
2831  }
2832 
2833  // Note: If we want to have a more sophisticated duplication, we might want
2834  // to ask for particular data right here, then insert this data upon
2835  // duplication.
2836  g_list_foreach(invoice_list, multi_duplicate_invoice_one, &dup_user_data);
2837  return;
2838  }
2839  }
2840 }
2841 
2842 static void post_one_invoice_cb(gpointer data, gpointer user_data)
2843 {
2844  GncInvoice *invoice = data;
2845  struct post_invoice_params *post_params = user_data;
2846  InvoiceWindow *iw = gnc_ui_invoice_edit(invoice);
2847  gnc_invoice_post(iw, post_params);
2848 }
2849 
2850 static void
2851 multi_post_invoice_cb (GList *invoice_list, gpointer user_data)
2852 {
2853  struct post_invoice_params post_params;
2854  InvoiceWindow *iw;
2855 
2856  if (g_list_length(invoice_list) == 0)
2857  return;
2858 
2859  // Get the posting parameters for these invoices
2860  iw = gnc_ui_invoice_edit(invoice_list->data);
2861  if (!gnc_dialog_post_invoice(iw, _("Do you really want to post these invoices?"),
2862  &post_params.ddue, &post_params.postdate,
2863  &post_params.memo, &post_params.acc,
2864  &post_params.accumulate))
2865  return;
2866 
2867  // Turn off GUI refresh for the duration. This is more than just an
2868  // optimization. If the search that got us here is based on the "posted"
2869  // status of an invoice, the updating the GUI will change the list we're
2870  // working on which leads to bad things happening.
2871  gnc_suspend_gui_refresh ();
2872  g_list_foreach(invoice_list, post_one_invoice_cb, &post_params);
2873  gnc_resume_gui_refresh ();
2874 }
2875 
2876 static void print_one_invoice_cb(gpointer data, gpointer user_data)
2877 {
2878  GncInvoice *invoice = data;
2879  gnc_invoice_window_print_invoice(invoice); // that's all!
2880 }
2881 
2882 static void
2883 multi_print_invoice_cb (GList *invoice_list, gpointer user_data)
2884 {
2885  if (g_list_length(invoice_list) == 0)
2886  return;
2887 
2888  g_list_foreach(invoice_list, print_one_invoice_cb, user_data);
2889 }
2890 
2891 static gpointer
2892 new_invoice_cb (gpointer user_data)
2893 {
2894  struct _invoice_select_window *sw = user_data;
2895  InvoiceWindow *iw;
2896 
2897  g_return_val_if_fail (user_data, NULL);
2898 
2899  iw = gnc_ui_invoice_new (sw->owner, sw->book);
2900  return iw_get_invoice (iw);
2901 }
2902 
2903 static void
2904 free_invoice_cb (gpointer user_data)
2905 {
2906  struct _invoice_select_window *sw = user_data;
2907 
2908  g_return_if_fail (sw);
2909 
2910  qof_query_destroy (sw->q);
2911  g_free (sw);
2912 }
2913 
2915 gnc_invoice_search (GncInvoice *start, GncOwner *owner, QofBook *book)
2916 {
2917  QofIdType type = GNC_INVOICE_MODULE_NAME;
2918  struct _invoice_select_window *sw;
2919  QofQuery *q, *q2 = NULL;
2920  GncOwnerType owner_type = GNC_OWNER_CUSTOMER;
2921  static GList *inv_params = NULL, *bill_params = NULL, *emp_params = NULL, *params;
2922  static GList *columns = NULL;
2923  const gchar *title, *label;
2924  static GNCSearchCallbackButton *buttons;
2925  static GNCSearchCallbackButton inv_buttons[] =
2926  {
2927  { N_("View/Edit Invoice"), edit_invoice_cb, NULL, TRUE},
2928  { N_("Process Payment"), pay_invoice_cb, NULL, FALSE},
2929  { N_("Duplicate"), NULL, multi_duplicate_invoice_cb, FALSE},
2930  { N_("Post"), NULL, multi_post_invoice_cb, FALSE},
2931  { N_("Printable Report"), NULL, multi_print_invoice_cb, TRUE},
2932  { NULL },
2933  };
2934  static GNCSearchCallbackButton bill_buttons[] =
2935  {
2936  { N_("View/Edit Bill"), edit_invoice_cb, NULL, TRUE},
2937  { N_("Process Payment"), pay_invoice_cb, NULL, FALSE},
2938  { N_("Duplicate"), NULL, multi_duplicate_invoice_cb, FALSE},
2939  { N_("Post"), NULL, multi_post_invoice_cb, FALSE},
2940  { N_("Printable Report"), NULL, multi_print_invoice_cb, TRUE},
2941  { NULL },
2942  };
2943  static GNCSearchCallbackButton emp_buttons[] =
2944  {
2945  /* Translators: The terms 'Voucher' and 'Expense Voucher' are used
2946  interchangeably in gnucash and mean the same thing. */
2947  { N_("View/Edit Voucher"), edit_invoice_cb, NULL, TRUE},
2948  { N_("Process Payment"), pay_invoice_cb, NULL, FALSE},
2949  { N_("Duplicate"), NULL, multi_duplicate_invoice_cb, FALSE},
2950  { N_("Post"), NULL, multi_post_invoice_cb, FALSE},
2951  { N_("Printable Report"), NULL, multi_print_invoice_cb, TRUE},
2952  { NULL },
2953  };
2954 
2955  g_return_val_if_fail (book, NULL);
2956 
2957  /* Build parameter list in reverse order */
2958  if (inv_params == NULL)
2959  {
2960  inv_params = gnc_search_param_prepend (inv_params,
2961  _("Invoice Owner"), NULL, type,
2962  INVOICE_OWNER, NULL);
2963  inv_params = gnc_search_param_prepend (inv_params,
2964  _("Invoice Notes"), NULL, type,
2965  INVOICE_NOTES, NULL);
2966  inv_params = gnc_search_param_prepend (inv_params,
2967  _("Billing ID"), NULL, type,
2968  INVOICE_BILLINGID, NULL);
2969  inv_params = gnc_search_param_prepend (inv_params,
2970  _("Is Paid?"), NULL, type,
2971  INVOICE_IS_PAID, NULL);
2972  inv_params = gnc_search_param_prepend (inv_params,
2973  _("Date Posted"), NULL, type,
2974  INVOICE_POSTED, NULL);
2975  inv_params = gnc_search_param_prepend (inv_params,
2976  _("Is Posted?"), NULL, type,
2977  INVOICE_IS_POSTED, NULL);
2978  inv_params = gnc_search_param_prepend (inv_params,
2979  _("Date Opened"), NULL, type,
2980  INVOICE_OPENED, NULL);
2981  inv_params = gnc_search_param_prepend (inv_params,
2982  _("Due Date"), NULL, type,
2983  INVOICE_DUE, NULL);
2984  inv_params = gnc_search_param_prepend (inv_params,
2985  _("Company Name "), NULL, type,
2986  INVOICE_OWNER, OWNER_PARENT,
2987  OWNER_NAME, NULL);
2988  inv_params = gnc_search_param_prepend (inv_params,
2989  _("Invoice ID"), NULL, type,
2990  INVOICE_ID, NULL);
2991  }
2992  if (bill_params == NULL)
2993  {
2994  bill_params = gnc_search_param_prepend (bill_params,
2995  _("Bill Owner"), NULL, type,
2996  INVOICE_OWNER, NULL);
2997  bill_params = gnc_search_param_prepend (bill_params,
2998  _("Bill Notes"), NULL, type,
2999  INVOICE_NOTES, NULL);
3000  bill_params = gnc_search_param_prepend (bill_params,
3001  _("Billing ID"), NULL, type,
3002  INVOICE_BILLINGID, NULL);
3003  bill_params = gnc_search_param_prepend (bill_params,
3004  _("Is Paid?"), NULL, type,
3005  INVOICE_IS_PAID, NULL);
3006  bill_params = gnc_search_param_prepend (bill_params,
3007  _("Date Posted"), NULL, type,
3008  INVOICE_POSTED, NULL);
3009  bill_params = gnc_search_param_prepend (bill_params,
3010  _("Is Posted?"), NULL, type,
3011  INVOICE_IS_POSTED, NULL);
3012  bill_params = gnc_search_param_prepend (bill_params,
3013  _("Date Opened"), NULL, type,
3014  INVOICE_OPENED, NULL);
3015  bill_params = gnc_search_param_prepend (bill_params,
3016  _("Due Date"), NULL, type,
3017  INVOICE_DUE, NULL);
3018  bill_params = gnc_search_param_prepend (bill_params,
3019  _("Company Name "), NULL, type,
3020  INVOICE_OWNER, OWNER_PARENT,
3021  OWNER_NAME, NULL);
3022  bill_params = gnc_search_param_prepend (bill_params,
3023  _("Bill ID"), NULL, type,
3024  INVOICE_ID, NULL);
3025  }
3026  if (emp_params == NULL)
3027  {
3028  emp_params = gnc_search_param_prepend (emp_params,
3029  _("Voucher Owner"), NULL, type,
3030  INVOICE_OWNER, NULL);
3031  emp_params = gnc_search_param_prepend (emp_params,
3032  _("Voucher Notes"), NULL, type,
3033  INVOICE_NOTES, NULL);
3034  emp_params = gnc_search_param_prepend (emp_params,
3035  _("Billing ID"), NULL, type,
3036  INVOICE_BILLINGID, NULL);
3037  emp_params = gnc_search_param_prepend (emp_params,
3038  _("Is Paid?"), NULL, type,
3039  INVOICE_IS_PAID, NULL);
3040  emp_params = gnc_search_param_prepend (emp_params,
3041  _("Date Posted"), NULL, type,
3042  INVOICE_POSTED, NULL);
3043  emp_params = gnc_search_param_prepend (emp_params,
3044  _("Is Posted?"), NULL, type,
3045  INVOICE_IS_POSTED, NULL);
3046  emp_params = gnc_search_param_prepend (emp_params,
3047  _("Date Opened"), NULL, type,
3048  INVOICE_OPENED, NULL);
3049  emp_params = gnc_search_param_prepend (emp_params,
3050  _("Due Date"), NULL, type,
3051  INVOICE_DUE, NULL);
3052  emp_params = gnc_search_param_prepend (emp_params,
3053  _("Employee Name"), NULL, type,
3054  INVOICE_OWNER, OWNER_PARENT,
3055  OWNER_NAME, NULL);
3056  emp_params = gnc_search_param_prepend (emp_params,
3057  _("Voucher ID"), NULL, type,
3058  INVOICE_ID, NULL);
3059  }
3060 
3061  /* Build the column list in reverse order */
3062  if (columns == NULL)
3063  {
3064  columns = gnc_search_param_prepend (columns, _("Billing ID"), NULL, type,
3065  INVOICE_BILLINGID, NULL);
3066  columns = gnc_search_param_prepend (columns, _("Type"), NULL, type,
3067  INVOICE_TYPE_STRING, NULL);
3068  columns = gnc_search_param_prepend_with_justify (columns, _("Paid"),
3069  GTK_JUSTIFY_CENTER, NULL, type,
3070  INVOICE_IS_PAID, NULL);
3071  columns = gnc_search_param_prepend (columns, _("Posted"), NULL, type,
3072  INVOICE_POSTED, NULL);
3073  columns = gnc_search_param_prepend (columns, _("Company"), NULL, type,
3074  INVOICE_OWNER, OWNER_PARENT,
3075  OWNER_NAME, NULL);
3076  columns = gnc_search_param_prepend (columns, _("Due"), NULL, type,
3077  INVOICE_DUE, NULL);
3078  columns = gnc_search_param_prepend (columns, _("Opened"), NULL, type,
3079  INVOICE_OPENED, NULL);
3080  columns = gnc_search_param_prepend (columns, _("Num"), NULL, type,
3081  INVOICE_ID, NULL);
3082  }
3083 
3084  /* Build the queries */
3085  q = qof_query_create_for (type);
3086  qof_query_set_book (q, book);
3087 
3088  /* If owner is supplied, limit all searches to invoices who's owner
3089  * or end-owner is the supplied owner! Show all invoices by this
3090  * owner. If a Job is supplied, search for all invoices for that
3091  * job, but if a Customer is supplied, search for all invoices owned
3092  * by that Customer or any of that Customer's Jobs. In other words,
3093  * match on <supplied-owner's guid> == Invoice->Owner->GncGUID or
3094  * Invoice->owner->parentGUID.
3095  */
3096  if (owner)
3097  {
3098  /* First, figure out the type of owner here.. */
3099  owner_type = gncOwnerGetType (gncOwnerGetEndOwner (owner));
3100 
3101  /* Then if there's an actual owner add it to the query
3102  * and limit the search to this owner
3103  * If there's only a type, limit the search to this type.
3104  */
3105  if (gncOwnerGetGUID (owner))
3106  {
3107  q2 = qof_query_create ();
3108  qof_query_add_guid_match (q2, g_slist_prepend
3109  (g_slist_prepend (NULL, QOF_PARAM_GUID),
3110  INVOICE_OWNER),
3111  gncOwnerGetGUID (owner), QOF_QUERY_OR);
3112 
3113  qof_query_add_guid_match (q2, g_slist_prepend
3114  (g_slist_prepend (NULL, OWNER_PARENTG),
3115  INVOICE_OWNER),
3116  gncOwnerGetGUID (owner), QOF_QUERY_OR);
3117  qof_query_merge_in_place (q, q2, QOF_QUERY_AND);
3118  qof_query_destroy (q2);
3119 
3120  /* Use this base query as pre-fill query.
3121  * This will pre-fill the search dialog with the query results
3122  */
3123  q2 = qof_query_copy (q);
3124 
3125  }
3126  else
3127  {
3128  QofQuery *q3 = qof_query_create ();
3129  QofQueryPredData *inv_type_pred = NULL;
3130  GList *type_list = NULL, *node = NULL;
3131 
3132  type_list = gncInvoiceGetTypeListForOwnerType(owner_type);
3133  for (node = type_list; node; node = node->next)
3134  {
3135  inv_type_pred = qof_query_int32_predicate(QOF_COMPARE_EQUAL,
3136  GPOINTER_TO_INT(node->data));
3137  qof_query_add_term (q3, g_slist_prepend (NULL, INVOICE_TYPE), inv_type_pred, QOF_QUERY_OR);
3138  }
3139  qof_query_merge_in_place (q, q3, QOF_QUERY_AND);
3140  qof_query_destroy (q3);
3141 
3142  /* Don't set a pre-fill query in this case, the result set would be too long */
3143  q2 = NULL;
3144  }
3145  }
3146 
3147  /* Launch select dialog and return the result */
3148  sw = g_new0 (struct _invoice_select_window, 1);
3149 
3150  if (owner)
3151  {
3152  gncOwnerCopy (owner, &(sw->owner_def));
3153  sw->owner = &(sw->owner_def);
3154  }
3155  sw->book = book;
3156  sw->q = q;
3157 
3158  switch (owner_type)
3159  {
3160  case GNC_OWNER_VENDOR:
3161  title = _("Find Bill");
3162  label = _("Bill");
3163  params = bill_params;
3164  buttons = bill_buttons;
3165  break;
3166  case GNC_OWNER_EMPLOYEE:
3167  title = _("Find Expense Voucher");
3168  label = _("Expense Voucher");
3169  params = emp_params;
3170  buttons = emp_buttons;
3171  break;
3172  default:
3173  title = _("Find Invoice");
3174  label = _("Invoice");
3175  params = inv_params;
3176  buttons = inv_buttons;
3177  break;
3178  }
3179  return gnc_search_dialog_create (type, title, params, columns, q, q2,
3180  buttons, NULL, new_invoice_cb,
3181  sw, free_invoice_cb, GNC_PREFS_GROUP_SEARCH,
3182  label);
3183 }
3184 
3186 gnc_invoice_show_bills_due (QofBook *book, double days_in_advance)
3187 {
3188  QofIdType type = GNC_INVOICE_MODULE_NAME;
3189  Query *q;
3190  QofQueryPredData* pred_data;
3191  time64 end_date;
3192  GList *res;
3193  gchar *message;
3194  DialogQueryView *dialog;
3195  gint len;
3196  Timespec ts;
3197  static GList *param_list = NULL;
3198  static GNCDisplayViewButton buttons[] =
3199  {
3200  { N_("View/Edit Bill"), edit_invoice_direct },
3201  { N_("Process Payment"), pay_invoice_direct },
3202  { NULL },
3203  };
3204 
3205  /* Create the param list (in reverse order) */
3206  if (param_list == NULL)
3207  {
3208  /* Translators: This abbreviation is the column heading for
3209  the condition "Is this invoice a Credit Note?" */
3210  param_list = gnc_search_param_prepend (param_list, _("CN?"), NULL, type,
3211  INVOICE_IS_CN, NULL);
3212  param_list = gnc_search_param_prepend (param_list, _("Amount"), NULL, type,
3213  INVOICE_POST_LOT, LOT_BALANCE, NULL);
3214  param_list = gnc_search_param_prepend (param_list, _("Company"), NULL, type,
3215  INVOICE_OWNER, OWNER_NAME, NULL);
3216  param_list = gnc_search_param_prepend (param_list, _("Due"), NULL, type,
3217  INVOICE_DUE, NULL);
3218  }
3219 
3220  /* Create the query to search for invoices; set the book */
3221  q = qof_query_create();
3222  qof_query_search_for(q, GNC_INVOICE_MODULE_NAME);
3223  qof_query_set_book (q, book);
3224 
3225  /* We want to find all invoices where:
3226  * invoice -> is_posted == TRUE
3227  * AND invoice -> lot -> is_closed? == FALSE
3228  * AND invoice -> type != customer invoice
3229  * AND invoice -> type != customer credit note
3230  * AND invoice -> due <= (today + days_in_advance)
3231  */
3232 
3233  qof_query_add_boolean_match (q, g_slist_prepend(NULL, INVOICE_IS_POSTED), TRUE,
3234  QOF_QUERY_AND);
3235 
3236  qof_query_add_boolean_match (q, g_slist_prepend(g_slist_prepend(NULL, LOT_IS_CLOSED),
3237  INVOICE_POST_LOT), FALSE, QOF_QUERY_AND);
3238 
3239  pred_data = qof_query_int32_predicate (QOF_COMPARE_NEQ, GNC_INVOICE_CUST_INVOICE);
3240  qof_query_add_term (q, g_slist_prepend(NULL, INVOICE_TYPE), pred_data, QOF_QUERY_AND);
3241 
3242  pred_data = qof_query_int32_predicate (QOF_COMPARE_NEQ, GNC_INVOICE_CUST_CREDIT_NOTE);
3243  qof_query_add_term (q, g_slist_prepend(NULL, INVOICE_TYPE), pred_data, QOF_QUERY_AND);
3244 
3245  end_date = gnc_time (NULL);
3246  if (days_in_advance < 0)
3247  days_in_advance = 0;
3248  end_date += days_in_advance * 60 * 60 * 24;
3249 
3250  ts.tv_sec = (gint64) end_date;
3251  ts.tv_nsec = 0;
3252  pred_data = qof_query_date_predicate (QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, ts);
3253  qof_query_add_term (q, g_slist_prepend(NULL, INVOICE_DUE), pred_data, QOF_QUERY_AND);
3254 
3255  res = qof_query_run(q);
3256  len = g_list_length (res);
3257  if (!res || len <= 0)
3258  {
3259  qof_query_destroy(q);
3260  return NULL;
3261  }
3262 
3263  message = g_strdup_printf
3264  (/* Translators: %d is the number of bills due. This is a
3265  ngettext(3) message. */
3266  ngettext("The following bill is due:",
3267  "The following %d bills are due:",
3268  len),
3269  len);
3270  dialog = gnc_dialog_query_view_create(param_list, q,
3271  _("Due Bills Reminder"),
3272  message,
3273  TRUE, FALSE,
3274  1, GTK_SORT_ASCENDING,
3275  buttons, NULL);
3276 
3277  g_free(message);
3278  qof_query_destroy(q);
3279  return dialog;
3280 }
3281 
3282 void
3283 gnc_invoice_remind_bills_due (void)
3284 {
3285  QofBook *book;
3286  gint days;
3287 
3288  if (!gnc_current_session_exist()) return;
3289  book = qof_session_get_book(gnc_get_current_session());
3290  days = gnc_prefs_get_float(GNC_PREFS_GROUP_BILL, GNC_PREF_DAYS_IN_ADVANCE);
3291 
3292  gnc_invoice_show_bills_due(book, days);
3293 }
3294 
3295 void
3296 gnc_invoice_remind_bills_due_cb (void)
3297 {
3298  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_BILL, GNC_PREF_NOTIFY_WHEN_DUE))
3299  return;
3300 
3301  gnc_invoice_remind_bills_due();
3302 }
3303 
GList * gncOwnerGetCommoditiesList(const GncOwner *owner)
Definition: gncOwner.c:1401
GNCPrice * gnc_price_create(QofBook *book)
Definition: gnc-pricedb.c:236
utility functions for the GnuCash UI
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
const GncGUID * gncOwnerGetGUID(const GncOwner *owner)
Definition: gncOwner.c:496
void qof_query_add_term(QofQuery *query, QofQueryParamList *param_list, QofQueryPredData *pred_data, QofQueryOp op)
int gnc_commodity_get_fraction(const gnc_commodity *cm)
time64 timespecToTime64(Timespec ts)
Business Interface: Object OWNERs.
const GncGUID * qof_instance_get_guid(gconstpointer)
void qof_instance_get(const QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_get.
void gncEntrySetQuantity(GncEntry *entry, gnc_numeric quantity)
Definition: gncEntry.c:552
Dialog for create/edit an account.
GList * gncOwnerGetAccountTypesList(const GncOwner *owner)
Definition: gncOwner.c:1383
utility functions for the GnuCash UI
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *primary_sort_params, QofQueryParamList *secondary_sort_params, QofQueryParamList *tertiary_sort_params)
gnc_numeric gnc_numeric_neg(gnc_numeric a)
gboolean string_to_guid(const gchar *string, GncGUID *guid)
void qof_instance_set(QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
gboolean timespec_equal(const Timespec *ta, const Timespec *tb)
gboolean gncOwnerEqual(const GncOwner *a, const GncOwner *b)
Definition: gncOwner.c:382
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
QofQuery * qof_query_copy(QofQuery *q)
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
void gncInvoiceRemoveEntries(GncInvoice *invoice)
Definition: gncInvoice.c:727
gboolean gnc_numeric_zero_p(gnc_numeric a)
Timespec gncEntryGetDate(const GncEntry *entry)
Definition: gncEntry.c:878
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
GHashTable * gncInvoiceGetForeignCurrencies(const GncInvoice *invoice)
Definition: gncInvoice.c:1236
gboolean qof_commit_edit(QofInstance *inst)
gnc_numeric gncInvoiceGetTotal(GncInvoice *invoice)
Definition: gncInvoice.c:908
struct _QofQuery QofQuery
Definition: qofquery.h:90
void gnc_plugin_page_invoice_update_menus(GncPluginPage *page, gboolean is_posted, gboolean can_unpost)
void gnc_main_window_display_page(GncPluginPage *page)
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Functions for adding content to a window.
QofInstance * qofOwnerGetOwner(const GncOwner *owner)
Definition: gncOwner.c:253
Definition: guid.h:65
gnc_commodity * gnc_default_currency(void)
Definition: gnc-ui-util.c:939
const gchar * QofIdType
Definition: qofid.h:85
gint timespec_cmp(const Timespec *ta, const Timespec *tb)
gboolean qof_begin_edit(QofInstance *inst)
QofBook * qof_session_get_book(const QofSession *session)
gchar * gnc_account_get_full_name(const Account *account)
Definition: Account.c:3038
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
void qof_query_destroy(QofQuery *q)
void gnc_plugin_page_set_use_new_window(GncPluginPage *page, gboolean use_new)
Definition: gncJob.c:41
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
GncPluginPage * gnc_plugin_page_invoice_new(InvoiceWindow *iw)
void qof_query_set_book(QofQuery *q, QofBook *book)
QofIdTypeConst qofOwnerGetType(const GncOwner *owner)
Definition: gncOwner.c:208
Functions for adding plugins to a GnuCash window.
#define gncInvoiceGetGUID(x)
Definition: gncInvoice.h:307
void gncEntrySetDateGDate(GncEntry *entry, const GDate *date)
Definition: gncEntry.c:505
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
void gnc_table_init_gui(GtkWidget *widget, gchar *state_section)
Definition: table-gnome.c:157
void gnc_gnome_help(const char *file_name, const char *anchor)
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Definition: gnc-ui-util.c:1437
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
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.
void gncInvoiceSetDateOpenedGDate(GncInvoice *invoice, const GDate *date)
Definition: gncInvoice.c:480
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
GncOwnerType gncOwnerGetType(const GncOwner *owner)
Definition: gncOwner.c:201
const GncOwner * gncOwnerGetEndOwner(const GncOwner *owner)
Definition: gncOwner.c:550
Business Invoice Interface.
GncJob * gncOwnerGetJob(const GncOwner *owner)
Definition: gncOwner.c:354
gboolean gncInvoiceUnpost(GncInvoice *invoice, gboolean reset_tax_tables)
Definition: gncInvoice.c:1621
GList * qof_query_run(QofQuery *query)
GDate helper routines.
const GncGUID * guid_null(void)
Timespec timespec_now(void)
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
void gnc_main_window_close_page(GncPluginPage *page)
gnc_numeric gncEntryGetQuantity(const GncEntry *entry)
Definition: gncEntry.c:919
#define QUERY_DEFAULT_SORT
Definition: qofquery.h:106
GncVendor * gncOwnerGetVendor(const GncOwner *owner)
Definition: gncOwner.c:361
GncCustomer * gncOwnerGetCustomer(const GncOwner *owner)
Definition: gncOwner.c:347
time64 gnc_time(time64 *tbuf)
get the current local time
gint64 time64
Definition: gnc-date.h:83
void qof_query_add_boolean_match(QofQuery *q, QofQueryParamList *param_list, gboolean value, QofQueryOp op)
GncEmployee * gncOwnerGetEmployee(const GncOwner *owner)
Definition: gncOwner.c:368
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
GncInvoice * gncInvoiceCopy(const GncInvoice *from)
Definition: gncInvoice.c:336
QofQuery * qof_query_create(void)
void qof_query_search_for(QofQuery *query, QofIdTypeConst obj_type)
void gnc_plugin_page_invoice_update_title(GncPluginPage *plugin_page)
void gnc_gdate_set_time64(GDate *gd, time64 time)
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:227
void qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op)
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
Definition: Account.c:1827
GDate * gnc_g_date_new_today(void)