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