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