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