gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Thu Mar 17 17:47:25 EDT 2016


Updated	 via  https://github.com/Gnucash/gnucash/commit/3109b6f6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d0e103be (commit)
	from  https://github.com/Gnucash/gnucash/commit/c2ce2044 (commit)



commit 3109b6f6c3bbfa939bce0e97177f8d654d06c0ce
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 17 15:41:51 2016 -0700

    Set the transaction currency during auto-completion.
    
    When auto-completing a transaction that was originally created in another
    account with a different currency the balancing code will try to apply
    conversions in the wrong direction if one edits the transaction. Explicitly
    setting the transaction currency to the current register's currency
    prevents the conversions being applied and allows the transaction to
    balance correctly.

diff --git a/src/register/ledger-core/split-register-control.c b/src/register/ledger-core/split-register-control.c
index 39565a4..493a67a 100644
--- a/src/register/ledger-core/split-register-control.c
+++ b/src/register/ledger-core/split-register-control.c
@@ -885,11 +885,15 @@ gnc_split_register_auto_completion (SplitRegister *reg,
 
         if (gnc_split_register_get_default_account (reg) != NULL)
         {
-            Account *default_account;
+            Account *default_account =
+                gnc_split_register_get_default_account (reg);
+            gnc_commodity *trans_cmdty = xaccTransGetCurrency(trans);
+            gnc_commodity *acct_cmdty = xaccAccountGetCommodity(default_account);
             Split *s;
             int i = 0;
-
-            default_account = gnc_split_register_get_default_account (reg);
+            if (gnc_commodity_is_currency(acct_cmdty) &&
+                !gnc_commodity_equal(trans_cmdty, acct_cmdty))
+                xaccTransSetCurrency(trans, acct_cmdty);
 
             while ((s = xaccTransGetSplit(trans, i)) != NULL)
             {

commit d0e103be086f65db04a006cefc09e6acf48f977a
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 17 15:37:19 2016 -0700

    Correctly re-value splits when the transaction currency is changed.
    
    When a transaction with existing splits had its currency changed, the
    function would change the values to use the new currency's denominator
    without changing the actual value. The balancing code would then apply
    the price of the the new "other" split to the amount, changing it as
    well. Changing the transaction currency back would convert the value in
    the other split correctly so that it would equal the amount that the
    balancing code wouldn't change anything. I actually detected this bug
    when I wrote the test but didn't recognize it as a bug.
    
    The new code first calculates a new price and then applies it to each
    split so that the transaction balances correctly in the new transaction
    currency. This also round-trips correctly

diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c
index eac94d6..06b8016 100644
--- a/src/engine/Transaction.c
+++ b/src/engine/Transaction.c
@@ -672,6 +672,8 @@ xaccTransCopyFromClipBoard(const Transaction *from_trans, Transaction *to_trans,
     xaccTransBeginEdit(to_trans);
 
     FOR_EACH_SPLIT(to_trans, xaccSplitDestroy(s));
+    g_list_free(to_trans->splits);
+    to_trans->splits = NULL;
 
     xaccTransSetCurrency(to_trans, xaccTransGetCurrency(from_trans));
     xaccTransSetDescription(to_trans, xaccTransGetDescription(from_trans));
@@ -1267,22 +1269,78 @@ xaccTransGetCurrency (const Transaction *trans)
     return trans ? trans->common_currency : NULL;
 }
 
+/* Helper functions for xaccTransSetCurrency */
+static gnc_numeric
+find_new_rate(Transaction *trans, gnc_commodity *curr)
+{
+    GList *node;
+    gnc_numeric rate = gnc_numeric_zero();
+    for (node = trans->splits; node != NULL; node = g_list_next (node))
+    {
+        Split *split = GNC_SPLIT(node->data);
+        gnc_commodity *split_com =
+            xaccAccountGetCommodity(xaccSplitGetAccount(split));
+        if (gnc_commodity_equal(curr, split_com))
+        {
+/* This looks backwards, but the amount of the balancing transaction
+ * that we're going to use it on is in the value's currency. */
+            rate = gnc_numeric_div(xaccSplitGetAmount(split),
+                                   xaccSplitGetValue(split),
+                                   GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
+            break;
+        }
+    }
+    return rate;
+}
+
+static void
+split_set_new_value(Split* split, gnc_commodity *curr, gnc_commodity *old_curr,
+                    gnc_numeric rate)
+{
+    gnc_commodity *split_com =
+        xaccAccountGetCommodity(xaccSplitGetAccount(split));
+    if (gnc_commodity_equal(curr, split_com))
+        xaccSplitSetValue(split, xaccSplitGetAmount(split));
+    else if (gnc_commodity_equal(old_curr, split_com))
+        xaccSplitSetSharePrice(split, rate);
+    else
+    {
+        gnc_numeric old_rate = gnc_numeric_div(xaccSplitGetValue(split),
+                                               xaccSplitGetAmount(split),
+                                               GNC_DENOM_AUTO,
+                                               GNC_HOW_RND_NEVER);
+        gnc_numeric new_rate = gnc_numeric_div(old_rate, rate, GNC_DENOM_AUTO,
+                                               GNC_HOW_RND_NEVER);
+        xaccSplitSetSharePrice(split, new_rate);
+    }
+}
+
+/**
+ * Set a new currency on a transaction.
+ * When we do that to a transaction with splits we need to re-value
+ * all of the splits in the new currency.
+ * @param trans: The transaction to change
+ * @param curr: The new currency to set.
+ */
 void
 xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr)
 {
-    gint fraction, old_fraction;
-
+    gnc_commodity *old_curr = trans->common_currency;
     if (!trans || !curr || trans->common_currency == curr) return;
     xaccTransBeginEdit(trans);
 
-    old_fraction = gnc_commodity_get_fraction (trans->common_currency);
     trans->common_currency = curr;
-    fraction = gnc_commodity_get_fraction (curr);
-
-    /* avoid needless crud if fraction didn't change */
-    if (fraction != old_fraction)
+    if (old_curr != NULL && trans->splits != NULL)
     {
-        FOR_EACH_SPLIT(trans, xaccSplitSetValue(s, xaccSplitGetValue(s)));
+        gnc_numeric rate = find_new_rate(trans, curr);
+        if (!gnc_numeric_zero_p (rate))
+        {
+            FOR_EACH_SPLIT(trans, split_set_new_value(s, curr, old_curr, rate));
+        }
+        else
+        {
+            FOR_EACH_SPLIT(trans, xaccSplitSetValue(s, xaccSplitGetValue(s)));
+        }
     }
 
     qof_instance_set_dirty(QOF_INSTANCE(trans));
diff --git a/src/engine/test/utest-Transaction.c b/src/engine/test/utest-Transaction.c
index 77569f0..50bb824 100644
--- a/src/engine/test/utest-Transaction.c
+++ b/src/engine/test/utest-Transaction.c
@@ -929,20 +929,18 @@ test_xaccTransEqual (Fixture *fixture, gconstpointer pData)
 
     g_free (check->msg);
     g_free (check2.msg);
-    check->msg = g_strdup("[xaccSplitEqual] amounts differ: 13333/1000 vs 100000/1000");
     check2.msg = g_strdup_printf (
                      "[xaccTransEqual] splits %s and %s differ", split_guid0, split_guid0);
     qof_instance_set_guid (split1, qof_instance_get_guid (split0));
     g_assert (!xaccTransEqual (clone, txn0, TRUE, TRUE, TRUE, TRUE));
     g_assert (xaccTransEqual (clone, txn0, TRUE, FALSE, FALSE, TRUE));
-    g_assert_cmpint (check->hits, ==, 11);
+    g_assert_cmpint (check->hits, ==, 10);
     g_assert_cmpint (check2.hits, ==, 2);
 
     qof_instance_set_guid (xaccTransGetSplit (txn1, 0),
                            qof_instance_get_guid (split0));
     qof_instance_set_guid (xaccTransGetSplit (txn1, 1),
                            qof_instance_get_guid (xaccTransGetSplit (txn0, 1)));
-    g_free (check->msg);
     {
         Split* split00 = xaccTransGetSplit (txn0, 0);
         Split* split01 = xaccTransGetSplit (txn0, 1);
@@ -958,7 +956,7 @@ test_xaccTransEqual (Fixture *fixture, gconstpointer pData)
         test_add_error (&check3);
         g_assert (!xaccTransEqual (txn1, txn0, TRUE, TRUE, TRUE, TRUE));
         g_assert (xaccTransEqual (txn1, txn0, TRUE, TRUE, FALSE, TRUE));
-        g_assert_cmpint (check->hits, ==, 12);
+        g_assert_cmpint (check->hits, ==, 11);
         g_assert_cmpint (check2.hits, ==, 3);
         g_assert_cmpint (check3.hits, ==, 0);
 



Summary of changes:
 src/engine/Transaction.c                          | 74 ++++++++++++++++++++---
 src/engine/test/utest-Transaction.c               |  6 +-
 src/register/ledger-core/split-register-control.c | 10 ++-
 3 files changed, 75 insertions(+), 15 deletions(-)



More information about the gnucash-changes mailing list