r22000 - gnucash/trunk/src - Rework interaction between payments and invoices part 2.
Geert Janssens
gjanssens at code.gnucash.org
Fri Feb 10 10:34:55 EST 2012
Author: gjanssens
Date: 2012-02-10 10:34:55 -0500 (Fri, 10 Feb 2012)
New Revision: 22000
Trac: http://svn.gnucash.org/trac/changeset/22000
Modified:
gnucash/trunk/src/business/business-gnome/dialog-payment.c
gnucash/trunk/src/business/business-gnome/gtkbuilder/dialog-payment.glade
gnucash/trunk/src/engine/gncOwner.c
gnucash/trunk/src/engine/gncOwner.h
Log:
Rework interaction between payments and invoices part 2.
This commit deals with paying invoices.
Modified: gnucash/trunk/src/business/business-gnome/dialog-payment.c
===================================================================
--- gnucash/trunk/src/business/business-gnome/dialog-payment.c 2012-02-10 15:34:45 UTC (rev 21999)
+++ gnucash/trunk/src/business/business-gnome/dialog-payment.c 2012-02-10 15:34:55 UTC (rev 22000)
@@ -204,7 +204,9 @@
/* Set the payment amount in the dialog */
val = gnc_payment_dialog_calculate_selected_total (pw);
- gnc_ui_payment_window_set_amount(pw, val);
+ /* 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));
}
static gboolean
@@ -259,6 +261,7 @@
GtkTreeIter iter;
Timespec doc_date;
GncInvoice *document;
+ gnc_numeric value = gnc_numeric_zero();
gnc_numeric debit = gnc_numeric_zero();
gnc_numeric credit = gnc_numeric_zero();
GncInvoiceType doc_type = GNC_INVOICE_UNDEFINED;
@@ -297,22 +300,16 @@
}
/* Find the debit/credit amount.
- * Invoices/bills are debit
- * Credit notes are credit
- * Pre-payments are credit
+ * Invoices/vendor credit notes are debit (increasing the balance)
+ * Customer credit notes/bills are credit (decreasing the balance)
+ * Pre-payments are debit or credit depending on their sign
*/
- if (document)
- {
- if (!gncInvoiceGetIsCreditNote (document))
- debit = gnc_lot_get_balance (lot);
- else
- credit = gnc_lot_get_balance (lot);
- }
+ value = gnc_lot_get_balance (lot);
+
+ if (gnc_numeric_positive_p (value))
+ debit = value;
else
- {
- /* This is a pre-payment */
- credit = gnc_lot_get_balance (lot);
- }
+ credit = gnc_numeric_neg (value);
/* Only display non-zero debits/credits */
if (!gnc_numeric_zero_p (debit))
@@ -514,6 +511,29 @@
return FALSE;
}
+/*
+ * This helper function is called once for each row in the tree view
+ * that is currently selected. Its task is to add the corresponding
+ * lot to the end of a glist.
+ */
+static void
+get_selected_lots (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GList **return_list = data;
+ GNCLot *lot;
+ GValue value = { 0 };
+
+ gtk_tree_model_get_value (model, iter, 5, &value);
+ lot = (GNCLot *) g_value_get_pointer (&value);
+ g_value_unset (&value);
+
+ if (lot)
+ *return_list = g_list_append(*return_list, lot);
+}
+
void
gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
{
@@ -527,6 +547,9 @@
/* Verify the amount is non-zero */
amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_edit));
+
+ /* 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))
{
text = _("You must enter the amount of the payment. "
@@ -569,13 +592,19 @@
const char *memo, *num;
Timespec date;
gnc_numeric exch = gnc_numeric_create(1, 1); //default to "one to one" rate
+ GNCLot *payment_lot;
+ GList *selected_lots = NULL;
+ GtkTreeSelection *selection;
/* Obtain all our ancillary information */
memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry));
num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry));
date = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (pw->date_edit));
- /* FIXME Get a lotlist from the dialog */
+ /* Obtain the list of selected lots (documents/payments) from the dialog */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
+ gtk_tree_selection_selected_foreach (selection, get_selected_lots, &selected_lots);
+
/* If the 'acc' account and the post account don't have the same
currency, we need to get the user to specify the exchange rate */
if (!gnc_commodity_equal(xaccAccountGetCommodity(acc), xaccAccountGetCommodity(post)))
@@ -600,64 +629,17 @@
gnc_xfer_dialog_run_until_done(xfer);
}
- if (!gnc_payment_dialog_has_pre_existing_txn(pw))
- {
- /* Now apply the payment */
- gncOwnerApplyPayment (&pw->owner, pw->invoice,
- post, acc, amount, exch, date, memo, num);
- }
- else
- {
- // New implementation: Allow the user to have a
- // pre-selected transaction
- Transaction *txn = pw->pre_existing_txn;
- GncOwner *owner = &pw->owner;
+ /* Create a lot for this payment */
+ payment_lot = gncOwnerCreatePaymentLot (&pw->owner, pw->pre_existing_txn,
+ post, acc, amount, exch, date, memo, num);
- Split *xfer_split = xaccTransFindSplitByAccount(txn, acc);
-
- if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner))
- {
- g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction.");
- xfer_split = NULL;
- }
-
- if (!xfer_split)
- {
- g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one.");
-
- xaccTransBeginEdit (txn);
- xaccTransDestroy (txn);
- xaccTransCommitEdit (txn);
-
- pw->pre_existing_txn = NULL;
- gncOwnerApplyPayment (owner, pw->invoice,
- post, acc, amount, exch, date, memo, num);
- }
- else
- {
- int i = 0;
- xaccTransBeginEdit (txn);
- while (i < xaccTransCountSplits(txn))
- {
- Split *split = xaccTransGetSplit (txn, i);
- if (split == xfer_split)
- {
- ++i;
- }
- else
- {
- xaccSplitDestroy(split);
- }
- }
-
- // We have opened the txn for editing, and we have deleted all the other splits.
- gncOwnerAssignPaymentTxn (owner, txn,
- post, pw->invoice);
-
- xaccTransCommitEdit (txn);
- }
- }
-
+ /* And link the selected lots and the payment lot together as well as possible.
+ * If the payment was bigger than the selected documents/overpayments, only
+ * part of the payment will be used. Similarly if more documents were selected
+ * than the payment value set, not all documents will be marked as paid. */
+ if (payment_lot)
+ selected_lots = g_list_prepend (selected_lots, payment_lot);
+ gncOwnerAutoApplyPaymentsWithLots (&pw->owner, selected_lots);
}
gnc_resume_gui_refresh ();
@@ -1094,6 +1076,8 @@
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_xferaccount(pw, xaccSplitGetAccount(assetaccount_split));
if (postaccount_split)
Modified: gnucash/trunk/src/business/business-gnome/gtkbuilder/dialog-payment.glade
===================================================================
--- gnucash/trunk/src/business/business-gnome/gtkbuilder/dialog-payment.glade 2012-02-10 15:34:45 UTC (rev 21999)
+++ gnucash/trunk/src/business/business-gnome/gtkbuilder/dialog-payment.glade 2012-02-10 15:34:55 UTC (rev 22000)
@@ -183,7 +183,7 @@
<property name="resizable">True</property>
<property name="sizing">fixed</property>
<property name="min_width">50</property>
- <property name="title" translatable="yes">Debet</property>
+ <property name="title" translatable="yes">Debit</property>
<property name="reorderable">True</property>
<child>
<object class="GtkCellRendererText" id="docs_list_deb_renderer"/>
Modified: gnucash/trunk/src/engine/gncOwner.c
===================================================================
--- gnucash/trunk/src/engine/gncOwner.c 2012-02-10 15:34:45 UTC (rev 21999)
+++ gnucash/trunk/src/engine/gncOwner.c 2012-02-10 15:34:55 UTC (rev 22000)
@@ -899,6 +899,144 @@
* Apply a payment of "amount" for the owner, between the xfer_account
* (bank or other asset) and the posted_account (A/R or A/P).
*/
+GNCLot *
+gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
+ Account *posted_acc, Account *xfer_acc,
+ gnc_numeric amount, gnc_numeric exch, Timespec date,
+ const char *memo, const char *num)
+{
+ QofBook *book;
+ Split *split;
+ const char *name;
+ gnc_commodity *commodity;
+ gboolean reverse;
+ gnc_numeric payment_value = amount;
+ Split *xfer_split = NULL;
+ GNCLot *payment_lot;
+
+ /* Verify our arguments */
+ if (!owner || !posted_acc || !xfer_acc) return NULL;
+ g_return_val_if_fail (owner->owner.undefined != NULL, NULL);
+
+ /* Compute the ancillary data */
+ book = gnc_account_get_book (posted_acc);
+ name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner));
+ commodity = gncOwnerGetCurrency (owner);
+ reverse = use_reversed_payment_amounts(owner);
+
+ if (txn)
+ {
+ /* Pre-existing transaction was specified. We completely clear it,
+ * except for the split in the transfer account, unless the
+ * transaction can't be reused (wrong currency, wrong transfer account).
+ * In that case, the transaction is simply removed and an new
+ * one created. */
+
+ xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc);
+
+ if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner))
+ {
+ g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction.");
+ xfer_split = NULL;
+ }
+
+ if (!xfer_split)
+ {
+ g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one.");
+
+ xaccTransBeginEdit (txn);
+ xaccTransDestroy (txn);
+ xaccTransCommitEdit (txn);
+
+ txn = NULL;
+ }
+ else
+ {
+ int i = 0;
+ xaccTransBeginEdit (txn);
+ while (i < xaccTransCountSplits(txn))
+ {
+ Split *split = xaccTransGetSplit (txn, i);
+ if (split == xfer_split)
+ {
+ ++i;
+ }
+ else
+ {
+ xaccSplitDestroy(split);
+ }
+ }
+ xaccTransCommitEdit (txn);
+ }
+ }
+
+ /* Create the transaction if we don't have one yet */
+ if (!txn)
+ txn = xaccMallocTransaction (book);
+
+ /* Insert a split for the transfer account if we don't have one yet */
+ if (!xfer_split)
+ {
+ xaccTransBeginEdit (txn);
+
+ /* Set up the transaction */
+ xaccTransSetDescription (txn, name ? name : "");
+ xaccTransSetNum (txn, num);
+ xaccTransSetCurrency (txn, commodity);
+ xaccTransSetDateEnteredSecs (txn, time(NULL));
+ xaccTransSetDatePostedTS (txn, &date);
+ xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
+
+
+ /* The split for the transfer account */
+ split = xaccMallocSplit (book);
+ xaccSplitSetMemo (split, memo);
+ xaccSplitSetAction (split, _("Payment"));
+ xaccAccountBeginEdit (xfer_acc);
+ xaccAccountInsertSplit (xfer_acc, split);
+ xaccAccountCommitEdit (xfer_acc);
+ xaccTransAppendSplit (txn, split);
+
+ if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity))
+ {
+ xaccSplitSetBaseValue (split, reverse ? amount :
+ gnc_numeric_neg (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));
+ }
+ }
+
+ /* Add a split in the post account */
+ split = xaccMallocSplit (book);
+ xaccSplitSetMemo (split, memo);
+ xaccSplitSetAction (split, _("Payment"));
+ xaccAccountBeginEdit (posted_acc);
+ xaccAccountInsertSplit (posted_acc, split);
+ xaccAccountCommitEdit (posted_acc);
+ xaccTransAppendSplit (txn, split);
+ xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) : amount, commodity);
+
+ /* Create a new lot for the payment */
+ payment_lot = gnc_lot_new (book);
+ gncOwnerAttachToLot (owner, payment_lot);
+ gnc_lot_add_split (payment_lot, split);
+
+
+ /* Commit this new transaction */
+ xaccTransCommitEdit (txn);
+
+ return payment_lot;
+}
+
+/*
+ * Apply a payment of "amount" for the owner, between the xfer_account
+ * (bank or other asset) and the posted_account (A/R or A/P).
+ */
Transaction *
gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
Account *posted_acc, Account *xfer_acc,
@@ -976,8 +1114,8 @@
* perform a balancing action on a set of lots, so you
* will also find frequent references to balancing instead. */
- /* Payments can only be applied when at least an owner is given
- * and either a list of lots to use or a first lot */
+ /* Payments can only be applied when at least an owner
+ * and a list of lots to use are given */
if (!owner) return;
if (!lots) return;
Modified: gnucash/trunk/src/engine/gncOwner.h
===================================================================
--- gnucash/trunk/src/engine/gncOwner.h 2012-02-10 15:34:45 UTC (rev 21999)
+++ gnucash/trunk/src/engine/gncOwner.h 2012-02-10 15:34:55 UTC (rev 22000)
@@ -191,6 +191,19 @@
KvpFrame* gncOwnerGetSlots(GncOwner* owner);
/**
+ * Create a lot for a payment for the given owner and with the given
+ * parameters. If a transaction is passed, this transaction will be
+ * reused if possible (meaning, if the transaction currency matches
+ * the owner's currency and if the transaction has (at least?) one
+ * split in the transfer account).
+ */
+GNCLot *
+gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
+ Account *posted_acc, Account *xfer_acc,
+ gnc_numeric amount, gnc_numeric exch, Timespec date,
+ const char *memo, const char *num);
+
+/**
* Apply a payment of "amount" for the owner, between the xfer_account
* (bank or other asset) and the posted_account (A/R or A/P). If the
* caller supplies an (optional) invoice argument, then apply the
More information about the gnucash-changes
mailing list