GnuCash c935c2f+
Loading...
Searching...
No Matches
dialog-payment.c
1/*
2 * dialog-payment.c -- Dialog for payment entry
3 * Copyright (C) 2002,2006 Derek Atkins
4 * Author: Derek Atkins <warlord@MIT.EDU>
5 * Copyright (c) 2006 David Hampton <hampton@employees.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, contact:
19 *
20 * Free Software Foundation Voice: +1-617-542-5942
21 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
22 * Boston, MA 02110-1301, USA gnu@gnu.org
23 */
24
25#include <config.h>
26
27#include <gtk/gtk.h>
28#include <glib/gi18n.h>
29
30#include "dialog-utils.h"
31#include "gnc-component-manager.h"
32#include "gnc-ui.h"
33#include "gnc-gui-query.h"
34#include "gnc-ui-util.h"
35#include <gnc-glib-utils.h>
36#include "qof.h"
37#include "gnc-date.h"
38#include "gnc-date-edit.h"
39#include "gnc-amount-edit.h"
40#include "gnc-gtk-utils.h"
41#include "gnc-prefs.h"
43#include "tree-view-utils.h"
44#include "Transaction.h"
45#include "Account.h"
46#include "gncOwner.h"
47#include "engine-helpers.h"
48
49#include "gncInvoice.h"
50
51#include "dialog-payment.h"
52#include "business-gnome-utils.h"
53
54#include "dialog-transfer.h"
55#include "dialog-print-check.h"
56#include "gnc-general-search.h"
57#include <qoflog.h>
58
59static const QofLogModule log_module = G_LOG_DOMAIN;
60
61#define DIALOG_PAYMENT_CM_CLASS "payment-dialog"
62#define GNC_PREFS_GROUP "dialogs.process-payment"
63
64typedef enum
65{
66 COL_OWNER_TYPE_NAME ,
67 COL_OWNER_TYPE_NUM ,
68} OwnerTypeCols;
69
70typedef struct
71{
72 GNCLot * lot;
73 gnc_numeric amount;
75
76typedef struct
77{
78 GncOwner owner;
79 Transaction * txn;
80 Account * post_acct;
81 GList * lots;
83
85{
86 GtkWidget * dialog;
87
88 GtkWidget * payment_warning;
89 GtkWidget * conflict_message;
90 GtkWidget * ok_button;
91 GtkWidget * num_entry;
92 GtkWidget * memo_entry;
93 GtkWidget * post_combo;
94 GtkWidget * owner_box;
95 GtkWidget * owner_type_combo;
96 GtkWidget * owner_choice;
97 GtkWidget * amount_debit_edit;
98 GtkWidget * amount_credit_edit;
99 GtkWidget * amount_payment_box;
100 GtkWidget * amount_refund_box;
101 GtkWidget * date_edit;
102 GtkWidget * acct_tree;
103 GtkWidget * docs_list_tree_view;
104 GtkWidget * commodity_label;
105 GtkWidget * print_check;
106
107 gint component_id;
108 QofBook * book;
109 GncOwner owner;
110 GncOwnerType owner_type;
111 Account * post_acct;
112 Account * xfer_acct;
113 gnc_numeric amount_tot;
114 GList * acct_types;
115 GList * acct_commodities;
116
117 InitialPaymentInfo *tx_info;
118 gboolean print_check_state;
119};
120
121void gnc_ui_payment_window_set_num (PaymentWindow *pw, const char* num)
122{
123 g_assert(pw);
124 gtk_entry_set_text(GTK_ENTRY (pw->num_entry), num);
125}
126void gnc_ui_payment_window_set_memo (PaymentWindow *pw, const char* memo)
127{
128 g_assert(pw);
129 gtk_entry_set_text(GTK_ENTRY (pw->memo_entry), memo);
130}
131void gnc_ui_payment_window_set_date (PaymentWindow *pw, const GDate *date)
132{
133 g_assert(pw);
134 g_assert(date);
135 gnc_date_edit_set_gdate (GNC_DATE_EDIT (pw->date_edit), date);
136}
137void gnc_ui_payment_window_set_amount (PaymentWindow *pw, gnc_numeric amount)
138{
139 g_assert(pw);
140
141 /* Debits are negative, credits are positive */
142 if (gnc_numeric_positive_p (amount))
143 {
144 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_credit_edit),
145 amount);
146 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_debit_edit),
147 gnc_numeric_zero ());
148 }
149 else
150 {
151 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_debit_edit),
152 gnc_numeric_neg (amount));
153 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_credit_edit),
154 gnc_numeric_zero ());
155 }
156
157}
158
159static void gnc_ui_payment_window_set_commodity (PaymentWindow *pw, const Account* account)
160{
161 gchar *comm_string;
162 gnc_commodity *comm;
163
164 g_assert(pw);
165 g_assert(account);
166
167 comm = xaccAccountGetCommodity (account);
168 comm_string = g_strconcat ("(", gnc_commodity_get_nice_symbol (comm), ")", NULL);
169 gtk_label_set_text (GTK_LABEL(pw->commodity_label), comm_string);
170 g_free (comm_string);
171}
172
173void gnc_ui_payment_window_set_postaccount (PaymentWindow *pw, const Account* account)
174{
175 g_assert(pw);
176 g_assert(account);
177 {
178 gchar *acct_string = gnc_account_get_full_name (account);
179 gnc_cbwe_set_by_string(GTK_COMBO_BOX(pw->post_combo), acct_string);
180 g_free(acct_string);
181 }
182
183 gnc_ui_payment_window_set_commodity (pw, account);
184}
185
186void gnc_ui_payment_window_set_xferaccount (PaymentWindow *pw, const Account* account)
187{
188 g_assert(pw);
189 g_assert(account);
190 gnc_tree_view_account_set_selected_account(GNC_TREE_VIEW_ACCOUNT(pw->acct_tree),
191 (Account*)account);
192}
193
194static gboolean gnc_payment_dialog_has_pre_existing_txn(const PaymentWindow* pw)
195{
196 return pw->tx_info->txn != NULL;
197}
198int gnc_payment_dialog_owner_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data);
199int gnc_payment_dialog_post_to_changed_cb (GtkWidget *widget, gpointer data);
200void gnc_payment_dialog_document_selection_changed_cb (GtkWidget *widget, gpointer data);
201void gnc_payment_dialog_xfer_acct_changed_cb (GtkWidget *widget, gpointer data);
202void gnc_payment_ok_cb (GtkWidget *widget, gpointer data);
203void gnc_payment_cancel_cb (GtkWidget *widget, gpointer data);
204void gnc_payment_window_destroy_cb (GtkWidget *widget, gpointer data);
205void gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *path,
206 GtkTreeViewColumn *column, PaymentWindow *pw);
207void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event,
208 PaymentWindow *pw);
209void gnc_payment_activate_amount_cb (GtkWidget *widget, PaymentWindow *pw);
210void gnc_payment_window_fill_docs_list (PaymentWindow *pw);
211
212
213static void
214gnc_payment_window_refresh_handler (G_GNUC_UNUSED GHashTable *changes, gpointer data)
215{
216 PaymentWindow *pw = data;
217
218 gnc_payment_window_fill_docs_list (pw);
219 pw->post_acct = gnc_account_select_combo_fill (pw->post_combo, pw->book, pw->acct_types, NULL);
220}
221
222static gboolean
223gnc_payment_window_check_payment (PaymentWindow *pw)
224{
225 const char *conflict_msg = NULL;
226 gnc_numeric amount_deb, amount_cred;
227 gboolean enable_xfer_acct = TRUE;
228 gboolean allow_payment = TRUE;
229 GtkTreeSelection *selection;
230 gint c_result, d_result;
231
232 if (!pw)
233 return FALSE;
234
235 /* Verify the "post" account */
236 if (!pw->post_acct)
237 {
238 conflict_msg = _("You must enter a valid account name for posting.");
239 allow_payment = FALSE;
240 goto update_cleanup;
241 }
242
243 /* Verify the user has selected an owner */
244 gnc_owner_get_owner (pw->owner_choice, &(pw->owner));
245 if (!gncOwnerIsValid(&pw->owner))
246 {
247 conflict_msg = _("You must select a company for payment processing.");
248 allow_payment = FALSE;
249 goto update_cleanup;
250 }
251
252 /* Verify the credit / debit amounts are valid */
253 d_result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(pw->amount_debit_edit),
254 &amount_deb, FALSE, NULL);
255
256 c_result = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(pw->amount_credit_edit),
257 &amount_cred, FALSE, NULL);
258
259 if ((d_result == 1) || (c_result == 1))
260 {
261 conflict_msg = _("There is a problem with the Payment or Refund amount.");
262 allow_payment = FALSE;
263 goto update_cleanup;
264 }
265
266 /* Test the total amount */
267 pw->amount_tot = gnc_numeric_sub (amount_cred, amount_deb,
269 xaccAccountGetCommodity (pw->post_acct)),
270 GNC_HOW_RND_ROUND_HALF_UP);
271
272 if (gnc_numeric_check (pw->amount_tot) || gnc_numeric_zero_p (pw->amount_tot))
273 {
274 enable_xfer_acct = FALSE;
275 }
276 else
277 {
278 /* Verify the user has selected a transfer account */
279 pw->xfer_acct = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(pw->acct_tree));
280 if (!pw->xfer_acct)
281 {
282 conflict_msg = _("You must select a transfer account from the account tree.");
283 allow_payment = FALSE;
284 goto update_cleanup;
285 }
286 }
287
288 /* this last test checks whether documents were selected. if none,
289 emit warning but still allow as an unattached payment. */
290 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
291 if (gtk_tree_selection_count_selected_rows (selection) == 0)
292 {
293 conflict_msg = _("No business items were selected to assign this payment to. This may create an unattached payment.");
294 allow_payment = TRUE;
295 }
296
297
298update_cleanup:
299 gtk_widget_set_sensitive (pw->acct_tree, enable_xfer_acct);
300
301 /* Disable "Print Check" widget if amount is zero but save current
302 state to restore when the widget is re-enabled */
303 if (gtk_widget_is_sensitive (pw->print_check))
304 pw->print_check_state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(pw->print_check));
305 if (!enable_xfer_acct)
306 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(pw->print_check), FALSE);
307 gtk_widget_set_sensitive (pw->print_check, enable_xfer_acct);
308 if (gtk_widget_is_sensitive (pw->print_check))
309 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(pw->print_check), pw->print_check_state);
310
311 /* Check if there are issues preventing a successful payment */
312 gtk_label_set_text (GTK_LABEL(pw->conflict_message), conflict_msg);
313 gtk_widget_set_sensitive (pw->ok_button, allow_payment);
314 if (conflict_msg)
315 {
316 gtk_widget_show (pw->payment_warning);
317 }
318 else
319 {
320 gtk_widget_hide (pw->payment_warning);
321 }
322
323 return allow_payment;
324}
325
326static void
327gnc_payment_window_close_handler (gpointer data)
328{
329 PaymentWindow *pw = data;
330
331 if (!pw) return;
332 gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pw->dialog));
333 gtk_widget_destroy (pw->dialog);
334}
335
336static void
337calculate_selected_total_helper (GtkTreeModel *model,
338 G_GNUC_UNUSED GtkTreePath *path,
339 GtkTreeIter *iter,
340 gpointer data)
341{
342 gnc_numeric *subtotal = (gnc_numeric*) data;
343 gnc_numeric cur_val;
344 GNCLot *lot;
345 Account *acct;
346 gnc_commodity *currency;
347
348 gtk_tree_model_get (model, iter, 5, &lot, -1);
349
350 /* Find the amount's currency to determine the required precision */
351 acct = gnc_lot_get_account (lot);
352 currency = xaccAccountGetCommodity (acct);
353
354 cur_val = gnc_lot_get_balance (lot);
355 *subtotal = gnc_numeric_add (*subtotal, cur_val,
356 gnc_commodity_get_fraction (currency), GNC_HOW_RND_ROUND_HALF_UP);
357}
358
359static gnc_numeric
360gnc_payment_dialog_calculate_selected_total (PaymentWindow *pw)
361{
362 GtkTreeSelection *selection;
363 gnc_numeric val = gnc_numeric_zero();
364
365 if (!pw->docs_list_tree_view || !GTK_IS_TREE_VIEW(pw->docs_list_tree_view))
366 return gnc_numeric_zero();
367
368 /* Figure out if anything is set in the current list */
369 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
370
371 gtk_tree_selection_selected_foreach (selection,
372 calculate_selected_total_helper,
373 (gpointer) &val);
374
375 return val;
376}
377
378static void
379gnc_payment_dialog_document_selection_changed (PaymentWindow *pw)
380{
381 gnc_numeric val;
382
383 /* Don't change the amount based on the selected documents
384 * in case this payment is from a pre-existing txn
385 */
386 if (gnc_payment_dialog_has_pre_existing_txn (pw))
387 return;
388
389 /* Set the payment amount in the dialog */
390 val = gnc_payment_dialog_calculate_selected_total (pw);
391 gnc_ui_payment_window_set_amount(pw, val);
392}
393
394static gint
395_gnc_lotinfo_find_by_lot(PreExistLotInfo *lotinfo_inst, GNCLot *lot_to_find)
396{
397 if (lotinfo_inst->lot == lot_to_find)
398 return 0;
399 return -1;
400}
401
402static void
403gnc_payment_dialog_highlight_documents (PaymentWindow *pw)
404{
405 gboolean selection_changed = FALSE;
406 GtkTreeIter iter;
407 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(pw->docs_list_tree_view));
408 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
409 gtk_tree_selection_unselect_all (selection);
410
411 if (gtk_tree_model_get_iter_first (model, &iter))
412 {
413 do
414 {
415 GNCLot *lot;
416 GList *li_node;
417
418 gtk_tree_model_get (model, &iter, 5, &lot, -1);
419
420 if (!lot)
421 continue; /* Lot has been deleted behind our back... */
422
423 li_node = g_list_find_custom (pw->tx_info->lots, lot,
424 (GCompareFunc)_gnc_lotinfo_find_by_lot);
425 if (li_node)
426 {
427 gtk_tree_selection_select_iter (selection, &iter);
428 selection_changed = TRUE;
429 }
430 }
431 while (gtk_tree_model_iter_next (model, &iter));
432 }
433
434 if (selection_changed)
435 gnc_payment_dialog_document_selection_changed (pw);
436}
437
438
439void
440gnc_payment_window_fill_docs_list (PaymentWindow *pw)
441{
442 GtkListStore *store;
443 GtkTreeSelection *selection;
444 GList *list = NULL, *node;
445
446 g_return_if_fail (pw->docs_list_tree_view && GTK_IS_TREE_VIEW(pw->docs_list_tree_view));
447
448 /* Get a list of open lots for this owner and post account */
449 if (pw->owner.owner.undefined && pw->post_acct)
451 &pw->owner, NULL);
452
453 /* If pre-existing transaction's post account equals the selected post account
454 * and we have lots for this transaction then compensate the document list for those.
455 * The presence of such lots indicates the pre-existing transaction is an existing payment that
456 * we are about to replace. So we should make sure this existing payment info can be reselected
457 * by the user (within the practical limits of the payment window*) to redo the same
458 * payment again.
459 * If the txn's lots are closed they are ignored by default so we should explicitly readd
460 * them here.
461 * And for all lots in the pre-existing transaction we need to readd the split amount
462 * for that lot or the existing payment values would not be taken into account.
463 * This will happen further below though.
464 *
465 * Finally all this is only relevant if the lot belongs to the same owner...
466 *
467 * * The practical limits are
468 * - The payment dialog can handle only one transfer split. If the pre-existing
469 * transaction has more possible candidates, all but the first will be used
470 * - The payment dialog can't handle AR/AP splits that aren't linked to a lot
471 * in the current post account. Such splits will be ignored as well.
472 * In both cases the user will have been informed before and given the option to abort.
473 */
474 if (pw->tx_info->post_acct == pw->post_acct)
475 for (node = pw->tx_info->lots; node; node = node->next)
476 {
477 PreExistLotInfo *lot_info = node->data;
478 if (gnc_numeric_zero_p (gnc_lot_get_balance (lot_info->lot)))
479 /* The not-zero case will be handled below when the lot is processed as part of the open lots */
480 {
481 GncOwner lotowner;
482 gncOwnerInitUndefined(&lotowner, NULL);
483 if (!gncOwnerGetOwnerFromLot(lot_info->lot, &lotowner))
484 {
485 const GncOwner *owner;
486 const GncInvoice *invoice = gncInvoiceGetInvoiceFromLot(lot_info->lot);
487 if (invoice)
488 {
489 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
490 gncOwnerCopy (owner, &lotowner);
491 }
492 }
493 if (gncOwnerEqual(&pw->owner, &lotowner))
494 list = g_list_prepend (list, lot_info->lot);
495 }
496 }
497
498 /* Clear the existing list */
499 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
500 gtk_tree_selection_unselect_all (selection);
501 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pw->docs_list_tree_view)));
502 gtk_list_store_clear(store);
503
504 /* Add the documents and overpayments to the tree view */
505 for (node = list; node; node = node->next)
506 {
507 GNCLot *lot = node->data;
508 GList *li_node;
509 time64 doc_date_time = 0;
510 const gchar *doc_type_str = NULL;
511 const gchar *doc_id_str = NULL;
512 const gchar *doc_deb_str = NULL;
513 const gchar *doc_cred_str = NULL;
514 GtkTreeIter iter;
515 GncInvoice *document;
516 gnc_numeric value;
517 gnc_numeric debit = gnc_numeric_zero();
518 gnc_numeric credit = gnc_numeric_zero();
519
520 /* Find the lot's document if it exists,
521 * it could also be a prepayment lot. */
522 document = gncInvoiceGetInvoiceFromLot (lot);
523
524 /* Find the document's date or pre-payment date */
525 if (document)
526 doc_date_time = gncInvoiceGetDatePosted (document);
527 else
528 {
529 /* Calculate the payment date based on the lot splits */
530 Transaction *trans = xaccSplitGetParent (gnc_lot_get_latest_split (lot));
531 if (trans)
532 doc_date_time = xaccTransRetDatePosted (trans);
533 else
534 continue; /* No valid split in this lot, skip it */
535 }
536
537 /* Find the document type. No type means pre-payment in this case */
538 if (document)
539 {
540 doc_type_str = gncInvoiceGetTypeString (document);
541 }
542 else
543 doc_type_str = _("Pre-Payment");
544
545 /* Find the document id. Empty for pre-payments. */
546 if (document)
547 {
548 doc_id_str = gncInvoiceGetID (document);
549 }
550
551 /* Find the debit/credit amount.
552 * Invoices/vendor credit notes are debit (increasing the balance)
553 * Customer credit notes/bills are credit (decreasing the balance)
554 * Pre-payments are debit or credit depending on their sign
555 */
556 value = gnc_lot_get_balance (lot);
557
558 /* If this lot is linked to the pre-existing transaction, compensate
559 * its amount so the same pre-existing transaction can be reselected bye the user
560 * (within applicable limits)
561 */
562 li_node = g_list_find_custom (pw->tx_info->lots, lot,
563 (GCompareFunc)_gnc_lotinfo_find_by_lot);
564 if (li_node)
565 {
566 PreExistLotInfo *lot_info = li_node->data;
567 value = gnc_numeric_sub(value, lot_info->amount,
569 GNC_HOW_RND_ROUND_HALF_UP);
570 }
571
572 if (gnc_numeric_zero_p (value))
573 /* If the lot's balance is 0 after the above compensation, skip this lot */
574 continue;
575 else if (gnc_numeric_positive_p (value))
576 debit = value;
577 else
578 credit = gnc_numeric_neg (value);
579
580
581 /* Only display non-zero debits/credits */
582 if (!gnc_numeric_zero_p (debit))
583 doc_deb_str = xaccPrintAmount (debit, gnc_default_print_info (FALSE));
584 if (!gnc_numeric_zero_p (credit))
585 doc_cred_str = xaccPrintAmount (credit, gnc_default_print_info (FALSE));
586
587 gtk_list_store_append (store, &iter);
588 gtk_list_store_set (store, &iter,
589 0, doc_date_time,
590 1, doc_id_str,
591 2, doc_type_str,
592 3, doc_deb_str,
593 4, doc_cred_str,
594 5, (gpointer)lot,
595 -1);
596
597 }
598
599 g_list_free (list);
600
601 /* Highlight the preset invoice if it's in the new list */
602 gnc_payment_dialog_highlight_documents (pw);
603}
604
605static void
606gnc_payment_dialog_post_to_changed (PaymentWindow *pw)
607{
608 gnc_payment_window_fill_docs_list (pw);
609}
610
611static void
612gnc_payment_dialog_owner_changed (PaymentWindow *pw)
613{
614 GncOwner *owner = &pw->owner;
615
616 /* refresh the post and acc available accounts, but cleanup first */
617 if (pw->acct_types)
618 {
619 g_list_free(pw->acct_types);
620 pw->acct_types = NULL;
621 }
622
623 if (pw->acct_commodities)
624 {
625 g_list_free(pw->acct_commodities);
626 pw->acct_commodities = NULL;
627 }
628
629 pw->acct_types = gncOwnerGetAccountTypesList(owner);
630 if (gncOwnerIsValid(owner))
631 pw->acct_commodities = gncOwnerGetCommoditiesList (owner);
632
633 pw->post_acct = gnc_account_select_combo_fill (pw->post_combo, pw->book, pw->acct_types, NULL);
634 if (gncOwnerEqual(&pw->owner, &pw->tx_info->owner) && pw->tx_info->post_acct)
635 {
636 pw->post_acct = pw->tx_info->post_acct;
637 gnc_ui_payment_window_set_postaccount (pw, pw->post_acct);
638 }
639 gnc_payment_dialog_post_to_changed (pw);
640
641 if (pw->post_acct)
642 gnc_ui_payment_window_set_commodity (pw, pw->post_acct);
643
644 /* Set the last-used transfer account, but only if we didn't
645 * create this dialog from a pre-existing transaction. */
646 if (!gnc_payment_dialog_has_pre_existing_txn(pw))
647 {
648 GncGUID *guid = NULL;
649 Account *last_acct = NULL;
650
651 if (gncOwnerIsValid(owner))
653 "payment-last-account", &guid,
654 NULL);
655 last_acct = xaccAccountLookup(guid, pw->book);
656 guid_free (guid);
657 if (last_acct)
658 gnc_tree_view_account_set_selected_account(GNC_TREE_VIEW_ACCOUNT(pw->acct_tree),
659 last_acct);
660 }
661}
662
663
664static void
665gnc_payment_dialog_owner_type_changed (PaymentWindow *pw)
666{
667 GtkWidget *debit_box, *credit_box;
668
669 /* Some terminology:
670 * Invoices are paid, credit notes are refunded.
671 * A customer payment is a credit action, paying a vendor is debit
672 *
673 * So depending on the owner the payment amount should be considered
674 * credit (customer) or debit (vendor/employee) and refunds should be
675 * considered debit (customer) or credit (vendor/employee).
676 * For visual consistency, the dialog box will always show a payment and
677 * a refund field. Internally they are treated as credit or debit depending
678 * on the owner type.
679 */
680 if (pw->owner_type == GNC_OWNER_CUSTOMER)
681 {
682 debit_box = pw->amount_refund_box;
683 credit_box = pw->amount_payment_box;
684 }
685 else
686 {
687 debit_box = pw->amount_payment_box;
688 credit_box = pw->amount_refund_box;
689 }
690
691 g_object_ref (G_OBJECT (pw->amount_debit_edit));
692 g_object_ref (G_OBJECT (pw->amount_credit_edit));
693
694 if (gtk_widget_is_ancestor(pw->amount_debit_edit, credit_box))
695 gtk_container_remove (GTK_CONTAINER (credit_box), pw->amount_debit_edit);
696 if (gtk_widget_is_ancestor(pw->amount_credit_edit, debit_box))
697 gtk_container_remove (GTK_CONTAINER (debit_box), pw->amount_credit_edit);
698
699 if (!gtk_widget_is_ancestor(pw->amount_debit_edit, debit_box))
700 gtk_box_pack_start (GTK_BOX (debit_box), pw->amount_debit_edit, TRUE, TRUE, 0);
701 if (!gtk_widget_is_ancestor(pw->amount_credit_edit, credit_box))
702 gtk_box_pack_start (GTK_BOX (credit_box), pw->amount_credit_edit, TRUE, TRUE, 0);
703
704 g_object_unref (G_OBJECT (pw->amount_debit_edit));
705 g_object_unref (G_OBJECT (pw->amount_credit_edit));
706
707 /* Redo the owner_choice widget */
708 if (pw->owner_choice)
709 gtk_widget_destroy(pw->owner_choice);
710 pw->owner_choice = gnc_owner_select_create (NULL, pw->owner_box, pw->book, &pw->owner);
711 gtk_widget_show (pw->owner_choice);
712 gnc_payment_dialog_owner_changed (pw);
713
714 g_signal_connect (G_OBJECT (pw->owner_choice), "changed",
715 G_CALLBACK (gnc_payment_dialog_owner_changed_cb), pw);
716}
717
718static void
719gnc_payment_dialog_remember_account (PaymentWindow *pw, Account *acc)
720{
721 QofInstance *owner = qofOwnerGetOwner (&pw->owner);
722 const GncGUID *guid;
723
724 if (!acc) return;
725
726 guid = xaccAccountGetGUID(acc);
727 qof_begin_edit (owner);
728 qof_instance_set (owner,
729 "payment-last-account", guid,
730 NULL);
731 qof_commit_edit (owner);
732}
733
734
735static void
736gnc_payment_update_style_classes (PaymentWindow *pw)
737{
738 GtkStyleContext *stylectxt = gtk_widget_get_style_context (GTK_WIDGET(pw->dialog));
739 const gchar *style_label = NULL;
740
741 if (gtk_style_context_has_class (stylectxt, "gnc-class-customers"))
742 gtk_style_context_remove_class (stylectxt, "gnc-class-customers");
743
744 if (gtk_style_context_has_class (stylectxt, "gnc-class-vendors"))
745 gtk_style_context_remove_class (stylectxt, "gnc-class-vendors");
746
747 if (gtk_style_context_has_class (stylectxt, "gnc-class-employees"))
748 gtk_style_context_remove_class (stylectxt, "gnc-class-employees");
749
750 switch (pw->owner_type)
751 {
752 case GNC_OWNER_CUSTOMER:
753 style_label = "gnc-class-customers";
754 break;
755 case GNC_OWNER_VENDOR:
756 style_label = "gnc-class-vendors";
757 break;
758 case GNC_OWNER_EMPLOYEE:
759 style_label = "gnc-class-employees";
760 break;
761 default:
762 style_label = "gnc-class-unknown";
763 break;
764 }
765 // Set a secondary style context for this page so it can be easily manipulated with css
766 gtk_style_context_add_class (stylectxt, style_label);
767}
768
769static void
770gnc_payment_set_owner_type (PaymentWindow *pw, GncOwnerType owner_type)
771{
772 gboolean valid;
773 GtkTreeModel *store;
774 GtkTreeIter iter;
775
776 switch (owner_type)
777 {
778 case GNC_OWNER_CUSTOMER:
779 case GNC_OWNER_EMPLOYEE:
780 case GNC_OWNER_VENDOR:
781 pw->owner_type = owner_type;
782 break;
783 default:
784 pw->owner_type = GNC_OWNER_CUSTOMER;
785 }
786
787 store = gtk_combo_box_get_model (GTK_COMBO_BOX(pw->owner_type_combo));
788 valid = gtk_tree_model_get_iter_first (store, &iter);
789 while (valid)
790 {
791 GncOwnerType owner_type;
792 gtk_tree_model_get (store, &iter, COL_OWNER_TYPE_NUM, &owner_type, -1);
793 if (owner_type == pw->owner_type)
794 {
795 gtk_combo_box_set_active_iter (GTK_COMBO_BOX(pw->owner_type_combo), &iter);
796 break;
797 }
798 valid = gtk_tree_model_iter_next (store, &iter);
799 }
800 gnc_payment_update_style_classes (pw);
801
802 gnc_payment_dialog_owner_type_changed (pw);
803}
804
805int
806gnc_payment_dialog_owner_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
807{
808 PaymentWindow *pw = data;
809 GncOwner owner;
810
811 if (!pw) return FALSE;
812
813 gncOwnerCopy (&(pw->owner), &owner);
814 gnc_owner_get_owner (pw->owner_choice, &owner);
815
816 /* If this owner really changed, then reset ourselves */
817 if (!gncOwnerEqual (&owner, &(pw->owner)))
818 {
819 gncOwnerCopy (&owner, &(pw->owner));
820 gnc_payment_dialog_owner_changed(pw);
821 }
822
823 /* Reflect if the payment could complete now */
824 gnc_payment_window_check_payment (pw);
825
826 return FALSE;
827}
828
829static int
830gnc_payment_dialog_owner_type_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
831{
832 PaymentWindow *pw = data;
833 GtkTreeIter iter;
834 GtkTreeModel *model;
835 GncOwnerType owner_type;
836
837 if (!pw) return FALSE;
838
839 gtk_combo_box_get_active_iter (GTK_COMBO_BOX(pw->owner_type_combo), &iter);
840 model = gtk_combo_box_get_model (GTK_COMBO_BOX(pw->owner_type_combo));
841 gtk_tree_model_get (model, &iter, COL_OWNER_TYPE_NUM, &owner_type, -1);
842
843 if (owner_type != pw->owner_type)
844 {
845 pw->owner_type = owner_type;
846
847 /* If type changed, the currently selected owner can't be valid any more
848 * If the initial owner is of the new owner_type, we propose that one
849 * otherwise we just reset the owner
850 */
851 if (gncOwnerGetType (&pw->tx_info->owner) == pw->owner_type)
852 gncOwnerCopy (&pw->tx_info->owner, &pw->owner);
853 else
854 {
855 switch (pw->owner_type)
856 {
857 case GNC_OWNER_VENDOR:
858 gncOwnerInitVendor (&pw->owner, NULL);
859 break;
860 case GNC_OWNER_EMPLOYEE:
861 gncOwnerInitEmployee (&pw->owner, NULL);
862 break;
863 default:
864 gncOwnerInitCustomer (&pw->owner, NULL);
865 }
866
867 }
868
869 gnc_payment_dialog_owner_type_changed (pw);
870 }
871
872 /* Reflect if the payment could complete now */
873 gnc_payment_window_check_payment (pw);
874
875 return FALSE;
876}
877
878void
879gnc_payment_dialog_document_selection_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
880{
881 PaymentWindow *pw = data;
882
883 if (!pw) return;
884
885 gnc_payment_dialog_document_selection_changed (pw);
886
887 /* Reflect if the payment could complete now */
888 gnc_payment_window_check_payment (pw);
889}
890
891void
892gnc_payment_dialog_xfer_acct_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
893{
894 PaymentWindow *pw = data;
895
896 if (!pw) return;
897
898 /* Reflect if the payment could complete now */
899 gnc_payment_window_check_payment (pw);
900}
901
902int
903gnc_payment_dialog_post_to_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
904{
905 PaymentWindow *pw = data;
906 Account *post_acct;
907
908 if (!pw) return FALSE;
909
910 post_acct = gnc_account_select_combo_get_active (pw->post_combo);
911
912 /* If this post account really changed, then reset ourselves */
913 if (post_acct != pw->post_acct)
914 {
915 pw->post_acct = post_acct;
916 gnc_payment_dialog_post_to_changed(pw);
917 }
918 else
919 gnc_payment_dialog_highlight_documents (pw);
920
921 /* Reflect if the payment could complete now */
922 gnc_payment_window_check_payment (pw);
923
924 return FALSE;
925}
926
927/*
928 * This helper function is called once for each row in the tree view
929 * that is currently selected. Its task is to add the corresponding
930 * lot to the end of a glist.
931 */
932static void
933get_selected_lots (GtkTreeModel *model,
934 G_GNUC_UNUSED GtkTreePath *path,
935 GtkTreeIter *iter,
936 gpointer data)
937{
938 GList **return_list = data;
939 GNCLot *lot;
940
941 gtk_tree_model_get (model, iter, 5, &lot, -1);
942
943 if (lot)
944 *return_list = g_list_insert_sorted (*return_list, lot, (GCompareFunc)gncOwnerLotsSortFunc);
945}
946
947void
948gnc_payment_ok_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
949{
950 PaymentWindow *pw = data;
951 const char *text = NULL;
952
953 if (!pw)
954 return;
955
956 /* The gnc_payment_window_check_payment function
957 * ensures we have valid owner, post account, transfer account
958 * and amount so we can proceed with the payment.
959 * Note: make sure it's called before all entry points to this function !
960 */
961
962 /* We're on our way out, stop watching for object changes that could
963 * trigger a gui refresh. Without this the gui suspend/resume
964 * pair could still trigger a gui update on the payment dialog
965 * before we close it. This is undesired because the lots may be in
966 * an inconsistent state until after all events are handled. So
967 * the gui refresh may result in a crash.
968 * See https://bugs.gnucash.org/show_bug.cgi?id=740471
969 */
970 gnc_gui_component_clear_watches (pw->component_id);
971
972 gnc_suspend_gui_refresh ();
973 {
974 const char *memo, *num;
975 GDate date;
976 time64 t;
977 gnc_numeric exch = gnc_numeric_create(1, 1); //default to "one to one" rate
978 GList *selected_lots = NULL;
979 GtkTreeSelection *selection;
980 gboolean auto_pay;
981
982 /* Obtain all our ancillary information */
983 memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry));
984 num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry));
985 g_date_clear (&date, 1);
986 gnc_date_edit_get_gdate (GNC_DATE_EDIT (pw->date_edit), &date);
987 t = gdate_to_time64 (date);
988
989 /* Obtain the list of selected lots (documents/payments) from the dialog */
990 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
991 gtk_tree_selection_selected_foreach (selection, get_selected_lots, &selected_lots);
992
993 /* When the payment amount is 0, the selected documents cancel each other out
994 * so no money is actually transferred.
995 * For non-zero payments money will be transferred between the post account
996 * and the transfer account. In that case if these two accounts don't have
997 * the same currency the user is asked to enter the exchange rate.
998 */
999 if (!gnc_numeric_zero_p (pw->amount_tot) &&
1001 {
1002 XferDialog* xfer;
1003
1004 text = _("The transfer and post accounts are associated with different currencies. Please specify the conversion rate.");
1005
1006 xfer = gnc_xfer_dialog(pw->dialog, pw->post_acct);
1007 gnc_info_dialog (GTK_WINDOW (pw->dialog), "%s", text);
1008
1009 gnc_xfer_dialog_select_to_account(xfer, pw->xfer_acct);
1010 gnc_xfer_dialog_set_amount(xfer, pw->amount_tot);
1011 gnc_xfer_dialog_set_date (xfer, t);
1012
1013 /* All we want is the exchange rate so prevent the user from thinking
1014 it makes sense to mess with other stuff */
1015 gnc_xfer_dialog_set_from_show_button_active(xfer, FALSE);
1016 gnc_xfer_dialog_set_to_show_button_active(xfer, FALSE);
1017 gnc_xfer_dialog_hide_from_account_tree(xfer);
1018 gnc_xfer_dialog_hide_to_account_tree(xfer);
1019 gnc_xfer_dialog_is_exchange_dialog(xfer, &exch);
1020
1021 if (!gnc_xfer_dialog_run_until_done(xfer))
1022 return; /* If the user cancels, return to the payment dialog without changes */
1023 }
1024
1025 /* Perform the payment */
1026 if (gncOwnerGetType (&(pw->owner)) == GNC_OWNER_CUSTOMER)
1027 auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
1028 else
1029 auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);
1030
1031 gncOwnerApplyPaymentSecs (&pw->owner, &(pw->tx_info->txn), selected_lots,
1032 pw->post_acct, pw->xfer_acct, pw->amount_tot,
1033 exch, t, memo, num, auto_pay);
1034 }
1035 gnc_resume_gui_refresh ();
1036
1037 /* Save the transfer account, xfer_acct */
1038 gnc_payment_dialog_remember_account(pw, pw->xfer_acct);
1039
1040 if (gtk_widget_is_sensitive (pw->print_check) &&
1041 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(pw->print_check)))
1042 {
1043 Split *split = xaccTransFindSplitByAccount (pw->tx_info->txn, pw->xfer_acct);
1044 GList *splits = NULL;
1045 splits = g_list_append(splits, split);
1046 gnc_ui_print_check_dialog_create(NULL, splits, NULL);
1047 g_list_free (splits);
1048 }
1049
1050 gnc_ui_payment_window_destroy (pw);
1051}
1052
1053void
1054gnc_payment_cancel_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
1055{
1056 PaymentWindow *pw = data;
1057 gnc_ui_payment_window_destroy (pw);
1058}
1059
1060void
1061gnc_payment_window_destroy_cb (G_GNUC_UNUSED GtkWidget *widget, gpointer data)
1062{
1063 PaymentWindow *pw = data;
1064
1065 if (!pw) return;
1066
1067 gnc_unregister_gui_component (pw->component_id);
1068
1069 g_list_free (pw->acct_types);
1070 g_list_free (pw->acct_commodities);
1071 if (pw->tx_info->lots)
1072 g_list_free_full (pw->tx_info->lots, g_free);
1073 g_free (pw);
1074}
1075
1076void
1077gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *path,
1078 G_GNUC_UNUSED GtkTreeViewColumn *column, PaymentWindow *pw)
1079{
1080 GtkTreeView *view;
1081 GtkTreeModel *model;
1082 GtkTreeIter iter;
1083
1084 g_return_if_fail(widget);
1085 view = GTK_TREE_VIEW(widget);
1086
1087 model = gtk_tree_view_get_model(view);
1088 if (gtk_tree_model_get_iter(model, &iter, path))
1089 {
1090 if (gtk_tree_model_iter_has_child(model, &iter))
1091 {
1092 /* There are children,
1093 * just expand or collapse the row. */
1094 if (gtk_tree_view_row_expanded(view, path))
1095 gtk_tree_view_collapse_row(view, path);
1096 else
1097 gtk_tree_view_expand_row(view, path, FALSE);
1098 }
1099 else if (gnc_payment_window_check_payment (pw))
1100 /* It's an account without any children
1101 * If all conditions for a valid payment are met click the Ok button. */
1102 gnc_payment_ok_cb(widget, pw);
1103 }
1104}
1105
1106void
1107gnc_payment_leave_amount_cb (G_GNUC_UNUSED GtkWidget *widget,
1108 G_GNUC_UNUSED GdkEventFocus *event,
1109 PaymentWindow *pw)
1110{
1111 gboolean d_payment_ok = FALSE;
1112 gboolean c_payment_ok = FALSE;
1113
1114 if (! pw->amount_credit_edit || ! pw->amount_debit_edit)
1115 return;
1116
1117 c_payment_ok = gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(pw->amount_credit_edit), NULL);
1118 d_payment_ok = gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(pw->amount_debit_edit), NULL);
1119
1120 if (c_payment_ok && d_payment_ok)
1121 {
1122 gnc_numeric amount_deb, amount_cred, amount_tot;
1123
1124 /* If both credit and debit amount are entered, simplify it to either one */
1125 amount_deb = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit));
1126 amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit));
1127 amount_tot = gnc_numeric_sub (amount_cred, amount_deb,
1129 xaccAccountGetCommodity (pw->post_acct)),
1130 GNC_HOW_RND_ROUND_HALF_UP);
1131
1132 gnc_ui_payment_window_set_amount (pw, amount_tot);
1133 }
1134 /* Reflect if the payment could complete now */
1135 gnc_payment_window_check_payment (pw);
1136}
1137
1138void
1139gnc_payment_activate_amount_cb (G_GNUC_UNUSED GtkWidget *widget,
1140 PaymentWindow *pw)
1141{
1142 gnc_payment_leave_amount_cb (NULL, NULL, pw);
1143}
1144
1145/* Select the list of accounts to show in the tree */
1146static void
1147gnc_payment_set_account_types (GncTreeViewAccount *tree)
1148{
1149 AccountViewInfo avi;
1150 int i;
1151
1153
1154 for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
1155 avi.include_type[i] = !xaccAccountIsAPARType (i);
1156
1158}
1159
1160static gboolean
1161find_handler (G_GNUC_UNUSED gpointer find_data, gpointer user_data)
1162{
1163 PaymentWindow *pw = user_data;
1164
1165 return (pw != NULL);
1166}
1167
1168static void print_date (G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
1169 GtkCellRenderer *cell,
1170 GtkTreeModel *tree_model,
1171 GtkTreeIter *iter,
1172 G_GNUC_UNUSED gpointer data)
1173{
1174 time64 doc_date_time;
1175 gchar *doc_date_str;
1176
1177 g_return_if_fail (cell && iter && tree_model);
1178
1179
1180 gtk_tree_model_get (tree_model, iter, 0, &doc_date_time, -1);
1181 doc_date_str = qof_print_date (doc_date_time);
1182 g_object_set (G_OBJECT (cell), "text", doc_date_str, NULL);
1183 g_free (doc_date_str);
1184}
1185
1186static gint
1187doc_sort_func (GtkTreeModel *model,
1188 GtkTreeIter *a,
1189 GtkTreeIter *b,
1190 gpointer user_data)
1191{
1192 time64 a_date, b_date;
1193 gchar *a_id = NULL, *b_id = NULL;
1194 int ret;
1195
1196 gtk_tree_model_get (model, a, 0, &a_date, 1, &a_id, -1);
1197 gtk_tree_model_get (model, b, 0, &b_date, 1, &b_id, -1);
1198
1199 if (a_date < b_date) ret = -1;
1200 else if (a_date > b_date) ret = 1;
1201 else ret = g_strcmp0 (a_id, b_id);
1202
1203 g_free (a_id);
1204 g_free (b_id);
1205 return ret;
1206}
1207
1208static gboolean
1209payment_dialog_delete_event_cb (GtkWidget *widget,
1210 GdkEvent *event,
1211 gpointer user_data)
1212{
1213 PaymentWindow *pw = user_data;
1214
1215 // this cb allows the window size to be saved on closing with the X
1216 gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pw->dialog));
1217
1218 return FALSE;
1219}
1220
1221static PaymentWindow *
1222new_payment_window (GtkWindow *parent, QofBook *book, InitialPaymentInfo *tx_info)
1223{
1224 PaymentWindow *pw;
1225 GtkBuilder *builder;
1226 GtkWidget *box;
1227 GtkTreeSelection *selection;
1228 GtkTreeViewColumn *column;
1229 GtkCellRenderer *renderer;
1230 GtkTreeModel *store;
1231 GtkTreeIter iter;
1232
1233 /* Ensure we always have a properly initialized PreExistTxnInfo struct to work with */
1234 if (!tx_info)
1235 {
1236 tx_info = g_new0 (InitialPaymentInfo, 1);
1237 gncOwnerInitCustomer (&tx_info->owner, NULL);
1238 }
1239
1240 /*
1241 * Find an existing payment window. If found, bring it to
1242 * the front. If we have an actual owner, then set it in
1243 * the window. And update the PreExistTxnInfo (tx_info) for this window.
1244 */
1245
1246 pw = gnc_find_first_gui_component (DIALOG_PAYMENT_CM_CLASS, find_handler, NULL);
1247 if (pw)
1248 {
1249
1250 // Reset the current
1251 if (pw->tx_info->lots)
1252 g_list_free_full (pw->tx_info->lots, g_free);
1253 g_free (pw->tx_info);
1254 pw->tx_info = tx_info;
1255
1256 gncOwnerCopy (&pw->tx_info->owner, &(pw->owner));
1257 gnc_payment_set_owner_type (pw, gncOwnerGetType(&pw->tx_info->owner));
1258
1259 gtk_window_set_transient_for (GTK_WINDOW(pw->dialog), parent);
1260 gtk_window_present (GTK_WINDOW(pw->dialog));
1261 return(pw);
1262 }
1263
1264 /* Ok, we need a new window */
1265
1266 pw = g_new0 (PaymentWindow, 1);
1267 pw->book = book;
1268 pw->tx_info = tx_info;
1269
1270 /* Open and read the Glade File */
1271 builder = gtk_builder_new();
1272 gnc_builder_add_from_file (builder, "dialog-payment.glade", "docs_list_hor_adj");
1273 gnc_builder_add_from_file (builder, "dialog-payment.glade", "docs_list_vert_adj");
1274 gnc_builder_add_from_file (builder, "dialog-payment.glade", "docs_list_model");
1275 gnc_builder_add_from_file (builder, "dialog-payment.glade", "post_combo_model");
1276 gnc_builder_add_from_file (builder, "dialog-payment.glade", "owner_type_combo_model");
1277 gnc_builder_add_from_file (builder, "dialog-payment.glade", "payment_dialog");
1278 pw->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "payment_dialog"));
1279 gtk_window_set_transient_for (GTK_WINDOW(pw->dialog), parent);
1280
1281 // Set the name for this dialog so it can be easily manipulated with css
1282 gtk_widget_set_name (GTK_WIDGET(pw->dialog), "gnc-id-payment");
1283
1284 /* Grab the widgets and build the dialog */
1285 pw->payment_warning = GTK_WIDGET (gtk_builder_get_object (builder, "payment_warning"));
1286 pw->conflict_message = GTK_WIDGET (gtk_builder_get_object (builder, "conflict_message"));
1287 pw->ok_button = GTK_WIDGET (gtk_builder_get_object (builder, "okbutton"));
1288 pw->num_entry = GTK_WIDGET (gtk_builder_get_object (builder, "num_entry"));
1289 pw->memo_entry = GTK_WIDGET (gtk_builder_get_object (builder, "memo_entry"));
1290 pw->commodity_label = GTK_WIDGET (gtk_builder_get_object (builder, "commodity_label"));
1291 pw->post_combo = GTK_WIDGET (gtk_builder_get_object (builder, "post_combo"));
1292 gtk_combo_box_set_entry_text_column( GTK_COMBO_BOX( pw->post_combo ), 0 );
1293 gnc_cbwe_require_list_item(GTK_COMBO_BOX(pw->post_combo));
1294
1295 pw->owner_type_combo = GTK_WIDGET (gtk_builder_get_object (builder, "owner_type_combo"));
1296 /* Add the respective GNC_OWNER_TYPEs to the combo box model
1297 * ATTENTION: the order here should match the order of the
1298 * store's entries as set in the glade file !
1299 */
1300 store = gtk_combo_box_get_model (GTK_COMBO_BOX(pw->owner_type_combo));
1301 gtk_tree_model_get_iter_first (store, &iter);
1302 gtk_list_store_set (GTK_LIST_STORE(store), &iter,
1303 COL_OWNER_TYPE_NAME, _("Customer"),
1304 COL_OWNER_TYPE_NUM, GNC_OWNER_CUSTOMER, -1);
1305 gtk_tree_model_iter_next (store, &iter);
1306 gtk_list_store_set (GTK_LIST_STORE(store), &iter,
1307 COL_OWNER_TYPE_NAME, _("Vendor"),
1308 COL_OWNER_TYPE_NUM, GNC_OWNER_VENDOR, -1);
1309 gtk_tree_model_iter_next (store, &iter);
1310 gtk_list_store_set (GTK_LIST_STORE(store), &iter,
1311 COL_OWNER_TYPE_NAME, _("Employee"),
1312 COL_OWNER_TYPE_NUM, GNC_OWNER_EMPLOYEE, -1);
1313
1314 pw->owner_box = GTK_WIDGET (gtk_builder_get_object (builder, "owner_box"));
1315
1316 pw->amount_refund_box = GTK_WIDGET (gtk_builder_get_object (builder, "amount_refund_box"));
1317 pw->amount_payment_box = GTK_WIDGET (gtk_builder_get_object (builder, "amount_payment_box"));
1318
1319 pw->amount_debit_edit = gnc_amount_edit_new ();
1320 gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (pw->amount_debit_edit),
1321 TRUE);
1322 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit), gnc_numeric_zero());
1323 g_signal_connect(G_OBJECT(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(pw->amount_debit_edit))),
1324 "focus-out-event",
1325 G_CALLBACK(gnc_payment_leave_amount_cb), pw);
1326
1327 g_signal_connect(G_OBJECT(pw->amount_debit_edit),
1328 "activate",
1329 G_CALLBACK(gnc_payment_activate_amount_cb), pw);
1330
1331 pw->amount_credit_edit = gnc_amount_edit_new ();
1332 gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (pw->amount_credit_edit),
1333 TRUE);
1334 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit), gnc_numeric_zero());
1335 g_signal_connect(G_OBJECT(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(pw->amount_credit_edit))),
1336 "focus-out-event",
1337 G_CALLBACK(gnc_payment_leave_amount_cb), pw);
1338
1339 g_signal_connect(G_OBJECT(pw->amount_credit_edit),
1340 "activate",
1341 G_CALLBACK(gnc_payment_activate_amount_cb), pw);
1342
1343 box = GTK_WIDGET (gtk_builder_get_object (builder, "date_box"));
1344 pw->date_edit = gnc_date_edit_new (time(NULL), FALSE, FALSE);
1345 gtk_box_pack_start (GTK_BOX (box), pw->date_edit, TRUE, TRUE, 0);
1346 pw->print_check = GTK_WIDGET (gtk_builder_get_object (builder, "print_check"));
1347
1348 pw->docs_list_tree_view = GTK_WIDGET (gtk_builder_get_object (builder, "docs_list_tree_view"));
1349 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
1350 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1351
1352 // Set grid lines option to preference
1353 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(pw->docs_list_tree_view), gnc_tree_view_get_grid_lines_pref ());
1354
1355 /* Configure date column */
1356 renderer = gtk_cell_renderer_text_new ();
1357 column = gtk_tree_view_get_column (GTK_TREE_VIEW (pw->docs_list_tree_view), 0);
1358 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1359 tree_view_column_set_default_width (GTK_TREE_VIEW (pw->docs_list_tree_view),
1360 column, "31-12-2013");
1361 gtk_tree_view_column_set_cell_data_func (column, renderer,
1362 (GtkTreeCellDataFunc) print_date,
1363 NULL, NULL);
1364
1365 /* Configure document number column */
1366 column = gtk_tree_view_get_column (GTK_TREE_VIEW (pw->docs_list_tree_view), 1);
1367 tree_view_column_set_default_width (GTK_TREE_VIEW (pw->docs_list_tree_view),
1368 column, _("Pre-Payment"));
1369
1370 /* Configure document type column */
1371 column = gtk_tree_view_get_column (GTK_TREE_VIEW (pw->docs_list_tree_view), 2);
1372 tree_view_column_set_default_width (GTK_TREE_VIEW (pw->docs_list_tree_view),
1373 column, _("Credit Note"));
1374
1375 /* Configure debit column */
1376 column = gtk_tree_view_get_column (GTK_TREE_VIEW (pw->docs_list_tree_view), 3);
1377 tree_view_column_set_default_width (GTK_TREE_VIEW (pw->docs_list_tree_view),
1378 column, "9,999,999.00");
1379
1380 /* Configure credit column */
1381 column = gtk_tree_view_get_column (GTK_TREE_VIEW (pw->docs_list_tree_view), 4);
1382 tree_view_column_set_default_width (GTK_TREE_VIEW (pw->docs_list_tree_view),
1383 column, "9,999,999.00");
1384
1385 gtk_tree_sortable_set_default_sort_func
1386 (GTK_TREE_SORTABLE (gtk_tree_view_get_model
1387 (GTK_TREE_VIEW (pw->docs_list_tree_view))),
1388 doc_sort_func, NULL, NULL);
1389
1390 gtk_tree_sortable_set_sort_column_id
1391 (GTK_TREE_SORTABLE (gtk_tree_view_get_model
1392 (GTK_TREE_VIEW (pw->docs_list_tree_view))),
1393 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1394 GTK_SORT_ASCENDING);
1395
1396 box = GTK_WIDGET (gtk_builder_get_object (builder, "acct_window"));
1397 pw->acct_tree = GTK_WIDGET(gnc_tree_view_account_new (FALSE));
1398 gtk_container_add (GTK_CONTAINER (box), pw->acct_tree);
1399 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(pw->acct_tree), FALSE);
1400 gnc_payment_set_account_types (GNC_TREE_VIEW_ACCOUNT (pw->acct_tree));
1401
1402 /* Set the dialog for the 'new' owner and owner type.
1403 * Note that this also sets the post account tree. */
1404 gncOwnerCopy (&pw->tx_info->owner, &(pw->owner));
1405 gnc_payment_set_owner_type (pw, gncOwnerGetType (&pw->tx_info->owner));
1406
1407 /* Setup signals */
1408 gtk_builder_connect_signals_full( builder,
1409 gnc_builder_connect_full_func,
1410 pw);
1411
1412 g_signal_connect (G_OBJECT (pw->acct_tree), "row-activated",
1413 G_CALLBACK (gnc_payment_acct_tree_row_activated_cb), pw);
1414
1415 g_signal_connect (G_OBJECT (pw->owner_type_combo), "changed",
1416 G_CALLBACK (gnc_payment_dialog_owner_type_changed_cb), pw);
1417
1418 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->acct_tree));
1419 g_signal_connect (G_OBJECT (selection), "changed",
1420 G_CALLBACK (gnc_payment_dialog_xfer_acct_changed_cb), pw);
1421
1422 g_signal_connect (G_OBJECT(pw->dialog), "delete-event",
1423 G_CALLBACK(payment_dialog_delete_event_cb), pw);
1424
1425 /* Register with the component manager */
1426 pw->component_id =
1427 gnc_register_gui_component (DIALOG_PAYMENT_CM_CLASS,
1428 gnc_payment_window_refresh_handler,
1429 gnc_payment_window_close_handler,
1430 pw);
1431
1432 /* Watch for any new or changed accounts */
1433 gnc_gui_component_watch_entity_type (pw->component_id,
1434 GNC_ID_ACCOUNT,
1435 QOF_EVENT_CREATE | QOF_EVENT_MODIFY |
1436 QOF_EVENT_DESTROY);
1437
1438 gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pw->dialog), GTK_WINDOW(parent));
1439
1440 /* Show it all */
1441 gtk_widget_show_all (pw->dialog);
1442 g_object_unref(G_OBJECT(builder));
1443
1444 // The customer choice widget should have keyboard focus
1445 if (GNC_IS_GENERAL_SEARCH(pw->owner_choice))
1446 {
1447 gnc_general_search_grab_focus(GNC_GENERAL_SEARCH(pw->owner_choice));
1448 }
1449
1450 /* Reflect if the payment could complete now */
1451 gnc_payment_window_check_payment (pw);
1452
1453 /* Warn the user if they have no valid post-to accounts */
1454 {
1455 const gchar *text;
1456 const char *acct_type;
1457
1458 text = gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (GTK_COMBO_BOX(pw->post_combo)))));
1459
1460 if (!text || g_strcmp0 (text, "") == 0)
1461 {
1462
1463 /* The code below assumes there will only be one account type.
1464 * Let's assert this to protect from potential future changes. */
1465 g_assert (g_list_length (pw->acct_types) == 1);
1466 acct_type = xaccAccountGetTypeStr(GPOINTER_TO_INT(pw->acct_types->data));
1467 gnc_warning_dialog(GTK_WINDOW (pw->dialog),
1468 _("You have no valid \"Post To\" accounts. "
1469 "Please create an account of type \"%s\" "
1470 "before you continue to process this payment. "
1471 "Perhaps you want to create an Invoice or "
1472 "Bill first?"),
1473 acct_type);
1474 }
1475 }
1476
1477 return pw;
1478}
1479
1480
1481void
1482gnc_ui_payment_window_destroy (PaymentWindow *pw)
1483{
1484 if (!pw) return;
1485
1486 gnc_close_gui_component (pw->component_id);
1487}
1488
1489PaymentWindow *
1490gnc_ui_payment_new_with_invoice (GtkWindow *parent, const GncOwner *owner,
1491 QofBook *book, GncInvoice *invoice)
1492{
1493 GNCLot *postlot;
1494 InitialPaymentInfo *tx_info;
1495
1496 if (!book) return NULL;
1497
1498
1499 tx_info = g_new0 (InitialPaymentInfo, 1);
1500
1501 if (owner)
1502 {
1503 /* Figure out the company */
1504 gncOwnerCopy (gncOwnerGetEndOwner (owner), &tx_info->owner);
1505 }
1506 else
1507 {
1508 gncOwnerInitCustomer (&tx_info->owner, NULL);
1509 }
1510
1511 tx_info->post_acct = gncInvoiceGetPostedAcc (invoice);
1512
1513 postlot = gncInvoiceGetPostedLot (invoice);
1514 if (postlot)
1515 {
1516 PreExistLotInfo *lot_info = g_new0 (PreExistLotInfo, 1);
1517 lot_info->lot = postlot;
1518 lot_info->amount = gnc_numeric_zero ();
1519 tx_info->lots = g_list_prepend (tx_info->lots, lot_info);
1520 }
1521 return new_payment_window (parent, book, tx_info);
1522}
1523
1524PaymentWindow *
1525gnc_ui_payment_new (GtkWindow *parent, GncOwner *owner, QofBook *book)
1526{
1527 return gnc_ui_payment_new_with_invoice (parent, owner, book, NULL);
1528}
1529
1530// ///////////////
1531
1532gboolean gnc_ui_payment_is_customer_payment(const Transaction *txn)
1533{
1534 gboolean result = TRUE;
1535 Split *assetaccount_split, *aparaccount_split;
1536 gnc_numeric amount;
1537
1538 if (!txn)
1539 return result;
1540
1541 if (!xaccTransGetSplitList(txn))
1542 return result;
1543
1544 /* First test if one split is in an A/R or A/P account.
1545 * That will give us the best Customer vs Vendor/Employee distinction */
1546 // Prefer true business split (one that's linked to a lot)
1547 aparaccount_split = xaccTransGetFirstAPARAcctSplit(txn, TRUE);
1548 if (!aparaccount_split)
1549 // No true business split found, try again but this time more relaxed
1550 aparaccount_split = xaccTransGetFirstAPARAcctSplit(txn, FALSE);
1551 if (aparaccount_split)
1552 {
1553 if (xaccAccountGetType (xaccSplitGetAccount (aparaccount_split)) == ACCT_TYPE_RECEIVABLE)
1554 return TRUE; // Type is Customer
1555 else if (xaccAccountGetType (xaccSplitGetAccount (aparaccount_split)) == ACCT_TYPE_PAYABLE)
1556 return FALSE; // Type is Vendor/Employee, there's not enough information to refine more
1557 }
1558
1559 /* For the lack of an A/R or A/P account we'll assume positive changes to an
1560 * Asset/Liability or Equity account are Customer payments the others will be
1561 * considered Vendor payments */
1562 assetaccount_split = xaccTransGetFirstPaymentAcctSplit(txn);
1563 if (!assetaccount_split)
1564 {
1565 /* Transaction isn't valid for a payment, just return the default
1566 * Calling code will have to handle this situation properly */
1567 PINFO("No asset splits in txn \"%s\"; cannot use this for assigning a payment.",
1569 return result;
1570 }
1571
1572 assetaccount_split = xaccTransGetFirstPaymentAcctSplit(txn);
1573 amount = xaccSplitGetValue(assetaccount_split);
1574 result = gnc_numeric_positive_p(amount); // positive amounts == customer
1575 //PINFO("Amount=%s", gnc_numeric_to_string(amount));
1576 return result;
1577}
1578
1579// ///////////////
1580static char *gen_split_desc (Transaction *txn, Split *split)
1581{
1582 gnc_numeric value = xaccSplitGetAmount(split);
1583 Account *xfer_acct = xaccSplitGetAccount(split);
1584 char *acct_name = gnc_account_get_full_name (xfer_acct);
1585 const char *action = gnc_get_action_num (txn, split);
1586 const char *memo = xaccSplitGetMemo (split);
1587 char rec_state = xaccSplitGetReconcile (split);
1588 const char *print_amt = xaccPrintAmount(value, gnc_account_print_info (xfer_acct, TRUE));
1589 char *split_str = NULL;
1590 char *rec_str = NULL;
1591
1592 if (rec_state == CREC)
1593 rec_str = g_strdup_printf("[%s] ", _("Cleared"));
1594 else if (rec_state == YREC)
1595 rec_str = g_strdup_printf("[%s] ", _("Reconciled"));
1596 else
1597 rec_str = g_strdup("");
1598
1599 if (action && *action && memo && *memo)
1600 split_str = g_strdup_printf ("%s%s: %s (%s, %s)", rec_str, acct_name, print_amt,
1601 action, memo);
1602 else if((action && *action) || (memo && *memo))
1603 split_str = g_strdup_printf ("%s%s: %s (%s)", rec_str, acct_name, print_amt,
1604 action ? action : memo);
1605 else
1606 split_str = g_strdup_printf ("%s%s: %s", rec_str, acct_name, print_amt);
1607
1608 g_free (acct_name);
1609 g_free (rec_str);
1610
1611 return split_str;
1612}
1613
1614static Split *select_payment_split (GtkWindow *parent, Transaction *txn)
1615{
1616 /* We require the txn to have one split in an Asset account.
1617 * The only exception would be a lot link transaction
1618 */
1619 GList *payment_splits = xaccTransGetPaymentAcctSplitList (txn);
1620 Split *selected_split = NULL;
1621 if (!payment_splits)
1622 {
1623 GtkWidget *dialog;
1624
1626 return NULL;
1627
1628 dialog = gtk_message_dialog_new (parent,
1629 GTK_DIALOG_DESTROY_WITH_PARENT,
1630 GTK_MESSAGE_INFO,
1631 GTK_BUTTONS_CLOSE,
1632 "%s",
1633 _("The selected transaction doesn't have splits that can be assigned as a payment"));
1634 gtk_dialog_run (GTK_DIALOG(dialog));
1635 gtk_widget_destroy (dialog);
1636 PINFO("No asset splits in txn \"%s\"; cannot use this for assigning a payment.",
1638 return NULL;
1639 }
1640
1641 if (g_list_length(payment_splits) > 1)
1642 {
1643 GtkWidget *first_rb = NULL;
1644 int answer = GTK_BUTTONS_OK;
1645 const char *message = _("While this transaction has multiple splits that can be considered\n"
1646 "as 'the payment split', GnuCash only knows how to handle one.\n"
1647 "Please select one, the others will be discarded.\n\n");
1648 GtkDialog *dialog = GTK_DIALOG(
1649 gtk_dialog_new_with_buttons (_("Warning"),
1650 parent,
1651 GTK_DIALOG_DESTROY_WITH_PARENT,
1652 _("Continue"), GTK_BUTTONS_OK,
1653 _("Cancel"), GTK_BUTTONS_CANCEL,
1654 NULL));
1655 GtkWidget *content = gtk_dialog_get_content_area(dialog);
1656 GtkWidget *label = gtk_label_new (message);
1657 gtk_box_pack_start (GTK_BOX(content), label, FALSE, TRUE, 0);
1658
1659 /* Add splits as selectable options to the dialog */
1660 for (GList *node = payment_splits; node; node = node->next)
1661 {
1662 GtkWidget *rbutton;
1663 Split *split = node->data;
1664 char *split_str = gen_split_desc (txn, split);
1665
1666 if (node == payment_splits)
1667 {
1668 first_rb = gtk_radio_button_new_with_label (NULL, split_str);
1669 rbutton = first_rb;
1670 }
1671 else
1672 rbutton = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_rb), split_str);
1673
1674 g_object_set_data(G_OBJECT(rbutton), "split", split);
1675 gtk_box_pack_start (GTK_BOX(content), rbutton, FALSE, FALSE, 0);
1676
1677 g_free (split_str);
1678 }
1679
1680 gtk_dialog_set_default_response (dialog, GTK_BUTTONS_CANCEL);
1681 gtk_widget_show_all (GTK_WIDGET(dialog));
1682 answer = gtk_dialog_run (dialog);
1683
1684 if (answer == GTK_BUTTONS_OK)
1685 {
1686 GSList *rbgroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(first_rb));
1687 GSList *rbnode;
1688 for (rbnode = rbgroup; rbnode; rbnode = rbnode->next)
1689 {
1690 GtkWidget *rbutton = rbnode->data;
1691 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rbutton)))
1692 {
1693 selected_split = g_object_get_data(G_OBJECT(rbutton), "split");
1694 break;
1695 }
1696 }
1697 }
1698
1699 gtk_widget_destroy (GTK_WIDGET(dialog));
1700 }
1701 else
1702 selected_split = payment_splits->data;
1703
1704 g_list_free (payment_splits);
1705 return selected_split;
1706}
1707
1708static GList *select_txn_lots (GtkWindow *parent, Transaction *txn, Account **post_acct, gboolean *abort)
1709{
1710 SplitList *apar_splits = NULL; /* all spits in txn that are APAR type */
1711 SplitList *apar_splits_no_lot = NULL; /* all splits in txn that are APAR type, but not tied to a lot */
1712 SplitList *iter;
1713 GList *txn_lots = NULL;
1714 GList *unique_apar_accts = NULL;
1715
1716 /* There's no use in continuing if I can't set the post_acct or abort variables */
1717 if (!post_acct || !abort)
1718 return NULL;
1719
1720 *abort = FALSE;
1721 *post_acct = NULL;
1722
1723 /* Start by filtering out all APAR splits that have lots. Those are the ones we can
1724 display as invoices and pre-payments in the payment window. */
1725 apar_splits = xaccTransGetAPARAcctSplitList (txn, FALSE);
1726 for (iter = apar_splits; iter; iter = iter->next)
1727 {
1728 GNCLot *postlot = NULL;
1729 Split *post_split = iter->data;
1730 Account *apar_acct = xaccSplitGetAccount (post_split);
1731
1732 /* Store found apar_acct in our list of unique_apar_accts
1733 * for later processing */
1734 if (!g_list_find (unique_apar_accts, apar_acct))
1735 unique_apar_accts = g_list_prepend (unique_apar_accts, apar_acct);
1736
1737 postlot = xaccSplitGetLot (post_split);
1738 if (postlot)
1739 {
1740 PreExistLotInfo *lot_info = g_new0 (PreExistLotInfo, 1);
1741 lot_info->lot = postlot;
1742 lot_info->amount = xaccSplitGetValue (post_split);
1743 txn_lots = g_list_prepend (txn_lots, lot_info);
1744 *post_acct = apar_acct;
1745 }
1746 else
1747 apar_splits_no_lot = g_list_prepend (apar_splits_no_lot, post_split);
1748 }
1749
1750 /* If no post_acct was selected from the postlots, fall back to the first apar split's
1751 * account if there is one. */
1752 if (!*post_acct && apar_splits_no_lot)
1753 *post_acct = xaccSplitGetAccount (apar_splits_no_lot->data);
1754
1755 /* Abort if the txn has splits in more than one APAR account
1756 * GnuCash can only handle one post account per payment transaction.
1757 */
1758 if (g_list_length (unique_apar_accts) > 1)
1759 {
1760 GtkWidget *dialog;
1761 char *split_str = g_strdup ("");
1762
1763 for (iter = unique_apar_accts; iter; iter = iter->next)
1764 {
1765 Account *acct = iter->data;
1766 char *acct_name = gnc_account_get_full_name (acct);
1767 char *tmp_str = g_strconcat(split_str, "• ", acct_name, "\n", NULL);
1768 g_free (acct_name);
1769 g_free (split_str);
1770 split_str = tmp_str;
1771 }
1772
1773 dialog = gtk_message_dialog_new (parent,
1774 GTK_DIALOG_DESTROY_WITH_PARENT,
1775 GTK_MESSAGE_INFO,
1776 GTK_BUTTONS_CLOSE,
1777 _("This transaction has splits in multiple business accounts:\n\n%s\n"
1778 "GnuCash can only handle transactions that post to a single account.\n\n"
1779 "Please correct this manually by editing the transaction directly and then try again."),
1780 split_str);
1781 gtk_dialog_run (GTK_DIALOG(dialog));
1782 gtk_widget_destroy (dialog);
1783 PINFO("Multiple asset accounts in splits of txn \"%s\"; cannot use this for assigning a payment.",
1785 g_free (split_str);
1786
1787 *abort = TRUE;
1788 g_list_free_full (txn_lots, g_free);
1789 txn_lots = NULL;
1790 }
1791
1792 g_list_free (apar_splits);
1793 g_list_free (apar_splits_no_lot);
1794 g_list_free (unique_apar_accts);
1795 return txn_lots;
1796}
1797
1798PaymentWindow * gnc_ui_payment_new_with_txn (GtkWindow* parent, GncOwner *owner, Transaction *txn)
1799{
1800 Split *payment_split = NULL;
1801 Account *post_acct = NULL;
1802 InitialPaymentInfo *tx_info = NULL;
1803 GList *txn_lots = NULL;
1804 gboolean abort = FALSE;
1805 PaymentWindow *pw;
1806
1807 if (!txn)
1808 return NULL;
1809
1810 if (!xaccTransGetSplitList(txn))
1811 return NULL;
1812
1813 /* We require the txn to have one split in an Asset account.
1814 * The only exception would be a lot link transaction
1815 */
1816 payment_split = select_payment_split (parent, txn);
1817 if (!payment_split && (xaccTransGetTxnType(txn) != TXN_TYPE_LINK))
1818 return NULL;
1819
1820 /* Get all APAR related lots. Watch out: there might be none */
1821 txn_lots = select_txn_lots (parent, txn, &post_acct, &abort);
1822 if (abort)
1823 return NULL;
1824
1825 // Fill in the values from the given txn
1826 tx_info = g_new0(InitialPaymentInfo, 1);
1827 tx_info->txn = txn;
1828 tx_info->post_acct = post_acct;
1829 tx_info->lots = txn_lots;
1830 gncOwnerCopy (owner, &tx_info->owner);
1831
1832 pw = new_payment_window (parent,
1833 qof_instance_get_book(QOF_INSTANCE(txn)),
1834 tx_info);
1835
1836 gnc_ui_payment_window_set_num(pw, gnc_get_num_action (txn, payment_split));
1837 gnc_ui_payment_window_set_memo(pw, xaccTransGetDescription(txn));
1838 {
1839 GDate txn_date = xaccTransGetDatePostedGDate (txn);
1840 gnc_ui_payment_window_set_date(pw, &txn_date);
1841 }
1842
1843 gnc_numeric amount = xaccSplitGetAmount (payment_split);
1844 /* Note: at this point post account selected in newly created payment dialog
1845 * may differ from what we got from select_txn_lots above.
1846 * Use the dialog's post account commodity to optionally convert the amount
1847 * to to display to the user */
1848 if (pw->post_acct)
1849 amount = xaccSplitConvertAmount (payment_split, pw->post_acct);
1850 gnc_ui_payment_window_set_amount(pw, amount);
1851 if (payment_split)
1852 gnc_ui_payment_window_set_xferaccount(pw, xaccSplitGetAccount(payment_split));
1853 return pw;
1854}
Account handling public routines.
API for Transactions and Splits (journal entries)
Date and Time handling routines.
GLib helper routines.
gtk helper routines.
Generic api to store and retrieve preferences.
GtkTreeView implementation for gnucash account tree.
utility functions for the GnuCash UI
Business Invoice Interface.
Business Interface: Object OWNERs.
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
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
#define xaccAccountGetGUID(X)
Definition Account.h:252
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
Definition Account.cpp:2050
@ NUM_ACCOUNT_TYPES
stop here; the following types just aren't ready for prime time
Definition Account.h:161
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
const char * gnc_commodity_get_nice_symbol(const gnc_commodity *cm)
Retrieve a symbol for the specified commodity, suitable for display to the user.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
char * qof_print_date(time64 t)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:610
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
SplitList * xaccTransGetPaymentAcctSplitList(const Transaction *trans)
The xaccTransGetPaymentAcctSplitList() method returns a GList of the splits in a transaction that bel...
char xaccTransGetTxnType(Transaction *trans)
Returns the Transaction Type: note this type will be derived from the transaction splits,...
GDate xaccTransGetDatePostedGDate(const Transaction *trans)
Retrieve the posted date of the transaction.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Split * xaccTransGetFirstPaymentAcctSplit(const Transaction *trans)
The xaccTransGetFirstPaymentAcctSplit() method returns a pointer to the first split in this transacti...
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition Account.cpp:4527
LotList * xaccAccountFindOpenLots(const Account *acc, gboolean(*match_func)(GNCLot *lot, gpointer user_data), gpointer user_data, GCompareFunc sort_func)
Find a list of open lots that match the match_func.
Definition Account.cpp:3982
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
Split * xaccTransGetFirstAPARAcctSplit(const Transaction *trans, gboolean strict)
The xaccTransGetFirstPaymentAcctSplit() method returns a pointer to the first split in this transacti...
GList SplitList
GList of Split.
Definition gnc-engine.h:207
SplitList * xaccTransGetAPARAcctSplitList(const Transaction *trans, gboolean strict)
The xaccTransGetAPARSplitList() method returns a GList of the splits in a transaction that belong to ...
#define TXN_TYPE_LINK
Transaction is a link between (invoice and payment) lots
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition Account.cpp:4357
void tree_view_column_set_default_width(GtkTreeView *view, GtkTreeViewColumn *column, const gchar *sizing_text)
Set default width for a treeview column.
void gnc_cbwe_set_by_string(GtkComboBox *cbwe, const gchar *text)
Find an entry in the GtkComboBox by its text value, and set the widget to that value.
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
void gnc_tree_view_account_get_view_info(GncTreeViewAccount *view, AccountViewInfo *avi)
Given pointers to an account tree and old style filter block, this function will copy the current con...
void gnc_tree_view_account_set_view_info(GncTreeViewAccount *view, AccountViewInfo *avi)
Given pointers to an account tree and old style filter block, this function will applies the settings...
Account * gnc_tree_view_account_get_selected_account(GncTreeViewAccount *view)
This function returns the account associated with the selected item in the account tree view.
void gnc_tree_view_account_set_selected_account(GncTreeViewAccount *view, Account *account)
This function selects an account in the account tree view.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
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.
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
Split * gnc_lot_get_latest_split(GNCLot *lot)
Convenience routineto identify the date this lot was closed.
Definition gnc-lot.cpp:670
Account * gnc_lot_get_account(const GNCLot *lot)
Returns the account with which this lot is associated.
Definition gnc-lot.cpp:377
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Returns the lot balance.
Definition gnc-lot.cpp:487
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
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.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gncOwnerIsValid(const GncOwner *owner)
Returns TRUE if the given owner is one of the valid objects.
Definition gncOwner.c:699
gboolean gncOwnerGetOwnerFromLot(GNCLot *lot, GncOwner *owner)
Get the owner from the lot.
Definition gncOwner.c:636
GList * gncOwnerGetCommoditiesList(const GncOwner *owner)
Returns a GList of currencies associated with the owner.
Definition gncOwner.c:1462
GList * gncOwnerGetAccountTypesList(const GncOwner *owner)
Returns a GList of account-types based on the owner type.
Definition gncOwner.c:1444
void gncOwnerApplyPaymentSecs(const GncOwner *owner, Transaction **preset_txn, GList *lots, Account *posted_acc, Account *xfer_acc, gnc_numeric amount, gnc_numeric exch, time64 date, const char *memo, const char *num, gboolean auto_pay)
A convenience function to apply a payment to the owner.
Definition gncOwner.c:1405
gint gncOwnerLotsSortFunc(GNCLot *lotA, GNCLot *lotB)
Helper function used to sort lots by date.
Definition gncOwner.c:728
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
gboolean gncOwnerLotMatchOwnerFunc(GNCLot *lot, gpointer user_data)
Helper function used to filter a list of lots by owner.
Definition gncOwner.c:706
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
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Definition Split.cpp:1920
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
#define CREC
The Split has been cleared
Definition Split.h:73
#define YREC
The Split has been reconciled.
Definition Split.h:74
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
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
QofBook reference.
Definition qofbook-p.hpp:47