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