From 0182bc6fd4208d733aeffa64262398dd17967e22 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sat, 4 Aug 2012 16:09:04 +0000 Subject: [PATCH] Adapt payment dialog for credit notes. To deal with all possible combinations, a user must set a debit or credit amount for the payment. In most cases the right amount is prefilled by selecting documents from the list. In the process, this changes gets rid of some confusing sign reversal logic in the payment code (gncOwner.c). WARNING FOR SCRIPTERS: if you have written python or scheme code that relies on gncOwnerApplyPayment, be careful: you now need to pass a signed amount to the function instead of an absolute value, because a payment could be for both an invoice/bill or a credit note. No more sign reversals happen internally based on the owner being a vendor or a customer. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@22286 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/business/business-gnome/dialog-payment.c | 95 +++++-- .../gtkbuilder/dialog-payment.glade | 247 ++++++++++++++---- src/engine/gncOwner.c | 28 +- 3 files changed, 274 insertions(+), 96 deletions(-) diff --git a/src/business/business-gnome/dialog-payment.c b/src/business/business-gnome/dialog-payment.c index 2afeeb1853..16a29bbf38 100644 --- a/src/business/business-gnome/dialog-payment.c +++ b/src/business/business-gnome/dialog-payment.c @@ -60,7 +60,8 @@ struct _payment_window GtkWidget * memo_entry; GtkWidget * post_combo; GtkWidget * owner_choice; - GtkWidget * amount_edit; + GtkWidget * amount_debit_edit; + GtkWidget * amount_credit_edit; GtkWidget * date_edit; GtkWidget * acct_tree; GtkWidget * docs_list_tree_view; @@ -95,7 +96,23 @@ void gnc_ui_payment_window_set_date (PaymentWindow *pw, const GDate *date) void gnc_ui_payment_window_set_amount (PaymentWindow *pw, gnc_numeric amount) { g_assert(pw); - gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_edit), amount); + + /* Debits are negative, credits are positive */ + if (gnc_numeric_positive_p (amount)) + { + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_credit_edit), + amount); + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_debit_edit), + gnc_numeric_zero ()); + } + else + { + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_debit_edit), + gnc_numeric_neg (amount)); + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_credit_edit), + gnc_numeric_zero ()); + } + } void gnc_ui_payment_window_set_postaccount (PaymentWindow *pw, const Account* account) { @@ -126,6 +143,8 @@ void gnc_payment_cancel_cb (GtkWidget *widget, gpointer data); void gnc_payment_window_destroy_cb (GtkWidget *widget, gpointer data); void gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *column, PaymentWindow *pw); +void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event, + PaymentWindow *pw); static void @@ -203,9 +222,7 @@ gnc_payment_dialog_document_selection_changed (PaymentWindow *pw) /* Set the payment amount in the dialog */ val = gnc_payment_dialog_calculate_selected_total (pw); - /* XXX It may not always be correct to use the absolute value of amount here - * This is an assumption from before the credit notes implementation. */ - gnc_ui_payment_window_set_amount(pw, gnc_numeric_abs (val)); + gnc_ui_payment_window_set_amount(pw, val); } static void @@ -521,20 +538,22 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data) PaymentWindow *pw = data; const char *text = NULL; Account *post, *acc; - gnc_numeric amount; + gnc_numeric amount_deb, amount_cred, amount_tot; if (!pw) return; - /* Verify the amount is non-zero */ - amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_edit)); + /* Verify the total amount is non-zero */ + amount_deb = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit)); + amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit)); + amount_tot = gnc_numeric_sub (amount_cred, amount_deb, + gnc_commodity_get_fraction (xaccAccountGetCommodity (pw->post_acct)), + GNC_HOW_RND_ROUND_HALF_UP); - /* XXX Amounts could possibly be negative as well if you take credit notes into account - * This still has to be reviewed */ - if (gnc_numeric_check (amount) || !gnc_numeric_positive_p (amount)) + if (gnc_numeric_check (amount_tot) || gnc_numeric_zero_p (amount_tot)) { text = _("You must enter the amount of the payment. " - "The payment amount must be greater than zero."); + "The payment amount must not be zero."); gnc_error_dialog (pw->dialog, "%s", text); return; } @@ -597,7 +616,7 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data) gnc_info_dialog(pw->dialog, "%s", text); gnc_xfer_dialog_select_to_account(xfer, post); - gnc_xfer_dialog_set_amount(xfer, amount); + gnc_xfer_dialog_set_amount(xfer, amount_tot); /* All we want is the exchange rate so prevent the user from thinking it makes sense to mess with other stuff */ @@ -611,7 +630,7 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data) /* Perform the payment */ gncOwnerApplyPayment (&pw->owner, pw->pre_existing_txn, selected_lots, - post, acc, amount, exch, date, memo, num); + post, acc, amount_tot, exch, date, memo, num); } gnc_resume_gui_refresh (); @@ -671,6 +690,25 @@ gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *path, } } +void +gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event, + PaymentWindow *pw) +{ + gnc_numeric amount_deb, amount_cred, amount_tot; + + if (! pw->amount_credit_edit || ! pw->amount_debit_edit) + return; + + /* If both credit and debit amount are entered, simplify it to either one */ + amount_deb = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit)); + amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit)); + amount_tot = gnc_numeric_sub (amount_cred, amount_deb, + gnc_commodity_get_fraction (xaccAccountGetCommodity (pw->post_acct)), + GNC_HOW_RND_ROUND_HALF_UP); + + gnc_ui_payment_window_set_amount (pw, amount_tot); +} + /* Select the list of accounts to show in the tree */ static void gnc_payment_set_account_types (GncTreeViewAccount *tree) @@ -768,12 +806,25 @@ new_payment_window (GncOwner *owner, QofBook *book, GncInvoice *invoice) box = GTK_WIDGET (gtk_builder_get_object (builder, "owner_box")); pw->owner_choice = gnc_owner_select_create (label, box, book, owner); - box = GTK_WIDGET (gtk_builder_get_object (builder, "amount_box")); - pw->amount_edit = gnc_amount_edit_new (); - gtk_box_pack_start (GTK_BOX (box), pw->amount_edit, TRUE, TRUE, 0); - gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (pw->amount_edit), + box = GTK_WIDGET (gtk_builder_get_object (builder, "amount_debit_box")); + pw->amount_debit_edit = gnc_amount_edit_new (); + gtk_box_pack_start (GTK_BOX (box), pw->amount_debit_edit, TRUE, TRUE, 0); + gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (pw->amount_debit_edit), TRUE); - gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (pw->amount_edit), gnc_numeric_zero()); + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit), gnc_numeric_zero()); + g_signal_connect(G_OBJECT(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(pw->amount_debit_edit))), + "focus-out-event", + G_CALLBACK(gnc_payment_leave_amount_cb), pw); + + box = GTK_WIDGET (gtk_builder_get_object (builder, "amount_credit_box")); + pw->amount_credit_edit = gnc_amount_edit_new (); + gtk_box_pack_start (GTK_BOX (box), pw->amount_credit_edit, TRUE, TRUE, 0); + gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (pw->amount_credit_edit), + TRUE); + gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit), gnc_numeric_zero()); + g_signal_connect(G_OBJECT(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(pw->amount_credit_edit))), + "focus-out-event", + G_CALLBACK(gnc_payment_leave_amount_cb), pw); box = GTK_WIDGET (gtk_builder_get_object (builder, "date_box")); pw->date_edit = gnc_date_edit_new (time(NULL), FALSE, FALSE); @@ -1045,7 +1096,7 @@ PaymentWindow * gnc_ui_payment_new_with_txn (GncOwner *owner, Transaction *txn) pw = gnc_ui_payment_new(owner, qof_instance_get_book(QOF_INSTANCE(txn))); g_assert(assetaccount_split); // we can rely on this because of the countAssetAccounts() check above - //g_message("Amount=%s", gnc_numeric_to_string(amount)); + g_debug("Amount=%s", gnc_numeric_to_string(amount)); // Fill in the values from the given txn pw->pre_existing_txn = txn; @@ -1055,9 +1106,7 @@ PaymentWindow * gnc_ui_payment_new_with_txn (GncOwner *owner, Transaction *txn) GDate txn_date = xaccTransGetDatePostedGDate (txn); gnc_ui_payment_window_set_date(pw, &txn_date); } - /* XXX It may not always be correct to use the absolute value of amount here - * This is an assumption from before the credit notes implementation. */ - gnc_ui_payment_window_set_amount(pw, gnc_numeric_abs(amount)); + gnc_ui_payment_window_set_amount(pw, amount); gnc_ui_payment_window_set_xferaccount(pw, xaccSplitGetAccount(assetaccount_split)); if (postaccount_split) gnc_ui_payment_window_set_postaccount(pw, xaccSplitGetAccount(postaccount_split)); diff --git a/src/business/business-gnome/gtkbuilder/dialog-payment.glade b/src/business/business-gnome/gtkbuilder/dialog-payment.glade index 41469afb20..3efbf65aaa 100644 --- a/src/business/business-gnome/gtkbuilder/dialog-payment.glade +++ b/src/business/business-gnome/gtkbuilder/dialog-payment.glade @@ -18,14 +18,16 @@ True False + 3 3 2 + 3 + 3 True False 0 - none True @@ -38,6 +40,7 @@ True The company associated with this payment. The company associated with this payment. + 3 @@ -68,7 +71,6 @@ True False 0 - none True @@ -78,6 +80,7 @@ True False + 3 post_combo_model True 0 @@ -114,7 +117,6 @@ True False 0 - none True @@ -124,6 +126,7 @@ True True + 3 docs_list_hor_adj docs_list_vert_adj in @@ -148,6 +151,7 @@ fixed 50 Date + True True 0 @@ -164,6 +168,7 @@ fixed 50 Number + True 1 @@ -179,6 +184,7 @@ fixed 50 Type + True 2 @@ -194,6 +200,7 @@ fixed 50 Debit + True 3 @@ -209,6 +216,7 @@ fixed 50 Credit + True 4 @@ -246,22 +254,24 @@ True False 0 - none True False 12 - + True False + 3 - + True False - 3 - True + 8 + 2 + 3 + 3 True @@ -271,9 +281,21 @@ right - False - False - 0 + GTK_FILL + GTK_FILL + + + + + True + False + + + + + + 1 + 2 @@ -292,13 +314,67 @@ If you have selected an invoice, GnuCash will propose the amount still due for i In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. 1 - Amount + <b>Amount</b> + True right - False - False - 1 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + False + True + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + 1 + Debit + right + + + 3 + 4 + GTK_FILL + GTK_FILL + + + + + True + False + True + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + 1 + Credit + right + + + 4 + 5 + GTK_FILL + GTK_FILL @@ -310,9 +386,10 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic right - False - False - 2 + 6 + 7 + GTK_FILL + GTK_FILL @@ -324,40 +401,40 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic right - False - False - 3 + 7 + 8 + GTK_FILL + GTK_FILL - - - False - True - 0 - - - - - True - False - 3 - True - + True False + True + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. - False - False - 0 + 1 + 2 + 3 + 4 - + True False True @@ -376,9 +453,10 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic - True - True - 1 + 1 + 2 + 4 + 5 @@ -389,9 +467,10 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic True - False - False - 2 + 1 + 2 + 6 + 7 @@ -402,16 +481,80 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic True - False - False - 3 + 1 + 2 + 7 + 8 + + + + + True + False + True + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + + + + + + 1 + 2 + + + 3 + + + + + True + False + True + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + The amount to pay for this invoice. + +If you have selected an invoice, GnuCash will propose the amount still due for it. You can change this amount to create a partial payment or an over-payment. + +In case of an over-payment or if no invoice was selected, GnuCash will automatically assign the remaining amount to the first unpaid invoice for this company. + + + + + + 5 + 6 + + + 3 + + + + + + + + + - True + False True - 1 + 0 @@ -440,7 +583,6 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic True False 0 - none True @@ -449,6 +591,7 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic 250 + 200 True True 3 @@ -493,7 +636,6 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic gtk-cancel - False True True True @@ -511,7 +653,6 @@ In case of an over-payment or if no invoice was selected, GnuCash will automatic gtk-ok - False True True True diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c index 11566f9ac4..70cf00fbaa 100644 --- a/src/engine/gncOwner.c +++ b/src/engine/gncOwner.c @@ -700,17 +700,6 @@ gncOwnerLotsSortFunc (GNCLot *lotA, GNCLot *lotB) return timespec_cmp (&da, &db); } -static gboolean use_reversed_payment_amounts(const GncOwner *owner) -{ - g_assert(owner); - return (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER); -} - - -/* - * Create a payment of "amount" for the owner and return - * the new lot associated with this payment. - */ GNCLot * gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, Account *posted_acc, Account *xfer_acc, @@ -721,8 +710,6 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, Split *split; const char *name; gnc_commodity *commodity; - gboolean reverse; - gnc_numeric payment_value = amount; Split *xfer_split = NULL; GNCLot *payment_lot; @@ -734,7 +721,7 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, book = gnc_account_get_book (posted_acc); name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner)); commodity = gncOwnerGetCurrency (owner); - reverse = use_reversed_payment_amounts(owner); +// reverse = use_reversed_payment_amounts(owner); if (txn) { @@ -811,15 +798,16 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity)) { - xaccSplitSetBaseValue (split, reverse ? amount : - gnc_numeric_neg (amount), commodity); + xaccSplitSetBaseValue (split, amount, commodity); } else { /* Need to value the payment in terms of the owner commodity */ - xaccSplitSetAmount(split, reverse ? amount : gnc_numeric_neg (amount)); - payment_value = gnc_numeric_mul(amount, exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP); - xaccSplitSetValue(split, reverse ? payment_value : gnc_numeric_neg(payment_value)); + gnc_numeric payment_value = gnc_numeric_mul(amount, + exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP); + + xaccSplitSetAmount(split, amount); + xaccSplitSetValue(split, payment_value); } } @@ -831,7 +819,7 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, xaccAccountInsertSplit (posted_acc, split); xaccAccountCommitEdit (posted_acc); xaccTransAppendSplit (txn, split); - xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) : amount, commodity); + xaccSplitSetBaseValue (split, gnc_numeric_neg (amount), commodity); /* Create a new lot for the payment */ payment_lot = gnc_lot_new (book);