gnucash master: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Mon Oct 26 15:35:38 EDT 2015


Updated	 via  https://github.com/Gnucash/gnucash/commit/5537a7ed (commit)
	 via  https://github.com/Gnucash/gnucash/commit/50e3bf21 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f5cf2ba5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/362b9e3d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ae35dbb4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dfc0b28b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4bfe29ac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9a53cc8f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7298a469 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/811a30db (commit)
	 via  https://github.com/Gnucash/gnucash/commit/70493537 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ab15ca8f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/76c1259f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/348fe45b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a40bc92d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6e141377 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f30b38b5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a8d4eaae (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f79a3af4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/be5b9f2b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9c2813ac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/96678937 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5e609dac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c4082524 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ca447fc0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/85148cd2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e94622c9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ab535fb1 (commit)
	from  https://github.com/Gnucash/gnucash/commit/de264c58 (commit)



commit 5537a7ed35a597f68701fd97d3bc4670b92a9bf5
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Oct 23 14:14:20 2015 -0700

    Implement gnc_numeric_invert via GncRational::invert.

diff --git a/src/libqof/qof/gnc-numeric.cpp b/src/libqof/qof/gnc-numeric.cpp
index 547b619..f9adbdf 100644
--- a/src/libqof/qof/gnc-numeric.cpp
+++ b/src/libqof/qof/gnc-numeric.cpp
@@ -487,20 +487,7 @@ gnc_numeric_invert(gnc_numeric num)
 {
     if (num.num == 0)
         return gnc_numeric_zero();
-    if (num.denom > 0)
-    {
-        if (num.num < 0)
-            return gnc_numeric_create (-num.denom, -num.num);
-        return gnc_numeric_create (num.denom, num.num);
-    }
-    else /* Negative denominator means multiply instead of divide. */
-    {
-        int64_t mult = (num.num < 0 ? INT64_C(-1) : INT64_C(1));
-        qofint128 denom = mult128(-num.denom, mult * num.num);
-        if (denom.hi)
-            return gnc_numeric_error(GNC_ERROR_OVERFLOW);
-        return gnc_numeric_create (mult, denom.lo);
-    }
+    return static_cast<gnc_numeric>(GncNumeric(num).inv());
 }
 /* *******************************************************************
  *  double_to_gnc_numeric
diff --git a/src/libqof/qof/gnc-rational.cpp b/src/libqof/qof/gnc-rational.cpp
index 35e7e6f..a3db422 100644
--- a/src/libqof/qof/gnc-rational.cpp
+++ b/src/libqof/qof/gnc-rational.cpp
@@ -80,9 +80,7 @@ GncRational::operator-() const noexcept
 GncRational&
 GncRational::inv () noexcept
 {
-    auto tmp = m_num;
-    m_num = m_den;
-    m_den = tmp;
+    std::swap(m_num, m_den);
 
     GncRational b {1, 1};
     GncDenom d {*this, b, INT64_C(0), GNC_HOW_RND_NEVER };
diff --git a/src/libqof/qof/gnc-rational.hpp b/src/libqof/qof/gnc-rational.hpp
index dd25789..1c4edc6 100644
--- a/src/libqof/qof/gnc-rational.hpp
+++ b/src/libqof/qof/gnc-rational.hpp
@@ -44,6 +44,7 @@ public:
     GncRational& div(GncRational b, GncDenom& d) noexcept;
     GncRational& add(const GncRational& b, GncDenom& d) noexcept;
     GncRational& sub(const GncRational& b, GncDenom& d) noexcept;
+
 /** Inverts the number, equivalent of /= {1, 1} */
     GncRational& inv() noexcept;
 

commit 50e3bf21db44661ac4273a1b52a596a2d957c792
Merge: de264c5 f5cf2ba
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 24 14:55:56 2015 -0700

    Merge branch 'maint'


commit f5cf2ba54264006aed462e9680b7a36e4a911ecc
Merge: e3da1c4 362b9e3
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 24 14:52:49 2015 -0700

    Merge branch 'single-price' into maint


commit 362b9e3d7c4dfff0e11656d6d983866e7d15b980
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 24 13:28:55 2015 -0700

    Fix leaking QofBook in most of the engine unit tests.
    
    The problem is that QofBook is not a well-formed GObject and doesn't clean
    itself up properly when its ref count goes to zero. qof_book_destroy() must
    be explicitly called on it. An interesting side effect is that QofObject
    keeps a list of all of the books ever opened and not properly destroyed and
    registering a class (in this case GNCPriceDB) tries to create that class's
    instance in all of those leaked books. Since they already have one, the
    instantiation code raises a critical error and the test program fails.

diff --git a/src/engine/test/utest-Account.c b/src/engine/test/utest-Account.c
index c027d8b..0f23dfc 100644
--- a/src/engine/test/utest-Account.c
+++ b/src/engine/test/utest-Account.c
@@ -694,8 +694,7 @@ test_gnc_book_set_get_root_account (Fixture *fixture, gconstpointer pData)
     /* acc1 gets freed by setting the root accout to acc2
         g_object_unref (acc1);
     */
-    g_object_unref (book1);
-    g_object_unref (acc2);
+    qof_book_destroy (book1);
 }
 
 /* xaccMallocAccount
@@ -711,8 +710,8 @@ test_xaccMallocAccount (void)
     g_assert (acc != NULL);
     test_signal_assert_hits (signal, 1);
     test_signal_free (signal);
-    g_object_unref (book);
     g_object_unref (acc);
+    qof_book_destroy (book);
 }
 
 /* gnc_account_create_root
@@ -734,8 +733,8 @@ test_gnc_account_create_root (void)
     g_object_get (acc, "name", &name, NULL);
     g_assert_cmpstr (name, == , "Root Account");
     g_assert (gnc_book_get_root_account (book) == acc);
-    g_object_unref (book);
     g_object_unref (acc);
+    qof_book_destroy (book);
     g_free (func);
     g_free (name);
 }
@@ -1403,7 +1402,7 @@ test_xaccAccountOrder ( )
     xaccAccountDestroy (aa);
     xaccAccountBeginEdit (ab);
     xaccAccountDestroy (ab);
-    g_object_unref (book);
+    qof_book_destroy (book);
 }
 /* qof_xaccAccountOrder
 static int
@@ -1544,7 +1543,7 @@ test_gnc_account_append_remove_child (Fixture *fixture, gconstpointer pData)
     xaccAccountDestroy (account);
     xaccAccountBeginEdit (froot);
     xaccAccountDestroy (froot);
-    g_object_unref (fbook);
+    qof_book_destroy (fbook);
 
 }
 /* Simple Getters or passthroughs, no tests:
diff --git a/src/engine/test/utest-Budget.c b/src/engine/test/utest-Budget.c
index 0509cfd..fdb624a 100644
--- a/src/engine/test/utest-Budget.c
+++ b/src/engine/test/utest-Budget.c
@@ -675,11 +675,7 @@ test_gnc_book_set_get_root_account (Fixture *fixture, gconstpointer pData)
     g_assert (gnc_book_get_root_account (book1) != acc1);
     g_assert (gnc_book_get_root_account (book1) == acc2);
     /* Clean up */
-    /* acc1 gets freed by setting the root accout to acc2
-        g_object_unref (acc1);
-    */
-    g_object_unref (book1);
-    g_object_unref (acc2);
+    qof_book_destroy (book1);
 }
 
 /* xaccMallocAccount
@@ -695,8 +691,7 @@ test_xaccMallocAccount (void)
     g_assert (acc != NULL);
     test_signal_assert_hits (signal, 1);
     test_signal_free (signal);
-    g_object_unref (book);
-    g_object_unref (acc);
+    qof_book_destroy (book);
 }
 
 /* gnc_account_create_root
@@ -718,8 +713,7 @@ test_gnc_account_create_root (void)
     g_object_get (acc, "name", &name, NULL);
     g_assert_cmpstr (name, == , "Root Account");
     g_assert (gnc_book_get_root_account (book) == acc);
-    g_object_unref (book);
-    g_object_unref (acc);
+    qof_book_destroy (book);
     g_free (func);
     g_free (name);
 }
@@ -1363,7 +1357,7 @@ test_xaccAccountOrder ( )
     xaccAccountDestroy (aa);
     xaccAccountBeginEdit (ab);
     xaccAccountDestroy (ab);
-    g_object_unref (book);
+    qof_book_destroy (book);
 }
 /* qof_xaccAccountOrder
 static int
@@ -1504,7 +1498,7 @@ test_gnc_account_append_remove_child (Fixture *fixture, gconstpointer pData)
     xaccAccountDestroy (account);
     xaccAccountBeginEdit (froot);
     xaccAccountDestroy (froot);
-    g_object_unref (fbook);
+    qof_book_destroy (fbook);
 
 }
 /* Simple Getters or passthroughs, no tests:
@@ -2494,6 +2488,7 @@ test_gnc_set_budget_num_periods()
     g_assert_cmpint(gnc_budget_get_num_periods(budget), ==, 20);
 
     gnc_budget_destroy(budget);
+    qof_book_destroy(book);
 }
 
 static void
@@ -2544,6 +2539,7 @@ test_gnc_set_budget_recurrence()
     }
 
     gnc_budget_destroy(budget);
+    qof_book_destroy(book);
 }
 
 static void
@@ -2577,9 +2573,8 @@ test_gnc_set_budget_account_period_value()
     g_assert_cmpint (check.hits, ==, 1);
     g_log_set_default_handler (oldlogger, NULL);
 
-    g_object_unref(book);
-    g_object_unref(acc);
     gnc_budget_destroy(budget);
+    qof_book_destroy(book);
 }
 
 void
diff --git a/src/engine/test/utest-Split.c b/src/engine/test/utest-Split.c
index 0064a8b..6d0446c 100644
--- a/src/engine/test/utest-Split.c
+++ b/src/engine/test/utest-Split.c
@@ -110,7 +110,7 @@ teardown (Fixture *fixture, gconstpointer pData)
     test_destroy (fixture->split);
     test_destroy (fixture->curr);
     test_destroy (fixture->comm);
-    test_destroy (book);
+    qof_book_destroy (book);
     g_free (fixture->func);
     g_slist_free_full (fixture->hdlrs, test_free_log_handler);
     test_clear_error_list();
@@ -171,7 +171,7 @@ test_gnc_split_dispose ()
     g_assert (instance->e_type == NULL);
 
     g_object_unref (split);
-    g_object_unref (book);
+    qof_book_destroy (book);
 }
 /* gnc_split_finalize
 static void
@@ -251,7 +251,7 @@ test_gnc_split_set_get_property ()
     g_object_unref (lot);
     gnc_commodity_destroy (curr);
     g_object_unref (split);
-    g_object_unref (book);
+    qof_book_destroy (book);
 }
 
 /* xaccInitSplit
@@ -1094,7 +1094,7 @@ test_xaccSplitDestroy ()
     test_destroy (txn);
     test_destroy (acc);
     test_destroy (gnaira);
-    test_destroy (book);
+    qof_book_destroy (book);
 }
 /* xaccSplitOrder
 gint
diff --git a/src/engine/test/utest-Transaction.c b/src/engine/test/utest-Transaction.c
index e81a9a4..77569f0 100644
--- a/src/engine/test/utest-Transaction.c
+++ b/src/engine/test/utest-Transaction.c
@@ -215,7 +215,7 @@ teardown (Fixture *fixture, gconstpointer pData)
     test_destroy (fixture->curr);
     test_destroy (fixture->comm);
     g_free (mbe);
-    test_destroy (book);
+    qof_book_destroy(book);
     g_slist_free_full (fixture->hdlrs, test_free_log_handler);
     test_clear_error_list();
 }
@@ -387,7 +387,7 @@ test_gnc_transaction_dispose ()
     test_destroy (curr);
 
     test_destroy (txn);
-    test_destroy (book);
+    qof_book_destroy (book);
 }
 /* gnc_transaction_finalize
 static void
@@ -473,7 +473,7 @@ test_gnc_transaction_set_get_property (Fixture *fixture, gconstpointer pData)
     xaccTransRollbackEdit (txn);
     test_destroy (txn);
     test_destroy (curr);
-    test_destroy (book);
+    qof_book_destroy (book);
     g_free (t_entered);
 }
 /* gnc_transaction_class_init
@@ -511,7 +511,7 @@ test_xaccMallocTransaction (Fixture *fixture, gconstpointer pData)
     test_signal_assert_hits (sig1, 1);
 
     test_destroy (txn);
-    test_destroy (book);
+    qof_book_destroy (book);
     test_signal_free (sig1);
 }
 /* xaccTransSortSplits
@@ -1420,7 +1420,7 @@ test_xaccTransDestroy ()
 
     test_destroy (txn);
     test_destroy (dupe);
-    test_destroy (book);
+    qof_book_destroy (book);
 }
 /* destroy_gains
 static void
@@ -1674,7 +1674,7 @@ test_xaccTransCommitEdit (void)
     test_destroy (acc2);
     test_destroy (curr);
     test_destroy (comm);
-    test_destroy (book);
+    qof_book_destroy (book);
 }
 /* xaccTransRollbackEdit
 void

commit ae35dbb46407762c925346975588cd58fdb60ca3
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Oct 18 11:44:51 2015 -0700

    Use gnc_pricedb_has_prices instead of testing the return value of get_prices.

diff --git a/src/gnome/dialog-commodities.c b/src/gnome/dialog-commodities.c
index 1a0e385..3f29a29 100644
--- a/src/gnome/dialog-commodities.c
+++ b/src/gnome/dialog-commodities.c
@@ -164,8 +164,7 @@ remove_clicked (CommoditiesDialog *cd)
     g_list_free (accounts);
 
     pdb = gnc_pricedb_get_db (cd->book);
-    prices = gnc_pricedb_get_prices(pdb, commodity, NULL);
-    if (prices)
+    if (gnc_pricedb_has_prices(pdb, commodity, NULL))
     {
         message = _("This commodity has price quotes. Are "
                     "you sure you want to delete the selected "

commit dfc0b28bedc3d4bc0f9d9afc9d638898cf08e6a3
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Oct 16 12:40:32 2015 -0700

    Remove be->price_lookup conditional clauses.
    
    No backend implements price_lookup, and the struct member is removed from
    master. Even if it had existed these clauses wouldn't actually do anything.

diff --git a/src/engine/gnc-pricedb-p.h b/src/engine/gnc-pricedb-p.h
index 8d867da..091cd64 100644
--- a/src/engine/gnc-pricedb-p.h
+++ b/src/engine/gnc-pricedb-p.h
@@ -78,17 +78,6 @@ typedef enum
     LOOKUP_EARLIEST_AFTER
 } PriceLookupType;
 
-
-struct gnc_price_lookup_s
-{
-    PriceLookupType type;
-    GNCPriceDB     *prdb;
-    const gnc_commodity  *commodity;
-    const gnc_commodity  *currency;
-    Timespec        date;
-};
-
-
 typedef struct gnc_price_lookup_helper_s
 {
     GList    **return_list;
diff --git a/src/engine/gnc-pricedb.c b/src/engine/gnc-pricedb.c
index bdc8e41..123c520 100644
--- a/src/engine/gnc-pricedb.c
+++ b/src/engine/gnc-pricedb.c
@@ -1364,24 +1364,9 @@ gnc_pricedb_lookup_latest(GNCPriceDB *db,
     GList *price_list;
     GNCPrice *result;
     GHashTable *currency_hash;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !commodity || !currency) return NULL;
     ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_LATEST;
-        pl.prdb = db;
-        pl.commodity = commodity;
-        pl.currency = currency;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
 
     currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
     if (!currency_hash)
@@ -1426,26 +1411,11 @@ gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db,
 {
     GList *result;
     GHashTable *currency_hash;
-    QofBook *book;
-    QofBackend *be;
 
     result = NULL;
 
     if (!db || !commodity) return NULL;
     ENTER ("db=%p commodity=%p", db, commodity);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_LATEST;
-        pl.prdb = db;
-        pl.commodity = commodity;
-        pl.currency = NULL;  /* can the backend handle this??? */
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
     if (!currency_hash)
     {
@@ -1483,24 +1453,9 @@ gnc_pricedb_has_prices(GNCPriceDB *db,
     GList *price_list;
     GHashTable *currency_hash;
     gint size;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !commodity) return FALSE;
     ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (book && be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_ALL;
-        pl.prdb = db;
-        pl.commodity = commodity;
-        pl.currency = currency;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
     if (!currency_hash)
     {
@@ -1535,27 +1490,13 @@ gnc_pricedb_get_prices(GNCPriceDB *db,
     GList *result;
     GList *node;
     GHashTable *currency_hash;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !commodity) return NULL;
     ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_ALL;
-        pl.prdb = db;
-        pl.commodity = commodity;
-        pl.currency = currency;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
     if (!currency_hash)
     {
+        currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
         LEAVE (" no currency hash");
         return NULL;
     }
@@ -1602,25 +1543,9 @@ gnc_pricedb_lookup_at_time(GNCPriceDB *db,
     GList *result = NULL;
     GList *item = NULL;
     GHashTable *currency_hash;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !c || !currency) return NULL;
     ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_AT_TIME;
-        pl.prdb = db;
-        pl.commodity = c;
-        pl.currency = currency;
-        pl.date = t;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, c);
     if (!currency_hash)
     {
@@ -1664,25 +1589,9 @@ lookup_nearest_in_time(GNCPriceDB *db,
     GNCPrice *result = NULL;
     GList *item = NULL;
     GHashTable *currency_hash;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !c || !currency) return NULL;
     ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_NEAREST_IN_TIME;
-        pl.prdb = db;
-        pl.commodity = c;
-        pl.currency = currency;
-        pl.date = t;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, c);
     if (!currency_hash)
     {
@@ -1812,26 +1721,10 @@ gnc_pricedb_lookup_latest_before (GNCPriceDB *db,
         GNCPrice *result = NULL;*/
     GList *item = NULL;
     GHashTable *currency_hash;
-    QofBook *book;
-    QofBackend *be;
     Timespec price_time;
 
     if (!db || !c || !currency) return NULL;
     ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_LATEST_BEFORE;
-        pl.prdb = db;
-        pl.commodity = c;
-        pl.currency = currency;
-        pl.date = t;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, c);
     if (!currency_hash)
     {
@@ -1963,25 +1856,9 @@ gnc_pricedb_lookup_nearest_in_time_any_currency(GNCPriceDB *db,
     GList *result = NULL;
     GHashTable *currency_hash;
     GNCPriceLookupHelper lookup_helper;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !c) return NULL;
     ENTER ("db=%p commodity=%p", db, c);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_NEAREST_IN_TIME;
-        pl.prdb = db;
-        pl.commodity = c;
-        pl.currency = NULL;  /* can the backend handle this??? */
-        pl.date = t;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, c);
     if (!currency_hash)
     {
@@ -2014,25 +1891,9 @@ gnc_pricedb_lookup_latest_before_any_currency(GNCPriceDB *db,
     GList *result = NULL;
     GHashTable *currency_hash;
     GNCPriceLookupHelper lookup_helper;
-    QofBook *book;
-    QofBackend *be;
 
     if (!db || !c) return NULL;
     ENTER ("db=%p commodity=%p", db, c);
-    book = qof_instance_get_book(&db->inst);
-    be = qof_book_get_backend(book);
-#ifdef GNUCASH_MAJOR_VERSION
-    if (be && be->price_lookup)
-    {
-        GNCPriceLookup pl;
-        pl.type = LOOKUP_LATEST_BEFORE;
-        pl.prdb = db;
-        pl.commodity = c;
-        pl.currency = NULL;  /* can the backend handle this??? */
-        pl.date = t;
-        (be->price_lookup) (be, &pl);
-    }
-#endif
     currency_hash = g_hash_table_lookup(db->commodity_hash, c);
     if (!currency_hash)
     {
diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h
index e3966b3..5c3a625 100644
--- a/src/engine/gnc-pricedb.h
+++ b/src/engine/gnc-pricedb.h
@@ -153,7 +153,6 @@ GType gnc_pricedb_get_type(void);
  @{ */
 
 /** */
-typedef struct gnc_price_lookup_s GNCPriceLookup;
 typedef GList PriceList;
 
 /** Price source enum. Be sure to keep in sync with the source_name array in

commit 4bfe29aca3beeac18d0abcf0fabee1ecc716d4e8
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Sep 15 12:03:39 2015 -0700

    Prevent F::Q from updating PRICE_SOURCE_EDIT_DLG prices.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 8e1e79e..a17b7f3 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -1324,7 +1324,7 @@ gnc_xfer_dialog_set_fetch_sensitive (GtkWidget *fetch)
     if (gnc_quote_source_fq_installed ())
     {
         gtk_widget_set_sensitive (fetch, TRUE);
-        gtk_widget_set_tooltip_text (fetch, _("Retrieve the current online quote"));
+        gtk_widget_set_tooltip_text (fetch, _("Retrieve the current online quote. This will fail if there is a manually-created price for today."));
         return;
     }
     gtk_widget_set_sensitive (fetch, FALSE);
diff --git a/src/scm/price-quotes.scm b/src/scm/price-quotes.scm
index b597a94..f3d97e0 100644
--- a/src/scm/price-quotes.scm
+++ b/src/scm/price-quotes.scm
@@ -412,14 +412,16 @@
                                                       commodity currency
                                                       gnc-time))
             (if (not (null? saved-price))
-              (begin
-                (gnc-price-begin-edit saved-price)
-                (gnc-price-set-time saved-price gnc-time)
+                (if (> (gnc-price-get-source saved-price) PRICE-SOURCE-FQ)
+                    (begin
+                      (gnc-price-begin-edit saved-price)
+                      (gnc-price-set-time saved-price gnc-time)
                       (gnc-price-set-source saved-price PRICE-SOURCE-FQ)
-                (gnc-price-set-typestr saved-price price-type)
-                (gnc-price-set-value saved-price price)
-                (gnc-price-commit-edit saved-price)
-                #f)
+                      (gnc-price-set-typestr saved-price price-type)
+                      (gnc-price-set-value saved-price price)
+                      (gnc-price-commit-edit saved-price)
+                      #f)
+                    #f)
               (let ((gnc-price (gnc-price-create book)))
                 (if (not gnc-price)
                     (string-append

commit 9a53cc8f58ad2dba4ea60b5720c5a6c890c2c15a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Sep 12 12:09:46 2015 -0700

    Remove unnecessary and harmful price rounding.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 926af38..8e1e79e 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -331,10 +331,7 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
     /* grab the price from the pricedb */
     price_value = gnc_price_get_value (pr.price);
     if (pr.reverse)
-    {
         price_value = gnc_numeric_invert (price_value);
-        price_value = round_price(pr.from, pr.to, price_value);
-    }
     gnc_price_unref(pr.price);
 
     /* and set the price entry */
@@ -1087,8 +1084,6 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
 
     gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit));
     price_value = gnc_xfer_dialog_compute_price_value(xferData);
-    price_value = round_price(xferData->from_commodity, xferData->to_commodity,
-                              price_value);
     gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
                                price_value);
     xferData->price_source = PRICE_SOURCE_XFER_DLG_VAL;
@@ -1594,7 +1589,6 @@ swap_commodities(gnc_commodity **from, gnc_commodity **to, gnc_numeric value)
     *to = *from;
     *from = tmp;
     value = gnc_numeric_invert(value);
-    value = round_price(*from, *to, value);
     return value;
 }
 
@@ -1880,8 +1874,7 @@ gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
     {
         gnc_numeric price_value = gnc_price_get_value(pr.price);
         if (pr.reverse)
-            price_value = round_price(pr.from, pr.to,
-                                      gnc_numeric_invert(price_value));
+            price_value = gnc_numeric_invert(price_value);
          gnc_xfer_dialog_set_price_edit(xferData, price_value);
         gnc_price_unref (pr.price);
     }
@@ -2498,10 +2491,7 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
         gnc_xfer_dialog_select_to_currency(xfer, txn_cur);
         gnc_xfer_dialog_select_from_currency(xfer, xfer_com);
         if (!gnc_numeric_zero_p(*exch_rate))
-        {
             dialog_rate = gnc_numeric_invert(*exch_rate);
-            dialog_rate = round_price(xfer_com, txn_cur, *exch_rate);
-        }
         amount = gnc_numeric_neg(amount);
     }
     else

commit 7298a469994c23a4e0526556ab31bf9940b9c7e1
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Sep 9 15:06:56 2015 -0700

    Implement user-entered-price preference.
    
    Add user:price as a source and prefer values with lower PriceSource enum
    values over higher ones: In other words a price with a lower PriceSource
    value (e.g. user:price-editor) will overwrite one with a higher value (e.g.
    user:split-register) and not the other way around.

diff --git a/src/engine/gnc-pricedb.c b/src/engine/gnc-pricedb.c
index 8cb841b..bdc8e41 100644
--- a/src/engine/gnc-pricedb.c
+++ b/src/engine/gnc-pricedb.c
@@ -71,8 +71,12 @@ gnc_price_init(GNCPrice* price)
  */
 static const char* source_names[] =
 {
-    "user:price-editor", //sync with price_to_gui in dialog-price-editor.c
-    "Finance::Quote",    //sync with
+    /* sync with price_to_gui in dialog-price-editor.c */
+    "user:price-editor",
+    /* sync with commidity-tz-quote->price in price-quotes.scm */
+    "Finance::Quote",
+    "user:price",
+    /* String retained for backwards compatibility. */
     "user:xfer-dialog",
     "user:split-register",
     "user:stock-split",
@@ -1004,7 +1008,7 @@ insert_or_replace_price(GNCPriceDB *db, GNCPrice *p)
                                                   p->currency, p->tmspec);
     if (old_price == NULL)
         return TRUE;
-    if (p->source != PRICE_SOURCE_FQ)
+    if (p->source < old_price->source)
         return TRUE;
     return FALSE;
 
diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h
index 00a4126..e3966b3 100644
--- a/src/engine/gnc-pricedb.h
+++ b/src/engine/gnc-pricedb.h
@@ -165,7 +165,8 @@ typedef enum
 {
     PRICE_SOURCE_EDIT_DLG,         // "user:price-editor"
     PRICE_SOURCE_FQ,               // "Finance::Quote"
-    PRICE_SOURCE_XFER_DLG,         // "user:xfer-dialog"
+    PRICE_SOURCE_USER_PRICE,       // "user:price"
+    PRICE_SOURCE_XFER_DLG_VAL,     // "user:xfer-dialog"
     PRICE_SOURCE_SPLIT_REG,        // "user:split-register"
     PRICE_SOURCE_STOCK_SPLIT,      // "user:stock-split"
     PRICE_SOURCE_INVOICE,          // "user:invoice-post"
@@ -174,7 +175,7 @@ typedef enum
 
 #define PRICE_TYPE_LAST "last"
 #define PRICE_TYPE_UNK "unknown"
-
+#define PRICE_TYPE_TRN "transaction"
 /* ------------------ */
 /** @name Constructors
     @{ */
diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index a07a8ba..926af38 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -121,6 +121,8 @@ struct _xferDialog
      * creating a transaction)
      */
     gnc_numeric *exch_rate;
+    PriceSource price_source;
+    const char *price_type;
 
     /* Callback function to notify of the newly created Transaction */
     gnc_xfer_dialog_cb transaction_cb;
@@ -1057,6 +1059,9 @@ gnc_xfer_price_update_cb(GtkWidget *widget, GdkEventFocus *event,
     XferDialog *xferData = data;
 
     gnc_xfer_update_to_amount (xferData);
+    xferData->price_source = PRICE_SOURCE_USER_PRICE;
+    xferData->price_type = PRICE_TYPE_TRN;
+
 
     return FALSE;
 }
@@ -1086,6 +1091,8 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
                               price_value);
     gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
                                price_value);
+    xferData->price_source = PRICE_SOURCE_XFER_DLG_VAL;
+    xferData->price_type = PRICE_TYPE_TRN;
     gnc_xfer_dialog_update_conv_info(xferData);
 
     return FALSE;
@@ -1634,9 +1641,9 @@ create_price(XferDialog *xferData, Timespec ts)
             gnc_price_unref (pr.price);
             return;
         }
-        if (gnc_price_get_source(pr.price) == PRICE_SOURCE_FQ)
+        if (gnc_price_get_source(pr.price) < xferData->price_source)
         {
-            PINFO("Existing price is from Finance::Quote, so won't supersede.");
+            PINFO("Existing price is preferred, so won't supersede.");
             gnc_price_unref (pr.price);
             return;
         }
@@ -1649,7 +1656,8 @@ create_price(XferDialog *xferData, Timespec ts)
         value = round_price(pr.from, pr.to, value);
         gnc_price_begin_edit (pr.price);
         gnc_price_set_time (pr.price, ts);
-        gnc_price_set_source (pr.price, PRICE_SOURCE_XFER_DLG);
+        gnc_price_set_source (pr.price, xferData->price_source);
+        gnc_price_set_typestr(pr.price, xferData->price_type);
         gnc_price_set_value (pr.price, value);
         gnc_price_commit_edit (pr.price);
         PINFO("Modified price: 1 %s = %f %s",
@@ -1682,7 +1690,8 @@ create_price(XferDialog *xferData, Timespec ts)
     gnc_price_set_commodity (price, from);
     gnc_price_set_currency (price, to);
     gnc_price_set_time (price, ts);
-    gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
+    gnc_price_set_source (price, xferData->price_source);
+    gnc_price_set_typestr (price, xferData->price_type);
     gnc_price_set_value (price, value);
     gnc_pricedb_add_price (xferData->pricedb, price);
     gnc_price_commit_edit (price);
diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index 1332bf1..3e4eb74 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -2044,7 +2044,8 @@ recalculate_value (Split *split, SplitRegister *reg,
 }
 
 static void
-record_price (SplitRegister *reg, Account *account, gnc_numeric value)
+record_price (SplitRegister *reg, Account *account, gnc_numeric value,
+              PriceSource source)
 {
     Transaction *trans = gnc_split_register_get_current_trans (reg);
     QofBook *book = qof_instance_get_book (QOF_INSTANCE (account));
@@ -2084,8 +2085,9 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
             gnc_price_unref (price);
             return;
         }
-        if (gnc_price_get_source(price) == PRICE_SOURCE_FQ)
+        if (gnc_price_get_source(price) < PRICE_SOURCE_XFER_DLG_VAL)
         {
+            /* Existing price is preferred over this one. */
             gnc_price_unref(price);
             return;
         }
@@ -2098,7 +2100,8 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
                                     GNC_HOW_RND_ROUND_HALF_UP);
         gnc_price_begin_edit (price);
         gnc_price_set_time (price, ts);
-        gnc_price_set_source (price, PRICE_SOURCE_SPLIT_REG);
+        gnc_price_set_source (price, source);
+        gnc_price_set_typestr (price, PRICE_TYPE_TRN);
         gnc_price_set_value (price, value);
         gnc_price_commit_edit (price);
         gnc_price_unref (price);
@@ -2112,7 +2115,8 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
     gnc_price_set_commodity (price, comm);
     gnc_price_set_currency (price, curr);
     gnc_price_set_time (price, ts);
-    gnc_price_set_source (price, PRICE_SOURCE_SPLIT_REG);
+    gnc_price_set_source (price, source);
+    gnc_price_set_typestr (price, PRICE_TYPE_TRN);
     gnc_price_set_value (price, value);
     gnc_pricedb_add_price (pricedb, price);
     gnc_price_commit_edit (price);
@@ -2135,6 +2139,7 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
     Account *account;
     int denom;
     int choice;
+    PriceSource source = PRICE_SOURCE_USER_PRICE;
 
     if (STOCK_REGISTER    != reg->type &&
         CURRENCY_REGISTER != reg->type &&
@@ -2283,6 +2288,7 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
     {
         recalculate_price (split, reg, value, amount);
         price_changed = TRUE;
+        source = PRICE_SOURCE_SPLIT_REG;
     }
     if (recalc_value)
         recalculate_value (split, reg, price, amount, shares_changed);
@@ -2293,7 +2299,7 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
                 PRIC_CELL);
         price = gnc_price_cell_get_value (cell);
 	if (gnc_numeric_positive_p(price))
-	    record_price (reg, account, price);
+	    record_price (reg, account, price, source);
     }
     return TRUE;
 }

commit 811a30db4a28bb8b62a945c744ef791cb1674e3b
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Sep 9 11:22:52 2015 -0700

    Fold separate call of gnc_pricedb_lookup_latest() into lookup_price.
    
    Requires a 3-state enum instead of a boolean for the second arg to lookup_price.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index bf3de11..a07a8ba 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -215,6 +215,13 @@ round_price(gnc_commodity *from, gnc_commodity *to, gnc_numeric value)
     return value;
 }
 
+typedef enum
+{
+    SAME_DAY,
+    NEAREST,
+    LATEST
+} PriceDate;
+
 typedef struct
 {
     GNCPrice *price;
@@ -239,7 +246,7 @@ price_request_from_xferData(PriceReq *pr, XferDialog *xd)
 }
 
 static gboolean
-lookup_price(PriceReq *pr, gboolean day_only)
+lookup_price(PriceReq *pr, PriceDate pd)
 {
     GNCPrice *prc = NULL;
     g_return_val_if_fail (pr != NULL, FALSE);
@@ -248,26 +255,37 @@ lookup_price(PriceReq *pr, gboolean day_only)
     g_return_val_if_fail (pr->to != NULL, FALSE);
 
     pr->reverse = FALSE;
-    if (day_only)
-    {
-        prc = gnc_pricedb_lookup_day (pr->pricedb, pr->from, pr->to, pr->ts);
-    if (!prc)
-    {
-            prc = gnc_pricedb_lookup_day (pr->pricedb, pr->to,
-                                          pr->from, pr->ts);
-            pr->reverse = TRUE;
-    }
-    }
-    else
+    switch (pd)
     {
-        prc = gnc_pricedb_lookup_nearest_in_time (pr->pricedb, pr->from,
-                                                  pr->to, pr->ts);
-    if (!prc)
-    {
-            prc = gnc_pricedb_lookup_nearest_in_time (pr->pricedb, pr->to,
-                                                      pr->from, pr->ts);
-            pr->reverse = TRUE;
-    }
+        default:
+        case SAME_DAY:
+            prc = gnc_pricedb_lookup_day (pr->pricedb, pr->from,
+                                          pr->to, pr->ts);
+            if (!prc)
+            {
+                prc = gnc_pricedb_lookup_day (pr->pricedb, pr->to,
+                                              pr->from, pr->ts);
+                pr->reverse = TRUE;
+            }
+        break;
+        case NEAREST:
+            prc = gnc_pricedb_lookup_nearest_in_time (pr->pricedb, pr->from,
+                                                      pr->to, pr->ts);
+            if (!prc)
+            {
+                prc = gnc_pricedb_lookup_nearest_in_time (pr->pricedb, pr->to,
+                                                          pr->from, pr->ts);
+                pr->reverse = TRUE;
+            }
+        break;
+        case LATEST:
+            prc = gnc_pricedb_lookup_latest (pr->pricedb, pr->from, pr->to);
+            if (!prc)
+            {
+                prc = gnc_pricedb_lookup_latest (pr->pricedb, pr->to, pr->from);
+                pr->reverse = TRUE;
+            }
+            break;
     }
     if (pr->reverse)
     {
@@ -304,8 +322,8 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
     if (!xferData->pricedb) return;
 
     price_request_from_xferData(&pr, xferData);
-    if (!lookup_price(&pr, TRUE))
-        if (!lookup_price(&pr, FALSE))
+    if (!lookup_price(&pr, SAME_DAY))
+        if (!lookup_price(&pr, NEAREST))
         return;
 
     /* grab the price from the pricedb */
@@ -1604,7 +1622,7 @@ create_price(XferDialog *xferData, Timespec ts)
  * shifts to be < 1. */
 
     price_request_from_xferData(&pr, xferData);
-    if (lookup_price(&pr, TRUE))
+    if (lookup_price(&pr, SAME_DAY))
     {
         price_value = gnc_price_get_value(pr.price);
         if (gnc_numeric_equal(pr.reverse ? gnc_numeric_invert(value) : value,
@@ -1809,14 +1827,11 @@ gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data)
 void
 gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
 {
-    gnc_numeric rate;
     GNCPrice *prc;
-    gnc_commodity *from = xferData->from_commodity;
-    gnc_commodity *to = xferData->to_commodity;
+    PriceReq pr;
     SCM quotes_func;
     SCM book_scm;
     SCM scm_window;
-    gboolean have_price = FALSE;
 
     g_return_if_fail (xferData);
 
@@ -1851,29 +1866,15 @@ gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
     gnc_unset_busy_cursor (NULL);
 
     /*the results should be in the price db now, but don't crash if not. */
-
-    prc = gnc_pricedb_lookup_latest(xferData->pricedb, from, to);
-    if (prc)
-    {
-        rate = gnc_price_get_value (prc);
-        gnc_xfer_dialog_set_price_edit(xferData, rate);
-        gnc_price_unref (prc);
-        have_price = TRUE;
-    }
-
-    /* Lets try reversing the commodities */
-    if(!have_price)
+    price_request_from_xferData(&pr, xferData);
+    if (lookup_price(&pr, LATEST))
     {
-        prc = gnc_pricedb_lookup_latest (xferData->pricedb, to, from);
-        if (prc)
-        {
-/* FIXME: We probably want to swap the result price's to and from, not invert the price. */
-            rate = gnc_numeric_invert(gnc_price_get_value (prc));
-            rate = round_price(from, to, rate);
-            gnc_xfer_dialog_set_price_edit(xferData, rate);
-            gnc_price_unref (prc);
-            have_price = TRUE;
-        }
+        gnc_numeric price_value = gnc_price_get_value(pr.price);
+        if (pr.reverse)
+            price_value = round_price(pr.from, pr.to,
+                                      gnc_numeric_invert(price_value));
+         gnc_xfer_dialog_set_price_edit(xferData, price_value);
+        gnc_price_unref (pr.price);
     }
 
     LEAVE("quote retrieved");

commit 70493537e9a5cc92f7ff1611b9846c8776891ea2
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Sep 3 16:22:14 2015 -0700

    Use an enum for internal representation of Price Sources.
    
    Strings are still used for storage and display.
    Purpose is to make multiple comparisons and conditional setting more
    convenient.

diff --git a/src/backend/sql/gnc-price-sql.c b/src/backend/sql/gnc-price-sql.c
index 8729c91..83c5f3d 100644
--- a/src/backend/sql/gnc-price-sql.c
+++ b/src/backend/sql/gnc-price-sql.c
@@ -203,7 +203,7 @@ write_price( GNCPrice* p, gpointer data )
     g_return_val_if_fail( p != NULL, FALSE );
     g_return_val_if_fail( data != NULL, FALSE );
 
-    if ( s->is_ok && strcmp(gnc_price_get_source(p), PRICE_SOURCE_INVOICE) != 0)
+    if ( s->is_ok && gnc_price_get_source(p) != PRICE_SOURCE_INVOICE)
     {
         s->is_ok = save_price( s->be, QOF_INSTANCE(p) );
     }
diff --git a/src/backend/xml/gnc-pricedb-xml-v2.c b/src/backend/xml/gnc-pricedb-xml-v2.c
index fad3ff4..710fa01 100644
--- a/src/backend/xml/gnc-pricedb-xml-v2.c
+++ b/src/backend/xml/gnc-pricedb-xml-v2.c
@@ -120,7 +120,7 @@ price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
     {
         char *text = dom_tree_to_text(sub_node);
         if (!text) return FALSE;
-        gnc_price_set_source(p, text);
+        gnc_price_set_source_string(p, text);
         g_free(text);
     }
     else if (g_strcmp0("price:type", (char*)sub_node->name) == 0)
@@ -440,7 +440,7 @@ gnc_price_to_dom_tree(const xmlChar *tag, GNCPrice *price)
     tmpnode = timespec_to_dom_tree("price:time", &timesp);
     if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
 
-    sourcestr = gnc_price_get_source(price);
+    sourcestr = gnc_price_get_source_string(price);
     if (sourcestr && (strlen(sourcestr) != 0))
     {
         tmpnode = text_to_dom_tree("price:source", sourcestr);
diff --git a/src/backend/xml/io-gncxml-v1.c b/src/backend/xml/io-gncxml-v1.c
index 099ccd6..2dff2d0 100644
--- a/src/backend/xml/io-gncxml-v1.c
+++ b/src/backend/xml/io-gncxml-v1.c
@@ -3112,7 +3112,7 @@ price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
     {
         char *text = dom_tree_to_text(sub_node);
         if (!text) return FALSE;
-        gnc_price_set_source(p, text);
+        gnc_price_set_source_string(p, text);
         g_free(text);
     }
     else if (g_strcmp0("price:type", (char*)sub_node->name) == 0)
diff --git a/src/engine/engine.i b/src/engine/engine.i
index bed289b..f11d309 100644
--- a/src/engine/engine.i
+++ b/src/engine/engine.i
@@ -354,7 +354,7 @@ KvpValue * kvp_frame_get_slot_path_gslist (KvpFrame *frame, GSList *key_path);
     SET_ENUM("TRANS-DATE-POSTED");
     SET_ENUM("TRANS-DESCRIPTION");
     SET_ENUM("TRANS-NUM");
-    
+
     SET_ENUM("KVP-OPTION-PATH");
 
     SET_ENUM("OPTION-SECTION-ACCOUNTS");
@@ -375,6 +375,15 @@ KvpValue * kvp_frame_get_slot_path_gslist (KvpFrame *frame, GSList *key_path);
     SET_ENUM("GNC-HOW-RND-ROUND");
     SET_ENUM("GNC-HOW-RND-NEVER");
 
+    SET_ENUM("PRICE-SOURCE-EDIT-DLG");
+    SET_ENUM("PRICE-SOURCE-FQ");
+    SET_ENUM("PRICE-SOURCE-USER-PRICE");
+    SET_ENUM("PRICE-SOURCE-XFER-DLG-VAL");
+    SET_ENUM("PRICE-SOURCE-SPLIT-REG");
+    SET_ENUM("PRICE-SOURCE-STOCK-SPLIT");
+    SET_ENUM("PRICE-SOURCE-INVOICE");
+    SET_ENUM("PRICE-SOURCE-INVALID");
+
 #undef SET_ENUM
   }
 
diff --git a/src/engine/gnc-pricedb-p.h b/src/engine/gnc-pricedb-p.h
index d93747b..8d867da 100644
--- a/src/engine/gnc-pricedb-p.h
+++ b/src/engine/gnc-pricedb-p.h
@@ -39,7 +39,7 @@ struct gnc_price_s
     gnc_commodity *commodity;
     gnc_commodity *currency;
     Timespec tmspec;
-    char *source;
+    PriceSource source;
     char *type;
     gnc_numeric value;
 
diff --git a/src/engine/gnc-pricedb.c b/src/engine/gnc-pricedb.c
index 81febf1..8cb841b 100644
--- a/src/engine/gnc-pricedb.c
+++ b/src/engine/gnc-pricedb.c
@@ -58,9 +58,28 @@ gnc_price_init(GNCPrice* price)
     price->refcount = 1;
     price->value = gnc_numeric_zero();
     price->type = NULL;
-    price->source = NULL;
+    price->source = PRICE_SOURCE_INVALID;
 }
 
+/* Array of char constants for converting price-source enums. Be sure to keep in
+ * sync with the enum values in gnc-pricedb.h The string user:price-editor is
+ * explicitly used by price_to_gui() in dialog-price-editor.c and the string
+ * Finance::Quote is explicitly used by fq-results->commod-tz-quote-triples in
+ * price-quotes.scm. Take care to keep them in sync if you make changes. Beware
+ * that the strings are used to store the enum values in the backends so any
+ * changes will affect backward data compatibility.
+ */
+static const char* source_names[] =
+{
+    "user:price-editor", //sync with price_to_gui in dialog-price-editor.c
+    "Finance::Quote",    //sync with
+    "user:xfer-dialog",
+    "user:split-register",
+    "user:stock-split",
+    "user:invoice-post",
+    "invalid"
+};
+
 static void
 gnc_price_dispose(GObject *pricep)
 {
@@ -90,7 +109,7 @@ gnc_price_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec
     switch (prop_id)
     {
     case PROP_SOURCE:
-        g_value_set_string(value, price->source);
+        g_value_set_string(value, gnc_price_get_source_string(price));
         break;
     case PROP_TYPE:
         g_value_set_string(value, price->type);
@@ -126,7 +145,7 @@ gnc_price_set_property(GObject* object, guint prop_id, const GValue* value, GPar
     switch (prop_id)
     {
     case PROP_SOURCE:
-        gnc_price_set_source(price, g_value_get_string(value));
+        gnc_price_set_source_string(price, g_value_get_string(value));
         break;
     case PROP_TYPE:
         gnc_price_set_typestr(price, g_value_get_string(value));
@@ -188,10 +207,10 @@ gnc_price_class_init(GNCPriceClass *klass)
      PROP_SOURCE,
      g_param_spec_string ("source",
                           "Price source",
-                          "The price source is a string describing the "
-                          "source of a price quote.  It will be something "
-                          "like this: 'Finance::Quote', 'user:misc', "
-                          "'user:foo', etc.",
+                          "The price source is PriceSource enum describing how"
+                          " the price was created. This property works on the"
+                          " string values in source_names for SQL database"
+                          " compatibility.",
                           NULL,
                           G_PARAM_READWRITE));
 
@@ -252,7 +271,6 @@ gnc_price_destroy (GNCPrice *p)
     qof_event_gen (&p->inst, QOF_EVENT_DESTROY, NULL);
 
     if (p->type) CACHE_REMOVE(p->type);
-    if (p->source) CACHE_REMOVE(p->source);
 
     /* qof_instance_release (&p->inst); */
     g_object_unref(p);
@@ -439,23 +457,30 @@ gnc_price_set_time(GNCPrice *p, Timespec t)
 }
 
 void
-gnc_price_set_source(GNCPrice *p, const char *s)
+gnc_price_set_source(GNCPrice *p, PriceSource s)
 {
     if (!p) return;
-    if (g_strcmp0(p->source, s) != 0)
-    {
-        char *tmp;
-
-        gnc_price_begin_edit (p);
-        tmp = CACHE_INSERT((gpointer) s);
-        if (p->source) CACHE_REMOVE(p->source);
-        p->source = tmp;
-        gnc_price_set_dirty(p);
-        gnc_price_commit_edit (p);
-    }
+    gnc_price_begin_edit (p);
+    p->source = s;
+    gnc_price_set_dirty(p);
+    gnc_price_commit_edit(p);
 }
 
 void
+gnc_price_set_source_string(GNCPrice *p, const char* str)
+{
+    if (!p) return;
+    for (PriceSource s = PRICE_SOURCE_EDIT_DLG;
+         s < PRICE_SOURCE_INVALID; ++s)
+        if (strcmp(source_names[s], str) == 0)
+        {
+            gnc_price_set_source(p, s);
+            return;
+        }
+
+
+}
+void
 gnc_price_set_typestr(GNCPrice *p, const char* type)
 {
     if (!p) return;
@@ -518,13 +543,20 @@ gnc_price_get_time(const GNCPrice *p)
     return p->tmspec;
 }
 
-const char *
+PriceSource
 gnc_price_get_source(const GNCPrice *p)
 {
-    if (!p) return NULL;
+    if (!p) return PRICE_SOURCE_INVALID;
     return p->source;
 }
 
+const char*
+gnc_price_get_source_string(const GNCPrice *p)
+{
+    if (!p) return NULL;
+    return source_names[p->source];
+}
+
 const char *
 gnc_price_get_typestr(const GNCPrice *p)
 {
@@ -573,8 +605,7 @@ gnc_price_equal (const GNCPrice *p1, const GNCPrice *p2)
     if (!timespec_equal (&ts1, &ts2))
         return FALSE;
 
-    if (g_strcmp0 (gnc_price_get_source (p1),
-                   gnc_price_get_source (p2)) != 0)
+    if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
         return FALSE;
 
     if (g_strcmp0 (gnc_price_get_typestr (p1),
@@ -973,11 +1004,11 @@ insert_or_replace_price(GNCPriceDB *db, GNCPrice *p)
                                                   p->currency, p->tmspec);
     if (old_price == NULL)
         return TRUE;
-    if (strcmp(p->source, "PRICE_SOURCE_FQ"))
+    if (p->source != PRICE_SOURCE_FQ)
         return TRUE;
     return FALSE;
-}
 
+}
 /* ==================================================================== */
 /* The add_price() function is a utility that only manages the
  * dual hash table instertion */
@@ -1210,7 +1241,7 @@ static gboolean
 check_one_price_date (GNCPrice *price, gpointer user_data)
 {
     remove_info *data = user_data;
-    const gchar *source;
+    PriceSource source;
     Timespec pt;
 
     ENTER("price %p (%s), data %p", price,
@@ -1219,7 +1250,7 @@ check_one_price_date (GNCPrice *price, gpointer user_data)
     if (!data->delete_user)
     {
         source = gnc_price_get_source (price);
-        if (g_strcmp0(source, "Finance::Quote") != 0)
+        if (source != PRICE_SOURCE_FQ)
         {
             LEAVE("Not an automatic quote");
             return TRUE;
@@ -2472,8 +2503,8 @@ gnc_price_print(GNCPrice *p, FILE *f, int indent)
     str = str ? str : "(null)";
     fprintf(f, "%s    <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
     fprintf(f, "%s  </pdb:currency>\n", istr);
-    str = gnc_price_get_source(p);
-    str = str ? str : "(null)";
+    str = source_names[gnc_price_get_source(p)];
+    str = str ? str : "invalid";
     fprintf(f, "%s  %s\n", istr, str);
     str = gnc_price_get_typestr(p);
     str = str ? str : "(null)";
diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h
index ae65748..00a4126 100644
--- a/src/engine/gnc-pricedb.h
+++ b/src/engine/gnc-pricedb.h
@@ -156,6 +156,25 @@ GType gnc_pricedb_get_type(void);
 typedef struct gnc_price_lookup_s GNCPriceLookup;
 typedef GList PriceList;
 
+/** Price source enum. Be sure to keep in sync with the source_name array in
+ * gnc-pricedb.c. These are in preference order, so for example a quote with
+ * PRICE_SOURCE_EDIT_DLG will overwrite one with PRICE_SOURCE_FQ but not the
+ * other way around.
+ */
+typedef enum
+{
+    PRICE_SOURCE_EDIT_DLG,         // "user:price-editor"
+    PRICE_SOURCE_FQ,               // "Finance::Quote"
+    PRICE_SOURCE_XFER_DLG,         // "user:xfer-dialog"
+    PRICE_SOURCE_SPLIT_REG,        // "user:split-register"
+    PRICE_SOURCE_STOCK_SPLIT,      // "user:stock-split"
+    PRICE_SOURCE_INVOICE,          // "user:invoice-post"
+    PRICE_SOURCE_INVALID,
+} PriceSource;
+
+#define PRICE_TYPE_LAST "last"
+#define PRICE_TYPE_UNK "unknown"
+
 /* ------------------ */
 /** @name Constructors
     @{ */
@@ -202,33 +221,26 @@ void gnc_price_commit_edit (GNCPrice *p);
 void gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c);
 void gnc_price_set_currency(GNCPrice *p, gnc_commodity *c);
 void gnc_price_set_time(GNCPrice *p, Timespec t);
-void gnc_price_set_source(GNCPrice *p, const char *source);
+void gnc_price_set_source(GNCPrice *p, PriceSource source);
+void gnc_price_set_source_string(GNCPrice *p, const char* s);
 void gnc_price_set_typestr(GNCPrice *p, const char* type);
 void gnc_price_set_value(GNCPrice *p, gnc_numeric value);
 /** @} */
 
-#define PRICE_SOURCE_FQ  "Finance::Quote"
-#define PRICE_SOURCE_INVOICE "user:invoice-post"
-#define PRICE_SOURCE_STOCK_SPLIT "user:stock-split"
-#define PRICE_SOURCE_XFER_DLG "user:xfer-dialog"
-#define PRICE_SOURCE_SPLIT_REG "user:split-register"
-#define PRICE_SOURCE_EDIT_DLG "user:price-editor"
-#define PRICE_TYPE_LAST "last"
-#define PRICE_TYPE_UNK "unknown"
-
 /* ------------------ */
 /** @name  Getters
     All of the getters return data that's internal
     to the GNCPrice, not copies, so don't free these values.
     @{ */
 
-GNCPrice *      gnc_price_lookup (const GncGUID *guid, QofBook *book);
+    GNCPrice *      gnc_price_lookup (const GncGUID *guid, QofBook *book);
 /*@ dependent @*/
 gnc_commodity * gnc_price_get_commodity(const GNCPrice *p);
 /*@ dependent @*/
 gnc_commodity * gnc_price_get_currency(const GNCPrice *p);
 Timespec        gnc_price_get_time(const GNCPrice *p);
-const char *    gnc_price_get_source(const GNCPrice *p);
+PriceSource     gnc_price_get_source(const GNCPrice *p);
+const char *    gnc_price_get_source_string(const GNCPrice *p);
 const char *    gnc_price_get_typestr(const GNCPrice *p);
 gnc_numeric     gnc_price_get_value(const GNCPrice *p);
 gboolean        gnc_price_equal(const GNCPrice *p1, const GNCPrice *p2);
diff --git a/src/engine/test-core/test-engine-stuff.c b/src/engine/test-core/test-engine-stuff.c
index fa63449..1415b6b 100644
--- a/src/engine/test-core/test-engine-stuff.c
+++ b/src/engine/test-core/test-engine-stuff.c
@@ -676,6 +676,7 @@ void
 make_random_changes_to_price (QofBook *book, GNCPrice *p)
 {
     Timespec *ts;
+    PriceSource ps;
     char *string;
     gnc_commodity *c;
 
@@ -693,9 +694,9 @@ make_random_changes_to_price (QofBook *book, GNCPrice *p)
     gnc_price_set_time (p, *ts);
     g_free (ts);
 
-    string = get_random_string ();
-    gnc_price_set_source (p, string);
-    g_free (string);
+    ps = (PriceSource)get_random_int_in_range((int)PRICE_SOURCE_EDIT_DLG,
+                                              (int)PRICE_SOURCE_INVALID);
+    gnc_price_set_source (p, ps);
 
     string = get_random_string ();
     gnc_price_set_typestr (p, string);
diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 58b2c63..bf3de11 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -1616,7 +1616,7 @@ create_price(XferDialog *xferData, Timespec ts)
             gnc_price_unref (pr.price);
             return;
         }
-        if (strcmp (gnc_price_get_source(pr.price), PRICE_SOURCE_FQ) == 0)
+        if (gnc_price_get_source(pr.price) == PRICE_SOURCE_FQ)
         {
             PINFO("Existing price is from Finance::Quote, so won't supersede.");
             gnc_price_unref (pr.price);
diff --git a/src/gnome-utils/gnc-tree-model-price.c b/src/gnome-utils/gnc-tree-model-price.c
index c62b749..20a4409 100644
--- a/src/gnome-utils/gnc-tree-model-price.c
+++ b/src/gnome-utils/gnc-tree-model-price.c
@@ -786,7 +786,7 @@ gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
         break;
     case GNC_TREE_MODEL_PRICE_COL_SOURCE:
         g_value_init (value, G_TYPE_STRING);
-        g_value_set_string (value, gettext (gnc_price_get_source (price)));
+        g_value_set_string (value, gettext (gnc_price_get_source_string (price)));
         break;
     case GNC_TREE_MODEL_PRICE_COL_TYPE:
         g_value_init (value, G_TYPE_STRING);
diff --git a/src/gnome-utils/gnc-tree-view-price.c b/src/gnome-utils/gnc-tree-view-price.c
index 7f3113d..b0dc599 100644
--- a/src/gnome-utils/gnc-tree-view-price.c
+++ b/src/gnome-utils/gnc-tree-view-price.c
@@ -300,8 +300,7 @@ sort_by_source (GtkTreeModel *f_model,
         return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
 
     /* sort by source first */
-    result = safe_utf8_collate (gnc_price_get_source (price_a),
-                                gnc_price_get_source (price_b));
+    result = gnc_price_get_source (price_a) < gnc_price_get_source (price_b);
     if (result != 0)
         return result;
 
diff --git a/src/gnome/dialog-price-editor.c b/src/gnome/dialog-price-editor.c
index c7c5b9e..d395903 100644
--- a/src/gnome/dialog-price-editor.c
+++ b/src/gnome/dialog-price-editor.c
@@ -161,7 +161,7 @@ price_to_gui (PriceEditDialog *pedit_dialog)
 
         currency = gnc_price_get_currency (pedit_dialog->price);
         date = gnc_price_get_time (pedit_dialog->price);
-        source = gnc_price_get_source (pedit_dialog->price);
+        source = gnc_price_get_source_string (pedit_dialog->price);
         type = gnc_price_get_typestr (pedit_dialog->price);
         value = gnc_price_get_value (pedit_dialog->price);
     }
@@ -170,7 +170,7 @@ price_to_gui (PriceEditDialog *pedit_dialog)
         currency = gnc_default_currency ();
         date.tv_sec = gnc_time (NULL);
         date.tv_nsec = 0;
-        source = PRICE_SOURCE_EDIT_DLG;
+        source = "user:price-editor"; //Sync with source_names in gnc-pricedb.c
         type = "";
         value = gnc_numeric_zero ();
     }
@@ -237,7 +237,7 @@ gui_to_price (PriceEditDialog *pedit_dialog)
     gnc_price_set_commodity (pedit_dialog->price, commodity);
     gnc_price_set_currency (pedit_dialog->price, currency);
     gnc_price_set_time (pedit_dialog->price, date);
-    gnc_price_set_source (pedit_dialog->price, source);
+    gnc_price_set_source_string (pedit_dialog->price, source);
     gnc_price_set_typestr (pedit_dialog->price, type);
     gnc_price_set_value (pedit_dialog->price, value);
     gnc_price_commit_edit (pedit_dialog->price);
diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index 1b5dd47..1332bf1 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -2084,7 +2084,7 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
             gnc_price_unref (price);
             return;
         }
-        if (strcmp (gnc_price_get_source(price), PRICE_SOURCE_FQ) == 0)
+        if (gnc_price_get_source(price) == PRICE_SOURCE_FQ)
         {
             gnc_price_unref(price);
             return;
diff --git a/src/scm/price-quotes.scm b/src/scm/price-quotes.scm
index c6a2405..b597a94 100644
--- a/src/scm/price-quotes.scm
+++ b/src/scm/price-quotes.scm
@@ -415,7 +415,7 @@
               (begin
                 (gnc-price-begin-edit saved-price)
                 (gnc-price-set-time saved-price gnc-time)
-                (gnc-price-set-source saved-price "Finance::Quote")
+                      (gnc-price-set-source saved-price PRICE-SOURCE-FQ)
                 (gnc-price-set-typestr saved-price price-type)
                 (gnc-price-set-value saved-price price)
                 (gnc-price-commit-edit saved-price)
@@ -429,7 +429,7 @@
                       (gnc-price-set-commodity gnc-price commodity)
                       (gnc-price-set-currency gnc-price currency)
                       (gnc-price-set-time gnc-price gnc-time)
-                      (gnc-price-set-source gnc-price "Finance::Quote")
+                      (gnc-price-set-source gnc-price PRICE-SOURCE-FQ)
                       (gnc-price-set-typestr gnc-price price-type)
                       (gnc-price-set-value gnc-price price)
                       (gnc-price-commit-edit gnc-price)

commit ab15ca8f8458480ebbc8d68f73eeb335ed959c25
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Sep 3 11:22:51 2015 -0700

    Extract function lookup_price in dialog_transfer.c

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index b36fdc0..58b2c63 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -215,73 +215,107 @@ round_price(gnc_commodity *from, gnc_commodity *to, gnc_numeric value)
     return value;
 }
 
-/* (maybe) update the price from the pricedb. */
-static void
-gnc_xfer_dialog_update_price (XferDialog *xferData)
+typedef struct
 {
-    GNCPrice *prc;
-    gnc_numeric price_value;
-    Timespec date;
-    gnc_commodity *from = xferData->from_commodity;
-    gnc_commodity *to = xferData->to_commodity;
+    GNCPrice *price;
+    GNCPriceDB *pricedb;
+    gnc_commodity *from;
+    gnc_commodity *to;
+    Timespec ts;
     gboolean reverse;
+} PriceReq;
 
-    if (!xferData) return;
-    if (!xferData->from_commodity || ! xferData->to_commodity) return;
-    if (gnc_commodity_equal (xferData->from_commodity, xferData->to_commodity))
-        return;
-    if (!xferData->pricedb) return;
-
-    /* when do we update, and when do we NOT update? */
-
-    /* XXX: I'm ALWAYS going to update whenever we get called */
-
-    /* grab the price on the same day or nearest to the DATE out of the pricedb */
-    date = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (xferData->date_entry));
+static void
+price_request_from_xferData(PriceReq *pr, XferDialog *xd)
+{
+    g_return_if_fail (pr != NULL);
+    g_return_if_fail (xd != NULL);
+    pr->price = NULL;
+    pr->pricedb = xd->pricedb;
+    pr->from = xd->from_commodity;
+    pr->to = xd->to_commodity;
+    pr->ts = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (xd->date_entry));
+    pr->reverse = FALSE;
+}
 
-    /* Look for a price on the same day or, failing that, closest in time. */
-    prc = gnc_pricedb_lookup_day (xferData->pricedb, from, to, date);
-    reverse = FALSE;
+static gboolean
+lookup_price(PriceReq *pr, gboolean day_only)
+{
+    GNCPrice *prc = NULL;
+    g_return_val_if_fail (pr != NULL, FALSE);
+    g_return_val_if_fail (pr->pricedb != NULL, FALSE);
+    g_return_val_if_fail (pr->from != NULL, FALSE);
+    g_return_val_if_fail (pr->to != NULL, FALSE);
 
+    pr->reverse = FALSE;
+    if (day_only)
+    {
+        prc = gnc_pricedb_lookup_day (pr->pricedb, pr->from, pr->to, pr->ts);
     if (!prc)
     {
-        /* Look for reverse price on same day */
-        prc = gnc_pricedb_lookup_day (xferData->pricedb, to, from, date);
-        reverse = TRUE;
+            prc = gnc_pricedb_lookup_day (pr->pricedb, pr->to,
+                                          pr->from, pr->ts);
+            pr->reverse = TRUE;
     }
-
+    }
+    else
+    {
+        prc = gnc_pricedb_lookup_nearest_in_time (pr->pricedb, pr->from,
+                                                  pr->to, pr->ts);
     if (!prc)
     {
-        /* Didn't find one on the same day, look for nearest in time */
-        prc = gnc_pricedb_lookup_nearest_in_time (xferData->pricedb, from,
-                                                  to, date);
-        reverse = FALSE;
+            prc = gnc_pricedb_lookup_nearest_in_time (pr->pricedb, pr->to,
+                                                      pr->from, pr->ts);
+            pr->reverse = TRUE;
     }
+    }
+    if (pr->reverse)
+    {
+        PINFO("Found reverse price: 1 %s = %f %s",
+              gnc_commodity_get_mnemonic(pr->to),
+              gnc_numeric_to_double(gnc_price_get_value(prc)),
+              gnc_commodity_get_mnemonic(pr->from));
 
-    if (!prc)
+    }
+    else
     {
-        prc = gnc_pricedb_lookup_nearest_in_time (xferData->pricedb, to,
-                                                  from, date);
-        reverse = TRUE;
+        PINFO("Found price: 1 %s = %f %s",
+              gnc_commodity_get_mnemonic(pr->from),
+              gnc_numeric_to_double(gnc_price_get_value(prc)),
+              gnc_commodity_get_mnemonic(pr->to));
     }
-
     if (!prc)
+        return FALSE;
+    pr->price = prc;
+    return TRUE;
+}
+
+/* (maybe) update the price from the pricedb. */
+static void
+gnc_xfer_dialog_update_price (XferDialog *xferData)
+{
+    PriceReq pr;
+    gnc_numeric price_value;
+
+    if (!xferData) return;
+    if (!xferData->from_commodity || ! xferData->to_commodity) return;
+    if (gnc_commodity_equal (xferData->from_commodity, xferData->to_commodity))
+        return;
+    if (!xferData->pricedb) return;
+
+    price_request_from_xferData(&pr, xferData);
+    if (!lookup_price(&pr, TRUE))
+        if (!lookup_price(&pr, FALSE))
         return;
 
     /* grab the price from the pricedb */
-    price_value = gnc_price_get_value (prc);
-    if (reverse)
+    price_value = gnc_price_get_value (pr.price);
+    if (pr.reverse)
     {
-        PINFO("Found reverse price: 1 %s = %f %s", gnc_commodity_get_mnemonic(to),
-              gnc_numeric_to_double(price_value), gnc_commodity_get_mnemonic(from));
         price_value = gnc_numeric_invert (price_value);
-        price_value = round_price(from, to, price_value);
-    }
-    else
-    {
-        PINFO("Found price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
-              gnc_numeric_to_double(price_value), gnc_commodity_get_mnemonic(to));
+        price_value = round_price(pr.from, pr.to, price_value);
     }
+    gnc_price_unref(pr.price);
 
     /* and set the price entry */
     gnc_xfer_dialog_set_price_edit(xferData, price_value);
@@ -1542,9 +1576,10 @@ swap_commodities(gnc_commodity **from, gnc_commodity **to, gnc_numeric value)
 static void
 create_price(XferDialog *xferData, Timespec ts)
 {
+    PriceReq pr;
+    GNCPrice *price = NULL;
     gnc_commodity *from = xferData->from_commodity;
     gnc_commodity *to = xferData->to_commodity;
-    GNCPrice *price;
     gnc_numeric price_value;
     gnc_numeric value;
     gnc_numeric from_amt, to_amt;
@@ -1561,52 +1596,49 @@ create_price(XferDialog *xferData, Timespec ts)
          (strcmp (gnc_commodity_get_mnemonic(from),
                   gnc_commodity_get_mnemonic(to)) < 0)))
         swap_amount (&from, &to, &value, &from_amt, &to_amt);
-    /* First see if the closest entry on the same day has an equivalent rate */
-    price = gnc_pricedb_lookup_day (xferData->pricedb, from, to, ts);
-    if (!price)
-    {
-        price = gnc_pricedb_lookup_day (xferData->pricedb, to, from, ts);
-        if (price)
-            swap = TRUE;
-    }
+
 /* Normally we want to store currency rates such that the rate > 1 and commodity
  * prices in terms of a currency regardless of value. However, if we already
  * have a price in either direction we want to continue using that direction for
  * the rest of the day so that we don't wind up with two prices if the rate
  * shifts to be < 1. */
 
-    if (price)
+    price_request_from_xferData(&pr, xferData);
+    if (lookup_price(&pr, TRUE))
     {
-        price_value = gnc_price_get_value(price);
-        if (gnc_numeric_equal(swap ? gnc_numeric_invert(value) : value,
+        price_value = gnc_price_get_value(pr.price);
+        if (gnc_numeric_equal(pr.reverse ? gnc_numeric_invert(value) : value,
                               price_value))
         {
-            PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
-                  gnc_commodity_get_mnemonic(to));
-            gnc_price_unref (price);
+            PINFO("Found price for %s in %s",
+                  gnc_commodity_get_mnemonic(pr.from),
+                  gnc_commodity_get_mnemonic(pr.to));
+            gnc_price_unref (pr.price);
             return;
         }
-        if (strcmp (gnc_price_get_source(price), PRICE_SOURCE_FQ) == 0)
+        if (strcmp (gnc_price_get_source(pr.price), PRICE_SOURCE_FQ) == 0)
         {
             PINFO("Existing price is from Finance::Quote, so won't supersede.");
-            gnc_price_unref (price);
+            gnc_price_unref (pr.price);
             return;
         }
-        if (swap)
+        if (pr.reverse)
         {
             value = swap_commodities(&from, &to, value);
             xferData->from_commodity = from;
             xferData->to_commodity = to;
         }
-        value = round_price(from, to, value);
-        gnc_price_begin_edit (price);
-        gnc_price_set_time (price, ts);
-        gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
-        gnc_price_set_value (price, value);
-        gnc_price_commit_edit (price);
-        PINFO("Modified price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
-              gnc_numeric_to_double(value), gnc_commodity_get_mnemonic(to));
-        gnc_price_unref (price);
+        value = round_price(pr.from, pr.to, value);
+        gnc_price_begin_edit (pr.price);
+        gnc_price_set_time (pr.price, ts);
+        gnc_price_set_source (pr.price, PRICE_SOURCE_XFER_DLG);
+        gnc_price_set_value (pr.price, value);
+        gnc_price_commit_edit (pr.price);
+        PINFO("Modified price: 1 %s = %f %s",
+              gnc_commodity_get_mnemonic(from),
+              gnc_numeric_to_double(value),
+              gnc_commodity_get_mnemonic(to));
+        gnc_price_unref (pr.price);
         return;
     }
     if (gnc_commodity_is_currency(from) && gnc_commodity_is_currency(to))

commit 76c1259f1b3b22320df931f6a1d4abe850579f5a
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Sep 1 12:44:25 2015 -0700

    Adjust split_register to match transfer dialog checking inverted prices.

diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index 3cf90cc..1b5dd47 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -2056,6 +2056,7 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
     int scu = gnc_commodity_get_fraction(curr);
     Timespec ts;
     BasicCell *cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL);
+    gboolean swap = FALSE;
 
     /* Only record the price for account types that don't have a
      * "rate" cell. They'll get handled later by
@@ -2065,24 +2066,20 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
         return;
     gnc_date_cell_get_date ((DateCell*)cell, &ts);
     price = gnc_pricedb_lookup_day (pricedb, comm, curr, ts);
-    if (price)
-    {
-        price_value = gnc_price_get_value(price);
-    }
-    else
+    if (!price)
     {
         price = gnc_pricedb_lookup_day (pricedb, curr, comm, ts);
         if (price)
-        {
 /* It might be better to raise an error here: We shouldn't be creating
  * currency->commodity prices.
  */
-            price_value = gnc_numeric_invert(gnc_price_get_value(price));
-        }
+            swap = TRUE;
     }
     if (price)
     {
-        if (gnc_numeric_equal(value, price_value))
+        price_value = gnc_price_get_value(price);
+        if (gnc_numeric_equal(swap ? gnc_numeric_invert(value) : value,
+                              price_value))
         {
             gnc_price_unref (price);
             return;
@@ -2092,16 +2089,13 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
             gnc_price_unref(price);
             return;
         }
-        if (!gnc_numeric_eq(price_value, gnc_price_get_value(price)))
+        if (swap)
         {
             value = gnc_numeric_invert(value);
             scu = gnc_commodity_get_fraction(comm);
-            value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
-                                        GNC_HOW_RND_ROUND_HALF_UP);
         }
-        else
-            value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
-                                        GNC_HOW_RND_ROUND_HALF_UP);
+        value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                    GNC_HOW_RND_ROUND_HALF_UP);
         gnc_price_begin_edit (price);
         gnc_price_set_time (price, ts);
         gnc_price_set_source (price, PRICE_SOURCE_SPLIT_REG);

commit 348fe45b76ad4527a82726a287f0a76186f1b80a
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Sep 1 12:39:12 2015 -0700

    Fix missing initialization of price_value.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 15afccb..b36fdc0 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -1577,6 +1577,7 @@ create_price(XferDialog *xferData, Timespec ts)
 
     if (price)
     {
+        price_value = gnc_price_get_value(price);
         if (gnc_numeric_equal(swap ? gnc_numeric_invert(value) : value,
                               price_value))
         {

commit a40bc92d3410409470ae281c4cf013818c295f54
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Sep 1 12:17:12 2015 -0700

    Change CURRENCY_DENOM to 10000, matching what F::Q returns.

diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h
index c7fe6d0..ae65748 100644
--- a/src/engine/gnc-pricedb.h
+++ b/src/engine/gnc-pricedb.h
@@ -250,7 +250,7 @@ void gnc_price_print(GNCPrice *db, FILE *f, int indent);
  * of the commodity with a fixed denominator of the pricing currency's
  * SCU * 10000.
  */
-#define CURRENCY_DENOM 1000
+#define CURRENCY_DENOM 10000
 #define COMMODITY_DENOM_MULT 10000
 
 /* ================================================================ */

commit 6e1413771032ee2ad14244042edf320e0ad94d44
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Sep 1 12:16:09 2015 -0700

    Extract function round_price(), consistently apply it.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index c43d868..15afccb 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -189,6 +189,32 @@ gnc_xfer_dialog_compute_price_value (XferDialog *xferData)
     return(gnc_numeric_div(to_amt, from_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE));
 }
 
+/* Round a price value according to this policy:
+ * If both commodities are currencies, round to a fixed denominator.
+ * If only one is a currency, round to the currency's scu * a fixed factor.
+ * The fixed values are defined in gnc-pricedb.h
+ */
+static gnc_numeric
+round_price(gnc_commodity *from, gnc_commodity *to, gnc_numeric value)
+{
+    if (gnc_commodity_is_currency(from) && gnc_commodity_is_currency(to))
+        value = gnc_numeric_convert(value, CURRENCY_DENOM,
+                                    GNC_HOW_RND_ROUND_HALF_UP);
+    else if (gnc_commodity_is_currency(to))
+    {
+        int scu = gnc_commodity_get_fraction (to);
+        value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                    GNC_HOW_RND_ROUND_HALF_UP);
+    }
+    else if (gnc_commodity_is_currency(from))
+    {
+        int scu = gnc_commodity_get_fraction (from);
+        value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                    GNC_HOW_RND_ROUND_HALF_UP);
+    }
+    return value;
+}
+
 /* (maybe) update the price from the pricedb. */
 static void
 gnc_xfer_dialog_update_price (XferDialog *xferData)
@@ -248,7 +274,8 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
     {
         PINFO("Found reverse price: 1 %s = %f %s", gnc_commodity_get_mnemonic(to),
               gnc_numeric_to_double(price_value), gnc_commodity_get_mnemonic(from));
-        price_value = gnc_numeric_invert(price_value);
+        price_value = gnc_numeric_invert (price_value);
+        price_value = round_price(from, to, price_value);
     }
     else
     {
@@ -1000,17 +1027,11 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
     XferDialog *xferData = data;
     gnc_numeric price_value;
     Account *account;
-    int scu;
 
-    account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
-    if (account == NULL)
-        account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
-    scu = xaccAccountGetCommoditySCU(account);
     gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit));
-
     price_value = gnc_xfer_dialog_compute_price_value(xferData);
-    price_value = gnc_numeric_convert (price_value, scu * COMMODITY_DENOM_MULT,
-                                       GNC_HOW_RND_ROUND_HALF_UP);
+    price_value = round_price(xferData->from_commodity, xferData->to_commodity,
+                              price_value);
     gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
                                price_value);
     gnc_xfer_dialog_update_conv_info(xferData);
@@ -1496,24 +1517,26 @@ static void
 swap_amount (gnc_commodity **from, gnc_commodity **to, gnc_numeric *value,
              gnc_numeric *from_amt, gnc_numeric *to_amt)
 {
-    gnc_commodity *tmp;
-    gnc_numeric *tmp_amt;
-    tmp = *from;
+    gnc_commodity *tmp = *from;
+    gnc_numeric *tmp_amt = from_amt;
     *from = *to;
     *to = tmp;
-    tmp_amt = from_amt;
     from_amt = to_amt;
     to_amt = tmp_amt;
     *value = gnc_numeric_invert (*value);
+    *value = round_price(*from, *to, *value);
 }
 
 static gnc_numeric
 swap_commodities(gnc_commodity **from, gnc_commodity **to, gnc_numeric value)
 {
     gnc_commodity *tmp = *to;
+
     *to = *from;
     *from = tmp;
-    return gnc_numeric_invert(value);
+    value = gnc_numeric_invert(value);
+    value = round_price(*from, *to, value);
+    return value;
 }
 
 static void
@@ -1546,6 +1569,11 @@ create_price(XferDialog *xferData, Timespec ts)
         if (price)
             swap = TRUE;
     }
+/* Normally we want to store currency rates such that the rate > 1 and commodity
+ * prices in terms of a currency regardless of value. However, if we already
+ * have a price in either direction we want to continue using that direction for
+ * the rest of the day so that we don't wind up with two prices if the rate
+ * shifts to be < 1. */
 
     if (price)
     {
@@ -1566,16 +1594,10 @@ create_price(XferDialog *xferData, Timespec ts)
         if (swap)
         {
             value = swap_commodities(&from, &to, value);
+            xferData->from_commodity = from;
+            xferData->to_commodity = to;
         }
-        if (gnc_commodity_is_currency(from) && gnc_commodity_is_currency(to))
-            value = gnc_numeric_convert(value, CURRENCY_DENOM,
-                                        GNC_HOW_RND_ROUND_HALF_UP);
-        else if (gnc_commodity_is_currency(to))
-        {
-            int scu = gnc_commodity_get_fraction (to);
-            value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
-                                        GNC_HOW_RND_ROUND_HALF_UP);
-        }
+        value = round_price(from, to, value);
         gnc_price_begin_edit (price);
         gnc_price_set_time (price, ts);
         gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
@@ -1814,6 +1836,7 @@ gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
         {
 /* FIXME: We probably want to swap the result price's to and from, not invert the price. */
             rate = gnc_numeric_invert(gnc_price_get_value (prc));
+            rate = round_price(from, to, rate);
             gnc_xfer_dialog_set_price_edit(xferData, rate);
             gnc_price_unref (prc);
             have_price = TRUE;
@@ -2370,6 +2393,7 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
     gboolean swap_amounts = FALSE;
     gnc_commodity *txn_cur = xaccTransGetCurrency(txn);
     gnc_commodity *reg_com = xaccAccountGetCommodity(reg_acc);
+    gnc_numeric dialog_rate = *exch_rate;
 
     g_return_val_if_fail(txn_cur, TRUE);
 
@@ -2431,7 +2455,10 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
         gnc_xfer_dialog_select_to_currency(xfer, txn_cur);
         gnc_xfer_dialog_select_from_currency(xfer, xfer_com);
         if (!gnc_numeric_zero_p(*exch_rate))
-            *exch_rate = gnc_numeric_invert(*exch_rate);
+        {
+            dialog_rate = gnc_numeric_invert(*exch_rate);
+            dialog_rate = round_price(xfer_com, txn_cur, *exch_rate);
+        }
         amount = gnc_numeric_neg(amount);
     }
     else
@@ -2461,10 +2488,5 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
     if (gnc_xfer_dialog_run_until_done(xfer) == FALSE)
         return TRUE;
 
-    /* If we swapped the amounts for the dialog, then make sure we swap
-     * it back now...
-     */
-    if (swap_amounts)
-        *exch_rate = gnc_numeric_invert(*exch_rate);
     return FALSE;
 }

commit f30b38b5c3496e1e3846c9a1e1ee20841de15a2e
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 28 21:20:51 2015 +0100

    Create a rounding policy for prices in the pricedb.
    
    Currency-currency prices will be priced in the smaller currency so that
    the price > 1 and will be rounded to 3 digits after the decimal.
    Commodity-currency prices will be priced in the currency and rounded to
    the currency's scu * 10000.
    This affects only prices stored in the pricedb. Prices in splits will
    continue to be computed from value/amount.

diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h
index befff2d..c7fe6d0 100644
--- a/src/engine/gnc-pricedb.h
+++ b/src/engine/gnc-pricedb.h
@@ -243,6 +243,15 @@ gboolean        gnc_price_equal(const GNCPrice *p1, const GNCPrice *p2);
 /** This simple function can be useful for debugging the price code */
 void gnc_price_print(GNCPrice *db, FILE *f, int indent);
 /** @} */
+/** @name Denominator Constants Price policy: In order to avoid rounding
+ * problems, currency prices (often called exchange rates) are saved in terms of
+ * the smaller currency, so that price > 1, with a fixed denominator of
+ * 1/1000. Commodity prices in currency are always expressed as value per unit
+ * of the commodity with a fixed denominator of the pricing currency's
+ * SCU * 10000.
+ */
+#define CURRENCY_DENOM 1000
+#define COMMODITY_DENOM_MULT 10000
 
 /* ================================================================ */
 /** @name GNCPrice lists
diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 6233581..c43d868 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -55,8 +55,6 @@
 #define DIALOG_TRANSFER_CM_CLASS "dialog-transfer"
 #define GNC_PREFS_GROUP "dialogs.transfer"
 
-#define PRECISION 1000000
-
 typedef enum
 {
     XFER_DIALOG_FROM,
@@ -1002,15 +1000,16 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
     XferDialog *xferData = data;
     gnc_numeric price_value;
     Account *account;
+    int scu;
 
     account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
     if (account == NULL)
         account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
-
+    scu = xaccAccountGetCommoditySCU(account);
     gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit));
 
     price_value = gnc_xfer_dialog_compute_price_value(xferData);
-    price_value = gnc_numeric_convert (price_value, PRECISION,
+    price_value = gnc_numeric_convert (price_value, scu * COMMODITY_DENOM_MULT,
                                        GNC_HOW_RND_ROUND_HALF_UP);
     gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
                                price_value);
@@ -1507,6 +1506,16 @@ swap_amount (gnc_commodity **from, gnc_commodity **to, gnc_numeric *value,
     to_amt = tmp_amt;
     *value = gnc_numeric_invert (*value);
 }
+
+static gnc_numeric
+swap_commodities(gnc_commodity **from, gnc_commodity **to, gnc_numeric value)
+{
+    gnc_commodity *tmp = *to;
+    *to = *from;
+    *from = tmp;
+    return gnc_numeric_invert(value);
+}
+
 static void
 create_price(XferDialog *xferData, Timespec ts)
 {
@@ -1516,6 +1525,7 @@ create_price(XferDialog *xferData, Timespec ts)
     gnc_numeric price_value;
     gnc_numeric value;
     gnc_numeric from_amt, to_amt;
+    gboolean swap = FALSE;
 
 /* Bail in the unlikely event that both currencies have joined the Euro. */
     if (gnc_is_euro_currency (from) && gnc_is_euro_currency (to))
@@ -1530,21 +1540,17 @@ create_price(XferDialog *xferData, Timespec ts)
         swap_amount (&from, &to, &value, &from_amt, &to_amt);
     /* First see if the closest entry on the same day has an equivalent rate */
     price = gnc_pricedb_lookup_day (xferData->pricedb, from, to, ts);
-    if (price)
-    {
-        price_value = gnc_price_get_value(price);
-    }
-    else
+    if (!price)
     {
         price = gnc_pricedb_lookup_day (xferData->pricedb, to, from, ts);
         if (price)
-
-            price_value = gnc_numeric_invert(gnc_price_get_value(price));
+            swap = TRUE;
     }
 
     if (price)
     {
-        if (gnc_numeric_equal(value, price_value))
+        if (gnc_numeric_equal(swap ? gnc_numeric_invert(value) : value,
+                              price_value))
         {
             PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
                   gnc_commodity_get_mnemonic(to));
@@ -1557,10 +1563,18 @@ create_price(XferDialog *xferData, Timespec ts)
             gnc_price_unref (price);
             return;
         }
-        if (!gnc_numeric_eq(price_value, gnc_price_get_value(price)))
+        if (swap)
         {
-            value = gnc_numeric_invert(value);
-            value = gnc_numeric_convert(value, PRECISION, GNC_HOW_DENOM_REDUCE);
+            value = swap_commodities(&from, &to, value);
+        }
+        if (gnc_commodity_is_currency(from) && gnc_commodity_is_currency(to))
+            value = gnc_numeric_convert(value, CURRENCY_DENOM,
+                                        GNC_HOW_RND_ROUND_HALF_UP);
+        else if (gnc_commodity_is_currency(to))
+        {
+            int scu = gnc_commodity_get_fraction (to);
+            value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                        GNC_HOW_RND_ROUND_HALF_UP);
         }
         gnc_price_begin_edit (price);
         gnc_price_set_time (price, ts);
@@ -1572,6 +1586,24 @@ create_price(XferDialog *xferData, Timespec ts)
         gnc_price_unref (price);
         return;
     }
+    if (gnc_commodity_is_currency(from) && gnc_commodity_is_currency(to))
+    {
+        if (value.num < value.denom)
+        {
+            value = swap_commodities(&from, &to, value);
+        }
+        value = gnc_numeric_convert(value, CURRENCY_DENOM,
+                                    GNC_HOW_RND_ROUND_HALF_UP);
+    }
+    else if (gnc_commodity_is_currency(from) || gnc_commodity_is_currency(to))
+    {
+        int scu;
+        if (gnc_commodity_is_currency(from))
+            value = swap_commodities(&from, &to, value);
+        scu = gnc_commodity_get_fraction (to);
+        value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                    GNC_HOW_RND_ROUND_HALF_UP);
+    }
     price = gnc_price_create (xferData->book);
     gnc_price_begin_edit (price);
     gnc_price_set_commodity (price, from);
diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index aef5c6f..3cf90cc 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -64,9 +64,9 @@ static QofLogModule log_module = GNC_MOD_LEDGER;
 static CursorClass copied_class = CURSOR_CLASS_NONE;
 static SCM copied_item = SCM_UNDEFINED;
 static GncGUID copied_leader_guid;
-static const int PRECISION = 1000000;
-
-
+/* A denominator representing number of digits to the right of the decimal point
+ * displayed in a price cell. */
+static int PRICE_CELL_DENOM = 1000000;
 /** static prototypes *****************************************************/
 
 static gboolean gnc_split_register_save_to_scm (SplitRegister *reg,
@@ -2053,6 +2053,7 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
     gnc_commodity *curr = xaccTransGetCurrency (trans);
     GNCPrice *price;
     gnc_numeric price_value;
+    int scu = gnc_commodity_get_fraction(curr);
     Timespec ts;
     BasicCell *cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL);
 
@@ -2073,10 +2074,10 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
         price = gnc_pricedb_lookup_day (pricedb, curr, comm, ts);
         if (price)
         {
-            price_value = gnc_numeric_div (gnc_numeric_create(1, 1),
-                                           gnc_price_get_value(price),
-                                           GNC_DENOM_AUTO,
-                                           GNC_HOW_DENOM_REDUCE);
+/* It might be better to raise an error here: We shouldn't be creating
+ * currency->commodity prices.
+ */
+            price_value = gnc_numeric_invert(gnc_price_get_value(price));
         }
     }
     if (price)
@@ -2092,9 +2093,15 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
             return;
         }
         if (!gnc_numeric_eq(price_value, gnc_price_get_value(price)))
-            value = gnc_numeric_div (gnc_numeric_create(1, 1), value,
-                                           PRECISION, GNC_HOW_DENOM_REDUCE);
-
+        {
+            value = gnc_numeric_invert(value);
+            scu = gnc_commodity_get_fraction(comm);
+            value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                        GNC_HOW_RND_ROUND_HALF_UP);
+        }
+        else
+            value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                        GNC_HOW_RND_ROUND_HALF_UP);
         gnc_price_begin_edit (price);
         gnc_price_set_time (price, ts);
         gnc_price_set_source (price, PRICE_SOURCE_SPLIT_REG);
@@ -2104,6 +2111,8 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
         return;
     }
 
+    value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
+                                GNC_HOW_RND_ROUND_HALF_UP);
     price = gnc_price_create (book);
     gnc_price_begin_edit (price);
     gnc_price_set_commodity (price, comm);
@@ -2589,7 +2598,8 @@ gnc_split_register_config_cells (SplitRegister *reg)
     /* Use 6 decimal places for prices and "exchange rates"  */
     gnc_price_cell_set_fraction
     ((PriceCell *)
-     gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL), PRECISION);
+     gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL),
+     PRICE_CELL_DENOM);
 
     /* Initialize shares and share balance cells */
     gnc_price_cell_set_print_info

commit a8d4eaae5ecf663d550687b2cebf4a02dc5c8f56
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 28 19:25:35 2015 +0100

    Rename _gnc_xfer_dialog_set_exchange_rate and use it consistently.
    
    gnc_xfer_dialog_set_price_edit says what we're actually setting.

diff --git a/src/business/business-gnome/dialog-invoice.c b/src/business/business-gnome/dialog-invoice.c
index 10c4d75..0a24b69 100644
--- a/src/business/business-gnome/dialog-invoice.c
+++ b/src/business/business-gnome/dialog-invoice.c
@@ -878,7 +878,7 @@ gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params)
             {
                 exch_rate = gnc_numeric_div ((gnc_numeric){1, 1}, exch_rate,
                             GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
-                gnc_xfer_dialog_set_exchange_rate (xfer, exch_rate);
+                gnc_xfer_dialog_set_price_edit (xfer, exch_rate);
             }
         }
 
diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 0a97be7..6233581 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -259,7 +259,7 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
     }
 
     /* and set the price entry */
-    gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->price_edit), price_value);
+    gnc_xfer_dialog_set_price_edit(xferData, price_value);
 
     /* And then update the to_amount */
     gnc_xfer_update_to_amount (xferData);
@@ -313,9 +313,7 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
     if (!currency_active)
     {
         GtkEntry *entry;
-
-        gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
-                                   gnc_numeric_zero ());
+        gnc_xfer_dialog_set_price_edit(xferData, gnc_numeric_zero());
         entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
                           (GNC_AMOUNT_EDIT(xferData->price_edit)));
         gtk_entry_set_text(entry, "");
@@ -1338,16 +1336,16 @@ void gnc_xfer_dialog_set_date_sensitive(XferDialog *xferData,
 }
 
 void
-gnc_xfer_dialog_set_exchange_rate(XferDialog *xferData, gnc_numeric exchange_rate)
+gnc_xfer_dialog_set_price_edit(XferDialog *xferData, gnc_numeric price_value)
 {
     if (xferData == NULL)
         return;
 
-    if (gnc_numeric_zero_p (exchange_rate))
+    if (gnc_numeric_zero_p (price_value))
         return;
 
     gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->price_edit),
-                                exchange_rate);
+                                price_value);
 
     gnc_xfer_update_to_amount (xferData);
 }
@@ -1771,7 +1769,7 @@ gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
     if (prc)
     {
         rate = gnc_price_get_value (prc);
-        gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit), rate);
+        gnc_xfer_dialog_set_price_edit(xferData, rate);
         gnc_price_unref (prc);
         have_price = TRUE;
     }
@@ -1782,9 +1780,9 @@ gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
         prc = gnc_pricedb_lookup_latest (xferData->pricedb, to, from);
         if (prc)
         {
-            gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit), rate);
 /* FIXME: We probably want to swap the result price's to and from, not invert the price. */
             rate = gnc_numeric_invert(gnc_price_get_value (prc));
+            gnc_xfer_dialog_set_price_edit(xferData, rate);
             gnc_price_unref (prc);
             have_price = TRUE;
         }
@@ -2425,7 +2423,7 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
      */
 
     /* Set the exchange rate */
-    gnc_xfer_dialog_set_exchange_rate(xfer, *exch_rate);
+    gnc_xfer_dialog_set_price_edit(xfer, *exch_rate);
 
     /* and run it... */
     if (gnc_xfer_dialog_run_until_done(xfer) == FALSE)
@@ -2435,7 +2433,6 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
      * it back now...
      */
     if (swap_amounts)
-        *exch_rate = gnc_numeric_div(gnc_numeric_create(1, 1), *exch_rate,
-                                     GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+        *exch_rate = gnc_numeric_invert(*exch_rate);
     return FALSE;
 }
diff --git a/src/gnome-utils/dialog-transfer.h b/src/gnome-utils/dialog-transfer.h
index 7a8d6b3..4b4bc84 100644
--- a/src/gnome-utils/dialog-transfer.h
+++ b/src/gnome-utils/dialog-transfer.h
@@ -141,9 +141,11 @@ void gnc_xfer_dialog_set_date(XferDialog *xferData, time64 set_time);
 /** Set the "sensitive" state of the date field to the given value */
 void gnc_xfer_dialog_set_date_sensitive(XferDialog *xferData, gboolean is_sensitive);
 
-/** Set the exchange rate.  If exchange-rate is 0, then do nothing */
-void gnc_xfer_dialog_set_exchange_rate(XferDialog *xferData,
-                                       gnc_numeric exchange_rate);
+/** Set the dialog's exchange rate edit.  If price_value is 0, then do
+ *  nothing.
+ */
+void gnc_xfer_dialog_set_price_edit(XferDialog *xferData,
+				    gnc_numeric price_value);
 
 /** Indicate whether the dialog should quickfill based on the "To" account,
  * rather than the default which is the "From" account.

commit f79a3af4a2168f11f03d85c1295c050bfd931932
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 28 19:21:37 2015 +0100

    Provide gnc_numeric_invert() convenience function.
    
    Clearer and faster than dividing into 1/1.

diff --git a/src/app-utils/gnc-sx-instance-model.c b/src/app-utils/gnc-sx-instance-model.c
index 560fe79..cc7dbbf 100644
--- a/src/app-utils/gnc-sx-instance-model.c
+++ b/src/app-utils/gnc-sx-instance-model.c
@@ -1170,9 +1170,9 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data)
                   }
                   else
                   {
-                  exchange = gnc_numeric_div(gnc_numeric_create(1,1),
-                  gnc_price_get_value(price),
-                  1000, GNC_HOW_RND_ROUND_HALF_UP);
+                  exchange = gnc_numeric_invert(gnc_price_get_value(price));
+                  exchange = gnc_numeric_convert(exchange, 1000,
+                                                 GNC_HOW_RND_ROUND_HALF_UP);
                   }
                   }
                   else
@@ -1779,4 +1779,3 @@ GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_e
                                     result_map, NULL);
     return result_map;
 }
-
diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 174ff7a..0a97be7 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -250,8 +250,7 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
     {
         PINFO("Found reverse price: 1 %s = %f %s", gnc_commodity_get_mnemonic(to),
               gnc_numeric_to_double(price_value), gnc_commodity_get_mnemonic(from));
-        price_value = gnc_numeric_div (gnc_numeric_create (1, 1), price_value,
-                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+        price_value = gnc_numeric_invert(price_value);
     }
     else
     {
@@ -910,8 +909,7 @@ gnc_xfer_dialog_update_conv_info (XferDialog *xferData)
         gtk_label_set_text(GTK_LABEL(xferData->conv_forward), string);
         g_free(string);
 
-        rate = gnc_numeric_div(gnc_numeric_create (1, 1), rate,
-                               GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+        rate = gnc_numeric_invert(rate);
         string = g_strdup_printf("1 %s = %f %s", to_mnemonic,
                                  gnc_numeric_to_double(rate), from_mnemonic);
         gtk_label_set_text(GTK_LABEL(xferData->conv_reverse), string);
@@ -1509,8 +1507,7 @@ swap_amount (gnc_commodity **from, gnc_commodity **to, gnc_numeric *value,
     tmp_amt = from_amt;
     from_amt = to_amt;
     to_amt = tmp_amt;
-    *value = gnc_numeric_div (gnc_numeric_create(1, 1), *value,
-                              GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+    *value = gnc_numeric_invert (*value);
 }
 static void
 create_price(XferDialog *xferData, Timespec ts)
@@ -1543,12 +1540,8 @@ create_price(XferDialog *xferData, Timespec ts)
     {
         price = gnc_pricedb_lookup_day (xferData->pricedb, to, from, ts);
         if (price)
-        {
-            price_value = gnc_numeric_div (gnc_numeric_create(1, 1),
-                                           gnc_price_get_value(price),
-                                           GNC_DENOM_AUTO,
-                                           GNC_HOW_DENOM_REDUCE);
-        }
+
+            price_value = gnc_numeric_invert(gnc_price_get_value(price));
     }
 
     if (price)
@@ -1567,9 +1560,10 @@ create_price(XferDialog *xferData, Timespec ts)
             return;
         }
         if (!gnc_numeric_eq(price_value, gnc_price_get_value(price)))
-            value = gnc_numeric_div (gnc_numeric_create(1, 1), value,
-                                           PRECISION, GNC_HOW_DENOM_REDUCE);
-
+        {
+            value = gnc_numeric_invert(value);
+            value = gnc_numeric_convert(value, PRECISION, GNC_HOW_DENOM_REDUCE);
+        }
         gnc_price_begin_edit (price);
         gnc_price_set_time (price, ts);
         gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
@@ -1788,10 +1782,9 @@ gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
         prc = gnc_pricedb_lookup_latest (xferData->pricedb, to, from);
         if (prc)
         {
-            rate = gnc_numeric_div (gnc_numeric_create (1, 1), gnc_price_get_value (prc),
-                                    GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
-
             gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit), rate);
+/* FIXME: We probably want to swap the result price's to and from, not invert the price. */
+            rate = gnc_numeric_invert(gnc_price_get_value (prc));
             gnc_price_unref (prc);
             have_price = TRUE;
         }
@@ -2397,9 +2390,9 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
         gnc_numeric rate = xaccTransGetAccountConvRate(txn, reg_acc);
 
         /* XXX: should we tell the user we've done the conversion? */
-        amount = gnc_numeric_div(
-            amount, rate,
-            gnc_commodity_get_fraction(txn_cur), GNC_HOW_DENOM_REDUCE);
+        amount = gnc_numeric_div(amount, rate,
+                                 gnc_commodity_get_fraction(txn_cur),
+                                 GNC_HOW_DENOM_REDUCE);
     }
 
     /* enter the accounts */
@@ -2408,8 +2401,7 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
         gnc_xfer_dialog_select_to_currency(xfer, txn_cur);
         gnc_xfer_dialog_select_from_currency(xfer, xfer_com);
         if (!gnc_numeric_zero_p(*exch_rate))
-            *exch_rate = gnc_numeric_div(gnc_numeric_create(1, 1), *exch_rate,
-                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+            *exch_rate = gnc_numeric_invert(*exch_rate);
         amount = gnc_numeric_neg(amount);
     }
     else
diff --git a/src/libqof/qof/gnc-numeric.c b/src/libqof/qof/gnc-numeric.c
index 693fdf9..44c6400 100644
--- a/src/libqof/qof/gnc-numeric.c
+++ b/src/libqof/qof/gnc-numeric.c
@@ -1149,7 +1149,26 @@ gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
     return TRUE;
 }
 
-
+gnc_numeric
+gnc_numeric_invert(gnc_numeric num)
+{
+    if (num.num == 0)
+        return gnc_numeric_zero();
+    if (num.denom > 0)
+    {
+        if (num.num < 0)
+            return gnc_numeric_create (-num.denom, -num.num);
+        return gnc_numeric_create (num.denom, num.num);
+    }
+    else /* Negative denominator means multiply instead of divide. */
+    {
+        int64_t mult = (num.num < 0 ? INT64_C(-1) : INT64_C(1));
+        qofint128 denom = mult128(-num.denom, mult * num.num);
+        if (denom.hi)
+            return gnc_numeric_error(GNC_ERROR_OVERFLOW);
+        return gnc_numeric_create (mult, denom.lo);
+    }
+}
 /* *******************************************************************
  *  double_to_gnc_numeric
  ********************************************************************/
diff --git a/src/libqof/qof/gnc-numeric.h b/src/libqof/qof/gnc-numeric.h
index bcb70f1..89933c8 100644
--- a/src/libqof/qof/gnc-numeric.h
+++ b/src/libqof/qof/gnc-numeric.h
@@ -504,6 +504,13 @@ gnc_numeric gnc_numeric_reduce(gnc_numeric n);
  ********************************************************************/
 gboolean gnc_numeric_to_decimal(gnc_numeric * a,
                                 guint8 * max_decimal_places);
+
+/** Invert a gnc_numeric.
+ * Much faster than dividing 1 by it.
+ * @param num The number to be inverted
+ * @return a gnc_numeric that is the inverse of num
+ */
+gnc_numeric gnc_numeric_invert (gnc_numeric num);
 /** @} */
 
 /** @name GValue

commit be5b9f2b84a1ba51e8501e6aea2f0451ca833bb9
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 28 18:40:01 2015 +0100

    Use price_value when referring to a gnc_numeric.
    
    Price and prc are for gnc_price*.
    For clarity.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index dbe76b4..174ff7a 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -180,7 +180,7 @@ void gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data);
 /** Implementations **********************************************/
 
 static gnc_numeric
-gnc_xfer_dialog_compute_price (XferDialog *xferData)
+gnc_xfer_dialog_compute_price_value (XferDialog *xferData)
 {
     gnc_numeric from_amt, to_amt;
     g_return_val_if_fail (xferData != NULL, gnc_numeric_error (GNC_ERROR_ARG));
@@ -196,7 +196,7 @@ static void
 gnc_xfer_dialog_update_price (XferDialog *xferData)
 {
     GNCPrice *prc;
-    gnc_numeric price;
+    gnc_numeric price_value;
     Timespec date;
     gnc_commodity *from = xferData->from_commodity;
     gnc_commodity *to = xferData->to_commodity;
@@ -245,22 +245,22 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
         return;
 
     /* grab the price from the pricedb */
-    price = gnc_price_get_value (prc);
+    price_value = gnc_price_get_value (prc);
     if (reverse)
     {
         PINFO("Found reverse price: 1 %s = %f %s", gnc_commodity_get_mnemonic(to),
-              gnc_numeric_to_double(price), gnc_commodity_get_mnemonic(from));
-        price = gnc_numeric_div (gnc_numeric_create (1, 1), price,
+              gnc_numeric_to_double(price_value), gnc_commodity_get_mnemonic(from));
+        price_value = gnc_numeric_div (gnc_numeric_create (1, 1), price_value,
                                  GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
     }
     else
     {
         PINFO("Found price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
-              gnc_numeric_to_double(price), gnc_commodity_get_mnemonic(to));
+              gnc_numeric_to_double(price_value), gnc_commodity_get_mnemonic(to));
     }
 
     /* and set the price entry */
-    gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->price_edit), price);
+    gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->price_edit), price_value);
 
     /* And then update the to_amount */
     gnc_xfer_update_to_amount (xferData);
@@ -309,7 +309,7 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
 {
     gnc_numeric from_rate;
     gnc_numeric to_rate;
-    gnc_numeric price;
+    gnc_numeric price_value;
 
     if (!currency_active)
     {
@@ -339,9 +339,9 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
     if (gnc_numeric_zero_p (from_rate) || gnc_numeric_zero_p (to_rate))
         gnc_xfer_dialog_update_price (xferData);
 
-    price = gnc_numeric_div (to_rate, from_rate, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+    price_value = gnc_numeric_div (to_rate, from_rate, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
 
-    gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(xferData->price_edit), price);
+    gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(xferData->price_edit), price_value);
 
     gnc_xfer_update_to_amount (xferData);
 }
@@ -938,7 +938,7 @@ static void
 gnc_xfer_update_to_amount (XferDialog *xferData)
 {
     GNCAmountEdit *amount_edit, *price_edit, *to_amount_edit;
-    gnc_numeric price, to_amount;
+    gnc_numeric price_value, to_amount;
     Account *account;
     int scu = 0;
 
@@ -961,11 +961,11 @@ gnc_xfer_update_to_amount (XferDialog *xferData)
 
     /* Determine the amount to transfer. */
     if (!gnc_amount_edit_evaluate(price_edit) ||
-        gnc_numeric_zero_p(price = gnc_amount_edit_get_amount(price_edit)))
+        gnc_numeric_zero_p(price_value = gnc_amount_edit_get_amount(price_edit)))
         to_amount = gnc_numeric_zero();
     else
         to_amount = gnc_numeric_mul(gnc_amount_edit_get_amount(amount_edit),
-                                    price, scu, GNC_HOW_RND_ROUND_HALF_UP);
+                                    price_value, scu, GNC_HOW_RND_ROUND_HALF_UP);
 
     /* Update the dialog. */
     gnc_amount_edit_set_amount(to_amount_edit, to_amount);
@@ -1004,7 +1004,7 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
                              gpointer data)
 {
     XferDialog *xferData = data;
-    gnc_numeric price;
+    gnc_numeric price_value;
     Account *account;
 
     account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
@@ -1013,9 +1013,11 @@ gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
 
     gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit));
 
-    price = gnc_xfer_dialog_compute_price(xferData);
-    price = gnc_numeric_convert (price, PRECISION, GNC_HOW_RND_ROUND_HALF_UP);
-    gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit), price);
+    price_value = gnc_xfer_dialog_compute_price_value(xferData);
+    price_value = gnc_numeric_convert (price_value, PRECISION,
+                                       GNC_HOW_RND_ROUND_HALF_UP);
+    gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
+                               price_value);
     gnc_xfer_dialog_update_conv_info(xferData);
 
     return FALSE;
@@ -1657,7 +1659,7 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
 
     if (xferData->exch_rate)
     {
-        gnc_numeric price;
+        gnc_numeric price_value;
 
         /* If we've got the price-button set, then make sure we update the
          * to-amount before we use it.
@@ -1665,8 +1667,8 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(xferData->price_radio)))
             gnc_xfer_update_to_amount(xferData);
 
-        price = gnc_xfer_dialog_compute_price(xferData);
-        *(xferData->exch_rate) = gnc_numeric_abs(price);
+        price_value = gnc_xfer_dialog_compute_price_value(xferData);
+        *(xferData->exch_rate) = gnc_numeric_abs(price_value);
     }
     else
         create_transaction (xferData, &ts, from_account, to_account,

commit 9c2813acb66a2070e026d6e4d710aafcb2822adc
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 25 11:17:07 2015 +0100

    Price-quotes: Modify quotes on same day instead of creating new ones.
    
    We can use only one per day so no point in keeping a bunch of them.
    Finance::Quote prices always overwrite user prices.

diff --git a/src/scm/price-quotes.scm b/src/scm/price-quotes.scm
index 7daacd0..c6a2405 100644
--- a/src/scm/price-quotes.scm
+++ b/src/scm/price-quotes.scm
@@ -370,7 +370,11 @@
                  (string? currency-str)
                  (gnc-commodity-table-lookup commodity-table
                                              "ISO4217"
-                                             (string-upcase currency-str)))))
+                                             (string-upcase currency-str))))
+           (pricedb (gnc-pricedb-get-db book))
+           (saved-price #f)
+           (commodity-str (gnc-commodity-get-printname commodity))
+           )
 
       (or-map (lambda (price-sym)
                 (let ((p (assq-ref quote-data price-sym)))
@@ -403,27 +407,44 @@
       (if (not (and commodity currency gnc-time price price-type))
           (string-append
            currency-str ":" (gnc-commodity-get-mnemonic commodity))
-          (let ((gnc-price (gnc-price-create book)))
-            (if (not gnc-price)
-                (string-append
-                 currency-str ":" (gnc-commodity-get-mnemonic commodity))
-                (begin
-				  (gnc-price-begin-edit gnc-price)
-                  (gnc-price-set-commodity gnc-price commodity)
-                  (gnc-price-set-currency gnc-price currency)
-                  (gnc-price-set-time gnc-price gnc-time)
-                  (gnc-price-set-source gnc-price "Finance::Quote")
-                  (gnc-price-set-typestr gnc-price price-type)
-                  (gnc-price-set-value gnc-price price)
-				  (gnc-price-commit-edit gnc-price)
-                  gnc-price))))))
+          (begin
+            (set! saved-price (gnc-pricedb-lookup-day pricedb
+                                                      commodity currency
+                                                      gnc-time))
+            (if (not (null? saved-price))
+              (begin
+                (gnc-price-begin-edit saved-price)
+                (gnc-price-set-time saved-price gnc-time)
+                (gnc-price-set-source saved-price "Finance::Quote")
+                (gnc-price-set-typestr saved-price price-type)
+                (gnc-price-set-value saved-price price)
+                (gnc-price-commit-edit saved-price)
+                #f)
+              (let ((gnc-price (gnc-price-create book)))
+                (if (not gnc-price)
+                    (string-append
+                     currency-str ":" (gnc-commodity-get-mnemonic commodity))
+                    (begin
+                      (gnc-price-begin-edit gnc-price)
+                      (gnc-price-set-commodity gnc-price commodity)
+                      (gnc-price-set-currency gnc-price currency)
+                      (gnc-price-set-time gnc-price gnc-time)
+                      (gnc-price-set-source gnc-price "Finance::Quote")
+                      (gnc-price-set-typestr gnc-price price-type)
+                      (gnc-price-set-value gnc-price price)
+                      (gnc-price-commit-edit gnc-price)
+                      gnc-price)))))
+          )))
 
   (define (book-add-prices! book prices)
     (let ((pricedb (gnc-pricedb-get-db book)))
       (for-each
        (lambda (price)
-         (gnc-pricedb-add-price pricedb price)
-         (gnc-price-unref price))
+         (if price
+             (begin
+               (gnc-pricedb-add-price pricedb price)
+               (gnc-price-unref price)
+               #f)))
        prices)))
 
   ;; FIXME: uses of gnc:warn in here need to be cleaned up.  Right

commit 966789374d15838820a60de36d3d7b18f33828ce
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 23 12:17:23 2015 +0100

    Edit split-based prices instead of adding.
    
    For split-register and xfer-dialog generated prices if there's an existing
    non-FQ price for the day, change it. If there's an F::Q quote for the day,
    do nothing. Only add a price if there isn't one for the from/to
    combination.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index bacbeba..dbe76b4 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -86,7 +86,7 @@ struct _xferDialog
     gnc_commodity *to_commodity;
 
     QuickFill *qf;     /* Quickfill on transfer descriptions,
-                           defaults to matching on the "From" account. */
+                          defaults to matching on the "From" account. */
 
     XferDirection quickfill;    /* direction match on the account instead. */
 
@@ -1549,26 +1549,46 @@ create_price(XferDialog *xferData, Timespec ts)
         }
     }
 
-    if (price && gnc_numeric_equal(value, price_value))
-    {
-        PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
-              gnc_commodity_get_mnemonic(to));
-    }
-    else
+    if (price)
     {
-        price = gnc_price_create (xferData->book);
+        if (gnc_numeric_equal(value, price_value))
+        {
+            PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
+                  gnc_commodity_get_mnemonic(to));
+            gnc_price_unref (price);
+            return;
+        }
+        if (strcmp (gnc_price_get_source(price), PRICE_SOURCE_FQ) == 0)
+        {
+            PINFO("Existing price is from Finance::Quote, so won't supersede.");
+            gnc_price_unref (price);
+            return;
+        }
+        if (!gnc_numeric_eq(price_value, gnc_price_get_value(price)))
+            value = gnc_numeric_div (gnc_numeric_create(1, 1), value,
+                                           PRECISION, GNC_HOW_DENOM_REDUCE);
+
         gnc_price_begin_edit (price);
-        gnc_price_set_commodity (price, from);
-        gnc_price_set_currency (price, to);
         gnc_price_set_time (price, ts);
         gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
         gnc_price_set_value (price, value);
-        gnc_pricedb_add_price (xferData->pricedb, price);
         gnc_price_commit_edit (price);
-        PINFO("Created price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
+        PINFO("Modified price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
               gnc_numeric_to_double(value), gnc_commodity_get_mnemonic(to));
+        gnc_price_unref (price);
+        return;
     }
-
+    price = gnc_price_create (xferData->book);
+    gnc_price_begin_edit (price);
+    gnc_price_set_commodity (price, from);
+    gnc_price_set_currency (price, to);
+    gnc_price_set_time (price, ts);
+    gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
+    gnc_price_set_value (price, value);
+    gnc_pricedb_add_price (xferData->pricedb, price);
+    gnc_price_commit_edit (price);
+    PINFO("Created price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
+          gnc_numeric_to_double(value), gnc_commodity_get_mnemonic(to));
     gnc_price_unref (price);
 }
 
diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index 119a459..aef5c6f 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -64,6 +64,7 @@ static QofLogModule log_module = GNC_MOD_LEDGER;
 static CursorClass copied_class = CURSOR_CLASS_NONE;
 static SCM copied_item = SCM_UNDEFINED;
 static GncGUID copied_leader_guid;
+static const int PRECISION = 1000000;
 
 
 /** static prototypes *****************************************************/
@@ -2078,9 +2079,31 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
                                            GNC_HOW_DENOM_REDUCE);
         }
     }
-
-    if (price && gnc_numeric_equal(value, price_value))
+    if (price)
+    {
+        if (gnc_numeric_equal(value, price_value))
+        {
+            gnc_price_unref (price);
+            return;
+        }
+        if (strcmp (gnc_price_get_source(price), PRICE_SOURCE_FQ) == 0)
+        {
+            gnc_price_unref(price);
+            return;
+        }
+        if (!gnc_numeric_eq(price_value, gnc_price_get_value(price)))
+            value = gnc_numeric_div (gnc_numeric_create(1, 1), value,
+                                           PRECISION, GNC_HOW_DENOM_REDUCE);
+
+        gnc_price_begin_edit (price);
+        gnc_price_set_time (price, ts);
+        gnc_price_set_source (price, PRICE_SOURCE_SPLIT_REG);
+        gnc_price_set_value (price, value);
+        gnc_price_commit_edit (price);
+        gnc_price_unref (price);
         return;
+    }
+
     price = gnc_price_create (book);
     gnc_price_begin_edit (price);
     gnc_price_set_commodity (price, comm);
@@ -2566,7 +2589,7 @@ gnc_split_register_config_cells (SplitRegister *reg)
     /* Use 6 decimal places for prices and "exchange rates"  */
     gnc_price_cell_set_fraction
     ((PriceCell *)
-     gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL), 1000000);
+     gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL), PRECISION);
 
     /* Initialize shares and share balance cells */
     gnc_price_cell_set_print_info

commit 5e609dac0d2b4c1a7ec59dfff675c620fb734a95
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 23 11:44:24 2015 +0100

    Check for an existing price before adding one in split_reg.
    
    To make the behavior the same as in xfer_dialog.

diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index fc4f587..119a459 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -2051,8 +2051,9 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
     gnc_commodity *comm = xaccAccountGetCommodity (account);
     gnc_commodity *curr = xaccTransGetCurrency (trans);
     GNCPrice *price;
+    gnc_numeric price_value;
     Timespec ts;
-    BasicCell *cell;
+    BasicCell *cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL);
 
     /* Only record the price for account types that don't have a
      * "rate" cell. They'll get handled later by
@@ -2060,8 +2061,26 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
      */
     if (gnc_split_reg_has_rate_cell (reg->type))
         return;
-    cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL);
     gnc_date_cell_get_date ((DateCell*)cell, &ts);
+    price = gnc_pricedb_lookup_day (pricedb, comm, curr, ts);
+    if (price)
+    {
+        price_value = gnc_price_get_value(price);
+    }
+    else
+    {
+        price = gnc_pricedb_lookup_day (pricedb, curr, comm, ts);
+        if (price)
+        {
+            price_value = gnc_numeric_div (gnc_numeric_create(1, 1),
+                                           gnc_price_get_value(price),
+                                           GNC_DENOM_AUTO,
+                                           GNC_HOW_DENOM_REDUCE);
+        }
+    }
+
+    if (price && gnc_numeric_equal(value, price_value))
+        return;
     price = gnc_price_create (book);
     gnc_price_begin_edit (price);
     gnc_price_set_commodity (price, comm);

commit c4082524cbc943562d37145cd5b9b142f0bb2f9c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 23 11:06:14 2015 +0100

    In the transfer dialog use the price_edit value for the saved price.
    
    Instead of computing it separately and differently from to_amt and from_amt.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 087b2dd..bacbeba 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -1524,16 +1524,7 @@ create_price(XferDialog *xferData, Timespec ts)
     if (gnc_is_euro_currency (from) && gnc_is_euro_currency (to))
         return;
 
-    from_amt =
-        gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
-    to_amt =
-        gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->to_amount_edit));
-
-    /* compute the price -- maybe we need to swap? */
-    value = gnc_numeric_div(to_amt, from_amt, GNC_DENOM_AUTO,
-                            GNC_HOW_DENOM_REDUCE);
-    value = gnc_numeric_abs (value);
-
+    value = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->price_edit));
     /* Try to be consistent about how quotes are installed. */
     if (from == gnc_default_currency() ||
         ((to != gnc_default_currency()) &&
@@ -1558,20 +1549,7 @@ create_price(XferDialog *xferData, Timespec ts)
         }
     }
 
-    /* See if we found a good enough price */
-    if (price)
-    {
-        int scu = gnc_commodity_get_fraction (to);
-        if (!gnc_numeric_equal (gnc_numeric_mul (from_amt, price_value,
-                                                 scu, GNC_HOW_RND_ROUND_HALF_UP),
-                                to_amt))
-        {
-            gnc_price_unref (price);
-            price = NULL;
-        }
-    }
-
-    if (price)
+    if (price && gnc_numeric_equal(value, price_value))
     {
         PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
               gnc_commodity_get_mnemonic(to));

commit ca447fc0473269fa5f54c13c817d290ea0de275d
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Aug 22 10:48:24 2015 +0100

    Fix up whitespace in dialog-transfer.c.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index 50c1367..087b2dd 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -69,61 +69,60 @@ static QofLogModule log_module = GNC_MOD_GUI;
 
 struct _xferDialog
 {
-    GtkWidget * dialog;
-
-    GtkWidget * amount_edit;
-    GtkWidget * date_entry;
-    GtkWidget * num_entry;
-    GtkWidget * description_entry;
-    GtkWidget * memo_entry;
-    GtkWidget * conv_forward;
-    GtkWidget * conv_reverse;
-
-    GtkWidget *	from_window;
+    GtkWidget *dialog;
+    GtkWidget *amount_edit;
+    GtkWidget *date_entry;
+    GtkWidget *num_entry;
+    GtkWidget *description_entry;
+    GtkWidget *memo_entry;
+    GtkWidget *conv_forward;
+    GtkWidget *conv_reverse;
+
+    GtkWidget *from_window;
     GtkTreeView * from_tree_view;
-    gnc_commodity *	from_commodity;
-    GtkWidget *	to_window;
-    GtkTreeView * to_tree_view;
-    gnc_commodity *	to_commodity;
+    gnc_commodity *from_commodity;
+    GtkWidget *to_window;
+    GtkTreeView *to_tree_view;
+    gnc_commodity *to_commodity;
 
-    QuickFill * qf;     /* Quickfill on transfer descriptions,
-                         defaults to matching on the "From" account. */
+    QuickFill *qf;     /* Quickfill on transfer descriptions,
+                           defaults to matching on the "From" account. */
 
-    XferDirection quickfill;	/* direction match on the account instead. */
+    XferDirection quickfill;    /* direction match on the account instead. */
 
     /* stored data for the description quickfill selection function */
     gint desc_start_selection;
     gint desc_end_selection;
     guint desc_selection_source_id;
 
-    GtkWidget * transferinfo_label;
+    GtkWidget *transferinfo_label;
 
-    GtkWidget * from_transfer_label;
-    GtkWidget * to_transfer_label;
+    GtkWidget *from_transfer_label;
+    GtkWidget *to_transfer_label;
 
-    GtkWidget * from_currency_label;
-    GtkWidget * to_currency_label;
+    GtkWidget *from_currency_label;
+    GtkWidget *to_currency_label;
 
-    GtkWidget * from_show_button;
-    GtkWidget * to_show_button;
+    GtkWidget *from_show_button;
+    GtkWidget *to_show_button;
 
-    GtkWidget * curr_xfer_table;
+    GtkWidget *curr_xfer_table;
 
-    GtkWidget * price_edit;
-    GtkWidget * to_amount_edit;
+    GtkWidget *price_edit;
+    GtkWidget *to_amount_edit;
 
-    GtkWidget * price_radio;
-    GtkWidget * amount_radio;
+    GtkWidget *price_radio;
+    GtkWidget *amount_radio;
 
-    GtkWidget * fetch_button;
+    GtkWidget *fetch_button;
 
-    QofBook *	book;
-    GNCPriceDB *	pricedb;
+    QofBook *book;
+    GNCPriceDB *pricedb;
 
     /* Where to store the "exchange_rate" at exit (in lieu of
      * creating a transaction)
      */
-    gnc_numeric * exch_rate;
+    gnc_numeric *exch_rate;
 
     /* Callback function to notify of the newly created Transaction */
     gnc_xfer_dialog_cb transaction_cb;
@@ -157,10 +156,10 @@ static void gnc_xfer_update_to_amount (XferDialog *xferData);
 static void gnc_xfer_dialog_update_conv_info(XferDialog *xferData);
 
 static Account *gnc_transfer_dialog_get_selected_account (XferDialog *dialog,
-        XferDirection direction);
+                                                          XferDirection direction);
 static void gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
-        Account *account,
-        XferDirection direction);
+                                                      Account *account,
+                                                      XferDirection direction);
 
 void gnc_xfer_description_insert_cb(GtkEditable *editable,
                                     const gchar *insert_text,
@@ -168,11 +167,11 @@ void gnc_xfer_description_insert_cb(GtkEditable *editable,
                                     gint *start_pos,
                                     XferDialog *xferData);
 gboolean gnc_xfer_description_key_press_cb( GtkEntry *entry,
-        GdkEventKey *event,
-        XferDialog *xferData );
+                                            GdkEventKey *event,
+                                            XferDialog *xferData );
 void gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData);
 gboolean gnc_xfer_dialog_inc_exp_filter_func (Account *account,
-        gpointer data);
+                                              gpointer data);
 void price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data);
 
 void gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data);
@@ -231,14 +230,14 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
     {
         /* Didn't find one on the same day, look for nearest in time */
         prc = gnc_pricedb_lookup_nearest_in_time (xferData->pricedb, from,
-                to, date);
+                                                  to, date);
         reverse = FALSE;
     }
 
     if (!prc)
     {
         prc = gnc_pricedb_lookup_nearest_in_time (xferData->pricedb, to,
-                from, date);
+                                                  from, date);
         reverse = TRUE;
     }
 
@@ -328,7 +327,7 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
     }
 
     if (!gnc_is_euro_currency (from_currency) ||
-            !gnc_is_euro_currency (to_currency))
+        !gnc_is_euro_currency (to_currency))
     {
         gnc_xfer_dialog_update_price (xferData);
         return;
@@ -363,8 +362,8 @@ gnc_xfer_dialog_curr_acct_activate(XferDialog *xferData)
 
     curr_active = (xferData->exch_rate ||
                    ((from_account != NULL) && (to_account != NULL)))
-                  && !gnc_commodity_equiv(xferData->from_commodity,
-                                          xferData->to_commodity);
+        && !gnc_commodity_equiv(xferData->from_commodity,
+                                xferData->to_commodity);
 
     gtk_widget_set_sensitive(xferData->curr_xfer_table, curr_active);
     gtk_widget_set_sensitive(xferData->price_edit,
@@ -441,7 +440,7 @@ gnc_xfer_dialog_reload_quickfill( XferDialog *xferData )
 
 static void
 gnc_xfer_dialog_from_tree_selection_changed_cb (GtkTreeSelection *selection,
-        gpointer data)
+                                                gpointer data)
 {
     XferDialog *xferData = data;
     GNCPrintAmountInfo print_info;
@@ -541,7 +540,7 @@ gnc_xfer_dialog_fill_tree_view(XferDialog *xferData,
 
     g_return_if_fail (xferData != NULL);
     use_accounting_labels = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
-                            GNC_PREF_ACCOUNTING_LABELS);
+                                               GNC_PREF_ACCOUNTING_LABELS);
 
     /* In "normal" mode (non accounting terms) the account where the
      * money comes from is displayed on the left side and the account
@@ -557,20 +556,20 @@ gnc_xfer_dialog_fill_tree_view(XferDialog *xferData,
     if (use_accounting_labels)
     {
         button = GTK_WIDGET(gtk_builder_get_object (builder,
-                            (direction == XFER_DIALOG_TO) ?
-                            "left_show_button" : "right_show_button"));
+                                                    (direction == XFER_DIALOG_TO) ?
+                                                    "left_show_button" : "right_show_button"));
         scroll_win = GTK_WIDGET(gtk_builder_get_object (builder,
-                                (direction == XFER_DIALOG_TO) ?
-                                "left_trans_window" : "right_trans_window"));
+                                                        (direction == XFER_DIALOG_TO) ?
+                                                        "left_trans_window" : "right_trans_window"));
     }
     else
     {
         button = GTK_WIDGET(gtk_builder_get_object (builder,
-                            (direction == XFER_DIALOG_TO) ?
-                            "right_show_button" : "left_show_button"));
+                                                    (direction == XFER_DIALOG_TO) ?
+                                                    "right_show_button" : "left_show_button"));
         scroll_win = GTK_WIDGET(gtk_builder_get_object (builder,
-                                (direction == XFER_DIALOG_TO) ?
-                                "right_trans_window" : "left_trans_window"));
+                                                        (direction == XFER_DIALOG_TO) ?
+                                                        "right_trans_window" : "left_trans_window"));
     }
 
 
@@ -686,7 +685,7 @@ gnc_xfer_dialog_quickfill( XferDialog *xferData )
      */
 
     if ( gnc_numeric_zero_p(
-                gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit))))
+             gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit))))
     {
         gnc_numeric amt;
         DEBUG("updating amount");
@@ -718,7 +717,7 @@ gnc_xfer_dialog_quickfill( XferDialog *xferData )
      * and select that account in the appropriate account tree.
      */
     if ( ( other = xaccSplitGetOtherSplit( split ) ) &&
-            ( other_acct = xaccSplitGetAccount( other ) ) )
+         ( other_acct = xaccSplitGetAccount( other ) ) )
     {
         GNCAccountType other_type;
         GtkWidget *other_button;
@@ -804,8 +803,8 @@ gnc_xfer_description_insert_cb(GtkEditable *editable,
     g_free(prefix);
 
     if ((match = gnc_quickfill_get_string_match(xferData->qf, new_text))
-            && (match_str = gnc_quickfill_string(match))
-            && ((match_str_len = strlen(match_str)) > new_text_len))
+        && (match_str = gnc_quickfill_string(match))
+        && ((match_str_len = strlen(match_str)) > new_text_len))
     {
         g_signal_handlers_block_matched (G_OBJECT (editable),
                                          G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
@@ -829,7 +828,7 @@ gnc_xfer_description_insert_cb(GtkEditable *editable,
         xferData->desc_start_selection = *start_pos;
         xferData->desc_end_selection = -1;
         xferData->desc_selection_source_id = g_idle_add(idle_select_region,
-                                             xferData);
+                                                        xferData);
     }
     g_free(new_text);
 }
@@ -954,7 +953,7 @@ gnc_xfer_update_to_amount (XferDialog *xferData)
     account = gnc_transfer_dialog_get_selected_account(xferData, XFER_DIALOG_TO);
     if (account == NULL)
         account = gnc_transfer_dialog_get_selected_account(xferData,
-                  XFER_DIALOG_FROM);
+                                                           XFER_DIALOG_FROM);
     if (account != NULL)
         scu = xaccAccountGetCommoditySCU(account);
     else if (xferData->to_commodity != NULL)
@@ -962,7 +961,7 @@ gnc_xfer_update_to_amount (XferDialog *xferData)
 
     /* Determine the amount to transfer. */
     if (!gnc_amount_edit_evaluate(price_edit) ||
-            gnc_numeric_zero_p(price = gnc_amount_edit_get_amount(price_edit)))
+        gnc_numeric_zero_p(price = gnc_amount_edit_get_amount(price_edit)))
         to_amount = gnc_numeric_zero();
     else
         to_amount = gnc_numeric_mul(gnc_amount_edit_get_amount(amount_edit),
@@ -1099,18 +1098,18 @@ gnc_xfer_dialog_lock_account_tree(XferDialog *xferData,
 
     switch (direction)
     {
-    case XFER_DIALOG_FROM:
-        tree_view = xferData->from_tree_view;
-        scroll_win = xferData->from_window;
-        show_button = xferData->from_show_button;
-        break;
-    case XFER_DIALOG_TO:
-        tree_view = xferData->to_tree_view;
-        scroll_win = xferData->to_window;
-        show_button = xferData->to_show_button;
-        break;
-    default:
-        return;
+        case XFER_DIALOG_FROM:
+            tree_view = xferData->from_tree_view;
+            scroll_win = xferData->from_window;
+            show_button = xferData->from_show_button;
+            break;
+        case XFER_DIALOG_TO:
+            tree_view = xferData->to_tree_view;
+            scroll_win = xferData->to_window;
+            show_button = xferData->to_show_button;
+            break;
+        default:
+            return;
     }
 
     gtk_widget_set_sensitive( GTK_WIDGET(tree_view), FALSE );
@@ -1199,7 +1198,7 @@ gnc_xfer_dialog_is_exchange_dialog (XferDialog *xferData,
     g_return_if_fail(xferData);
     ENTER("xferData=%p, exch_rate=%p (%s)", xferData, exch_rate,
           exch_rate == NULL ? "NULL" : xaccPrintAmount(*exch_rate,
-                  gnc_default_print_info(FALSE)));
+                                                       gnc_default_print_info(FALSE)));
 
     gtk_widget_set_sensitive (xferData->amount_edit, FALSE);
     gtk_widget_set_sensitive (xferData->date_entry, FALSE);
@@ -1233,15 +1232,15 @@ gnc_xfer_dialog_set_amount(XferDialog *xferData, gnc_numeric amount)
         return;
 
     account = gnc_transfer_dialog_get_selected_account (xferData,
-              XFER_DIALOG_FROM);
+                                                        XFER_DIALOG_FROM);
     if (account == NULL)
         account = gnc_transfer_dialog_get_selected_account (xferData,
-                  XFER_DIALOG_TO);
+                                                            XFER_DIALOG_TO);
 
     gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->amount_edit), amount);
 }
 void gnc_xfer_dialog_set_amount_sensitive(XferDialog *xferData,
-        gboolean is_sensitive)
+                                          gboolean is_sensitive)
 {
     g_assert(xferData);
     gtk_widget_set_sensitive(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT (xferData->amount_edit)), is_sensitive);
@@ -1252,9 +1251,9 @@ gnc_xfer_dialog_set_fetch_sensitive (GtkWidget *fetch)
 {
     if (gnc_quote_source_fq_installed ())
     {
-	gtk_widget_set_sensitive (fetch, TRUE);
-	gtk_widget_set_tooltip_text (fetch, _("Retrieve the current online quote"));
-	return;
+        gtk_widget_set_sensitive (fetch, TRUE);
+        gtk_widget_set_tooltip_text (fetch, _("Retrieve the current online quote"));
+        return;
     }
     gtk_widget_set_sensitive (fetch, FALSE);
     gtk_widget_set_tooltip_text (fetch, _("Finance::Quote must be installed to enable this button."));
@@ -1509,7 +1508,7 @@ swap_amount (gnc_commodity **from, gnc_commodity **to, gnc_numeric *value,
     from_amt = to_amt;
     to_amt = tmp_amt;
     *value = gnc_numeric_div (gnc_numeric_create(1, 1), *value,
-                             GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+                              GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
 }
 static void
 create_price(XferDialog *xferData, Timespec ts)
@@ -1809,7 +1808,7 @@ gnc_xfer_dialog_create(GtkWidget *parent, XferDialog *xferData)
     gboolean  use_accounting_labels;
 
     use_accounting_labels = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
-                            GNC_PREF_ACCOUNTING_LABELS);
+                                               GNC_PREF_ACCOUNTING_LABELS);
 
     ENTER(" ");
     builder = gtk_builder_new();
@@ -2130,7 +2129,7 @@ gnc_xfer_dialog_set_to_account_label( XferDialog *xferData,
 
 void
 gnc_xfer_dialog_set_from_show_button_active( XferDialog *xferData,
-        gboolean set_value )
+                                             gboolean set_value )
 {
     if ( xferData && xferData->from_show_button )
     {
@@ -2141,7 +2140,7 @@ gnc_xfer_dialog_set_from_show_button_active( XferDialog *xferData,
 
 void
 gnc_xfer_dialog_set_to_show_button_active( XferDialog *xferData,
-        gboolean set_value )
+                                           gboolean set_value )
 {
     if ( xferData && xferData->to_show_button )
     {
@@ -2152,16 +2151,16 @@ gnc_xfer_dialog_set_to_show_button_active( XferDialog *xferData,
 
 /* Add a button with a user-specified label and "clicked" callback */
 void gnc_xfer_dialog_add_user_specified_button( XferDialog *xferData,
-        const gchar *label,
-        GCallback callback,
-        gpointer user_data )
+                                                const gchar *label,
+                                                GCallback callback,
+                                                gpointer user_data )
 {
     if ( xferData && label && callback )
     {
         GtkBuilder *builder = g_object_get_data (G_OBJECT (xferData->dialog), "builder");
         GtkWidget *button   = gtk_button_new_with_label( label );
         GtkWidget *box      = GTK_WIDGET(gtk_builder_get_object (builder,
-                                         "transfermain-vbox" ));
+                                                                 "transfermain-vbox" ));
         gtk_box_pack_end( GTK_BOX(box), button, FALSE, FALSE, 0 );
         g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (callback), user_data);
         gtk_widget_show( button );
@@ -2169,7 +2168,7 @@ void gnc_xfer_dialog_add_user_specified_button( XferDialog *xferData,
 }
 
 void gnc_xfer_dialog_toggle_currency_table( XferDialog *xferData,
-        gboolean show_table )
+                                            gboolean show_table )
 {
     if (xferData && xferData->curr_xfer_table)
     {
@@ -2212,8 +2211,8 @@ gboolean gnc_xfer_dialog_run_until_done( XferDialog *xferData )
      * that's bad mojo whole gtk_dialog_run is still in control.
      */
     count = g_signal_handlers_disconnect_by_func(dialog,
-            gnc_xfer_dialog_response_cb,
-            xferData);
+                                                 gnc_xfer_dialog_response_cb,
+                                                 xferData);
     g_assert(count == 1);
 
     while ( TRUE )
@@ -2270,22 +2269,22 @@ gnc_xfer_dialog_quickfill_to_account(XferDialog *xferData,
 
 static Account *
 gnc_transfer_dialog_get_selected_account (XferDialog *dialog,
-        XferDirection direction)
+                                          XferDirection direction)
 {
     GtkTreeView *tree_view;
     Account *account;
 
     switch (direction)
     {
-    case XFER_DIALOG_FROM:
-        tree_view = dialog->from_tree_view;
-        break;
-    case XFER_DIALOG_TO:
-        tree_view = dialog->to_tree_view;
-        break;
-    default:
-        g_assert_not_reached ();
-        return NULL;
+        case XFER_DIALOG_FROM:
+            tree_view = dialog->from_tree_view;
+            break;
+        case XFER_DIALOG_TO:
+            tree_view = dialog->to_tree_view;
+            break;
+        default:
+            g_assert_not_reached ();
+            return NULL;
     }
 
     account = gnc_tree_view_account_get_selected_account  (GNC_TREE_VIEW_ACCOUNT (tree_view));
@@ -2294,8 +2293,8 @@ gnc_transfer_dialog_get_selected_account (XferDialog *dialog,
 
 static void
 gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
-        Account *account,
-        XferDirection direction)
+                                          Account *account,
+                                          XferDirection direction)
 {
     GtkTreeView *tree_view;
     GtkCheckButton *show_button;
@@ -2306,17 +2305,17 @@ gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
 
     switch (direction)
     {
-    case XFER_DIALOG_FROM:
-        tree_view = dialog->from_tree_view;
-        show_button = GTK_CHECK_BUTTON (dialog->from_show_button);
-        break;
-    case XFER_DIALOG_TO:
-        tree_view = dialog->to_tree_view;
-        show_button = GTK_CHECK_BUTTON (dialog->to_show_button);
-        break;
-    default:
-        g_assert_not_reached ();
-        return;
+        case XFER_DIALOG_FROM:
+            tree_view = dialog->from_tree_view;
+            show_button = GTK_CHECK_BUTTON (dialog->from_show_button);
+            break;
+        case XFER_DIALOG_TO:
+            tree_view = dialog->to_tree_view;
+            show_button = GTK_CHECK_BUTTON (dialog->to_show_button);
+            break;
+        default:
+            g_assert_not_reached ();
+            return;
     }
 
     type = xaccAccountGetType (account);
@@ -2325,7 +2324,7 @@ gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
                                   (type == ACCT_TYPE_INCOME));
 
     gnc_tree_view_account_set_selected_account (GNC_TREE_VIEW_ACCOUNT (tree_view),
-            account);
+                                                account);
 }
 
 
@@ -2399,8 +2398,8 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
 
         /* XXX: should we tell the user we've done the conversion? */
         amount = gnc_numeric_div(
-                     amount, rate,
-                     gnc_commodity_get_fraction(txn_cur), GNC_HOW_DENOM_REDUCE);
+            amount, rate,
+            gnc_commodity_get_fraction(txn_cur), GNC_HOW_DENOM_REDUCE);
     }
 
     /* enter the accounts */

commit 85148cd23bf27ac12e4add9b3cdf59cb443c5889
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Aug 22 10:44:00 2015 +0100

    Refactor gnc_xfer_dialog_response_cb with several extract-functions.

diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index aaa7a24..50c1367 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -1353,24 +1353,257 @@ gnc_xfer_dialog_set_exchange_rate(XferDialog *xferData, gnc_numeric exchange_rat
     gnc_xfer_update_to_amount (xferData);
 }
 
+static gboolean
+check_accounts  (XferDialog* xferData, Account* from_account,
+                 Account* to_account)
+{
+    if ((from_account == NULL) || (to_account == NULL))
+    {
+        const char *message = _("You must specify an account to transfer from, "
+                                "or to, or both, for this transaction. "
+                                "Otherwise, it will not be recorded.");
+        gnc_error_dialog(xferData->dialog, "%s", message);
+        LEAVE("bad account");
+        return FALSE;
+    }
+
+    if (from_account == to_account)
+    {
+        const char *message = _("You can't transfer from and to the same "
+                                "account!");
+        gnc_error_dialog(xferData->dialog, "%s", message);
+        LEAVE("same account");
+        return FALSE;
+    }
+
+    if (xaccAccountGetPlaceholder(from_account) ||
+        xaccAccountGetPlaceholder(to_account))
+    {
+        const char *placeholder_format =
+            _("The account %s does not allow transactions.");
+        char *name;
+
+        if (xaccAccountGetPlaceholder(from_account))
+            name = gnc_account_get_full_name(from_account);
+        else
+            name = gnc_account_get_full_name(to_account);
+        gnc_error_dialog(xferData->dialog, placeholder_format, name);
+        g_free(name);
+        LEAVE("placeholder");
+        return TRUE;
+    }
+
+    if (!gnc_commodity_is_iso (xferData->from_commodity))
+    {
+        const char *message =
+            _("You can't transfer from a non-currency account. "
+              "Try reversing the \"from\" and \"to\" accounts "
+              "and making the \"amount\" negative.");
+        gnc_error_dialog(xferData->dialog, "%s", message);
+        LEAVE("non-currency");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static gboolean
+check_edit(XferDialog *xferData)
+{
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->price_edit)))
+    {
+        if (gtk_toggle_button_get_active
+            (GTK_TOGGLE_BUTTON(xferData->price_radio)))
+        {
+            gnc_parse_error_dialog (xferData, _("You must enter a valid price."));
+            LEAVE("invalid price");
+            return FALSE;
+        }
+    }
+
+    if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit)))
+    {
+        if (gtk_toggle_button_get_active
+            (GTK_TOGGLE_BUTTON(xferData->amount_radio)))
+        {
+            gnc_parse_error_dialog (xferData,
+                                    _("You must enter a valid `to' amount."));
+            LEAVE("invalid to amount");
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+static void
+create_transaction(XferDialog *xferData, Timespec *ts,
+                   Account *from_account, Account* to_account,
+                   gnc_numeric amount, gnc_numeric to_amount)
+{
+    Transaction *trans;
+    Split *from_split;
+    Split *to_split;
+    const char *string;
+    /* Create the transaction */
+    trans = xaccMallocTransaction(xferData->book);
+
+    xaccTransBeginEdit(trans);
+
+    xaccTransSetCurrency(trans, xferData->from_commodity);
+    xaccTransSetDatePostedTS(trans, ts);
+
+    /* Trans-Num or Split-Action set with gnc_set_num_action below per book
+     * option */
+
+    string = gtk_entry_get_text(GTK_ENTRY(xferData->description_entry));
+    xaccTransSetDescription(trans, string);
+
+    /* create from split */
+    from_split = xaccMallocSplit(xferData->book);
+    xaccTransAppendSplit(trans, from_split);
+
+    /* create to split */
+    to_split = xaccMallocSplit(xferData->book);
+    xaccTransAppendSplit(trans, to_split);
+
+    xaccAccountBeginEdit(from_account);
+    xaccAccountInsertSplit(from_account, from_split);
+
+    xaccAccountBeginEdit(to_account);
+    xaccAccountInsertSplit(to_account, to_split);
+
+    xaccSplitSetBaseValue(from_split, gnc_numeric_neg (amount),
+                          xferData->from_commodity);
+    xaccSplitSetBaseValue(to_split, amount, xferData->from_commodity);
+    xaccSplitSetBaseValue(to_split, to_amount, xferData->to_commodity);
+
+    /* Set the transaction number or split action field based on book option*/
+    string = gtk_entry_get_text(GTK_ENTRY(xferData->num_entry));
+    gnc_set_num_action (trans, from_split, string, NULL);
+
+    /* Set the memo fields */
+    string = gtk_entry_get_text(GTK_ENTRY(xferData->memo_entry));
+    xaccSplitSetMemo(from_split, string);
+    xaccSplitSetMemo(to_split, string);
+
+    /* finish transaction */
+    xaccTransCommitEdit(trans);
+    xaccAccountCommitEdit(from_account);
+    xaccAccountCommitEdit(to_account);
+
+    /* If there is a registered callback handler that should be
+       notified of the newly created Transaction, call it now. */
+    if (xferData->transaction_cb)
+        xferData->transaction_cb(trans, xferData->transaction_user_data);
+}
+
+static void
+swap_amount (gnc_commodity **from, gnc_commodity **to, gnc_numeric *value,
+             gnc_numeric *from_amt, gnc_numeric *to_amt)
+{
+    gnc_commodity *tmp;
+    gnc_numeric *tmp_amt;
+    tmp = *from;
+    *from = *to;
+    *to = tmp;
+    tmp_amt = from_amt;
+    from_amt = to_amt;
+    to_amt = tmp_amt;
+    *value = gnc_numeric_div (gnc_numeric_create(1, 1), *value,
+                             GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
+}
+static void
+create_price(XferDialog *xferData, Timespec ts)
+{
+    gnc_commodity *from = xferData->from_commodity;
+    gnc_commodity *to = xferData->to_commodity;
+    GNCPrice *price;
+    gnc_numeric price_value;
+    gnc_numeric value;
+    gnc_numeric from_amt, to_amt;
+
+/* Bail in the unlikely event that both currencies have joined the Euro. */
+    if (gnc_is_euro_currency (from) && gnc_is_euro_currency (to))
+        return;
+
+    from_amt =
+        gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
+    to_amt =
+        gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->to_amount_edit));
+
+    /* compute the price -- maybe we need to swap? */
+    value = gnc_numeric_div(to_amt, from_amt, GNC_DENOM_AUTO,
+                            GNC_HOW_DENOM_REDUCE);
+    value = gnc_numeric_abs (value);
+
+    /* Try to be consistent about how quotes are installed. */
+    if (from == gnc_default_currency() ||
+        ((to != gnc_default_currency()) &&
+         (strcmp (gnc_commodity_get_mnemonic(from),
+                  gnc_commodity_get_mnemonic(to)) < 0)))
+        swap_amount (&from, &to, &value, &from_amt, &to_amt);
+    /* First see if the closest entry on the same day has an equivalent rate */
+    price = gnc_pricedb_lookup_day (xferData->pricedb, from, to, ts);
+    if (price)
+    {
+        price_value = gnc_price_get_value(price);
+    }
+    else
+    {
+        price = gnc_pricedb_lookup_day (xferData->pricedb, to, from, ts);
+        if (price)
+        {
+            price_value = gnc_numeric_div (gnc_numeric_create(1, 1),
+                                           gnc_price_get_value(price),
+                                           GNC_DENOM_AUTO,
+                                           GNC_HOW_DENOM_REDUCE);
+        }
+    }
+
+    /* See if we found a good enough price */
+    if (price)
+    {
+        int scu = gnc_commodity_get_fraction (to);
+        if (!gnc_numeric_equal (gnc_numeric_mul (from_amt, price_value,
+                                                 scu, GNC_HOW_RND_ROUND_HALF_UP),
+                                to_amt))
+        {
+            gnc_price_unref (price);
+            price = NULL;
+        }
+    }
+
+    if (price)
+    {
+        PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
+              gnc_commodity_get_mnemonic(to));
+    }
+    else
+    {
+        price = gnc_price_create (xferData->book);
+        gnc_price_begin_edit (price);
+        gnc_price_set_commodity (price, from);
+        gnc_price_set_currency (price, to);
+        gnc_price_set_time (price, ts);
+        gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
+        gnc_price_set_value (price, value);
+        gnc_pricedb_add_price (xferData->pricedb, price);
+        gnc_price_commit_edit (price);
+        PINFO("Created price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
+              gnc_numeric_to_double(value), gnc_commodity_get_mnemonic(to));
+    }
+
+    gnc_price_unref (price);
+}
+
 void
 gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
 {
     XferDialog *xferData = data;
     Account *to_account;
     Account *from_account;
-    gnc_commodity *from_commodity;
-    gnc_commodity *to_commodity;
     gnc_numeric amount, to_amount;
-    const char *string;
     Timespec ts;
 
-    gboolean curr_trans;
-
-    Transaction *trans;
-    Split *from_split;
-    Split *to_split;
-
     g_return_if_fail (xferData != NULL);
     ENTER(" ");
 
@@ -1390,54 +1623,9 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
     from_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
     to_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
 
-    if (xferData->exch_rate == NULL)
-    {
-        if ((from_account == NULL) || (to_account == NULL))
-        {
-            const char *message = _("You must specify an account to transfer from, "
-                                    "or to, or both, for this transaction. "
-                                    "Otherwise, it will not be recorded.");
-            gnc_error_dialog(xferData->dialog, "%s", message);
-            LEAVE("bad account");
-            return;
-        }
-
-        if (from_account == to_account)
-        {
-            const char *message = _("You can't transfer from and to the same "
-                                    "account!");
-            gnc_error_dialog(xferData->dialog, "%s", message);
-            LEAVE("same account");
-            return;
-        }
-
-        if (xaccAccountGetPlaceholder(from_account) ||
-                xaccAccountGetPlaceholder(to_account))
-        {
-            const char *placeholder_format =
-                _("The account %s does not allow transactions.");
-            char *name;
-
-            if (xaccAccountGetPlaceholder(from_account))
-                name = gnc_account_get_full_name(from_account);
-            else
-                name = gnc_account_get_full_name(to_account);
-            gnc_error_dialog(xferData->dialog, placeholder_format, name);
-            g_free(name);
-            LEAVE("placeholder");
-            return;
-        }
-
-        if (!gnc_commodity_is_iso (xferData->from_commodity))
-        {
-            const char *message = _("You can't transfer from a non-currency account. "
-                                    "Try reversing the \"from\" and \"to\" accounts "
-                                    "and making the \"amount\" negative.");
-            gnc_error_dialog(xferData->dialog, "%s", message);
-            LEAVE("non-currency");
-            return;
-        }
-    }
+    if (xferData->exch_rate == NULL &&
+        !check_accounts(xferData, from_account, to_account))
+        return;
 
     if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit)))
     {
@@ -1446,11 +1634,6 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
         return;
     }
 
-    from_commodity = xferData->from_commodity;
-    to_commodity = xferData->to_commodity;
-
-    curr_trans = !gnc_commodity_equiv(from_commodity, to_commodity);
-
     amount = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
 
     if (gnc_numeric_zero_p (amount))
@@ -1463,33 +1646,12 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
 
     ts = gnc_date_edit_get_date_ts(GNC_DATE_EDIT(xferData->date_entry));
 
-    if (curr_trans)
+    if (!gnc_commodity_equiv(xferData->from_commodity, xferData->to_commodity))
     {
-        if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->price_edit)))
-        {
-            if (gtk_toggle_button_get_active
-                    (GTK_TOGGLE_BUTTON(xferData->price_radio)))
-            {
-                gnc_parse_error_dialog (xferData, _("You must enter a valid price."));
-                LEAVE("invalid price");
-                return;
-            }
-        }
-
-        if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit)))
-        {
-            if (gtk_toggle_button_get_active
-                    (GTK_TOGGLE_BUTTON(xferData->amount_radio)))
-            {
-                gnc_parse_error_dialog (xferData,
-                                        _("You must enter a valid `to' amount."));
-                LEAVE("invalid to amount");
-                return;
-            }
-        }
-
+        if (!check_edit(xferData))
+            return;
         to_amount = gnc_amount_edit_get_amount
-                    (GNC_AMOUNT_EDIT(xferData->to_amount_edit));
+            (GNC_AMOUNT_EDIT(xferData->to_amount_edit));
     }
     else
         to_amount = amount;
@@ -1510,156 +1672,12 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
         *(xferData->exch_rate) = gnc_numeric_abs(price);
     }
     else
-    {
-        /* Create the transaction */
-        trans = xaccMallocTransaction(xferData->book);
-
-        xaccTransBeginEdit(trans);
-
-        xaccTransSetCurrency(trans, from_commodity);
-        xaccTransSetDatePostedTS(trans, &ts);
-
-        /* Trans-Num or Split-Action set with gnc_set_num_action below per book
-         * option */
-
-        string = gtk_entry_get_text(GTK_ENTRY(xferData->description_entry));
-        xaccTransSetDescription(trans, string);
-
-        /* create from split */
-        from_split = xaccMallocSplit(xferData->book);
-        xaccTransAppendSplit(trans, from_split);
-
-        /* create to split */
-        to_split = xaccMallocSplit(xferData->book);
-        xaccTransAppendSplit(trans, to_split);
-
-        xaccAccountBeginEdit(from_account);
-        xaccAccountInsertSplit(from_account, from_split);
-
-        xaccAccountBeginEdit(to_account);
-        xaccAccountInsertSplit(to_account, to_split);
-
-        xaccSplitSetBaseValue(from_split, gnc_numeric_neg (amount), from_commodity);
-        xaccSplitSetBaseValue(to_split, amount, from_commodity);
-        xaccSplitSetBaseValue(to_split, to_amount, to_commodity);
-
-        /* Set the transaction number or split action field based on book option*/
-        string = gtk_entry_get_text(GTK_ENTRY(xferData->num_entry));
-        gnc_set_num_action (trans, from_split, string, NULL);
-
-        /* Set the memo fields */
-        string = gtk_entry_get_text(GTK_ENTRY(xferData->memo_entry));
-        xaccSplitSetMemo(from_split, string);
-        xaccSplitSetMemo(to_split, string);
-
-        /* finish transaction */
-        xaccTransCommitEdit(trans);
-        xaccAccountCommitEdit(from_account);
-        xaccAccountCommitEdit(to_account);
-
-        /* If there is a registered callback handler that should be
-           notified of the newly created Transaction, call it now. */
-        if (xferData->transaction_cb)
-            xferData->transaction_cb(trans, xferData->transaction_user_data);
-    }
-
+        create_transaction (xferData, &ts, from_account, to_account,
+                            amount, to_amount);
     /* try to save this to the pricedb */
-    if (xferData->pricedb)
-    {
-        gnc_commodity *from = xferData->from_commodity;
-        gnc_commodity *to = xferData->to_commodity;
-
-        /* only continue if the currencies are DIFFERENT and are
-         * not both euroland currencies
-         */
-        if (!gnc_commodity_equal (from, to) &&
-                !(gnc_is_euro_currency (from) && gnc_is_euro_currency (to)))
-        {
-            GNCPrice *price;
-            gnc_numeric price_value;
-            gnc_numeric value;
-            gnc_commodity *tmp;
-            gnc_numeric from_amt, to_amt;
-            gnc_numeric tmp_amt;
-
-            from_amt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
-            to_amt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->to_amount_edit));
-
-            /* compute the price -- maybe we need to swap? */
-
-            value = gnc_numeric_div(to_amt, from_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
-            value = gnc_numeric_abs (value);
-
-            /* Try to be consistent about how quotes are installed. */
-            if (from == gnc_default_currency() ||
-                    ((to != gnc_default_currency()) &&
-                     (strcmp (gnc_commodity_get_mnemonic(from),
-                              gnc_commodity_get_mnemonic(to)) < 0)))
-            {
-                tmp = from;
-                from = to;
-                to = tmp;
-                tmp_amt = from_amt;
-                from_amt = to_amt;
-                to_amt = tmp_amt;
-                value = gnc_numeric_div (gnc_numeric_create(1, 1), value,
-                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
-            }
-
-            /* First see if the closest entry on the same day has an equivalent rate */
-            price = gnc_pricedb_lookup_day (xferData->pricedb, from, to, ts);
-            if (price)
-            {
-                price_value = gnc_price_get_value(price);
-            }
-            else
-            {
-                price = gnc_pricedb_lookup_day (xferData->pricedb, to, from, ts);
-                if (price)
-                {
-                    price_value = gnc_numeric_div (gnc_numeric_create(1, 1),
-                                                   gnc_price_get_value(price),
-                                                   GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
-                }
-            }
-
-            /* See if we found a good enough price */
-            if (price)
-            {
-                int scu = gnc_commodity_get_fraction (to);
-                if (!gnc_numeric_equal (gnc_numeric_mul (from_amt, price_value,
-                                        scu, GNC_HOW_RND_ROUND_HALF_UP),
-                                        to_amt))
-                {
-                    gnc_price_unref (price);
-                    price = NULL;
-                }
-            }
-
-            if (price)
-            {
-                PINFO("Found price for %s in %s", gnc_commodity_get_mnemonic(from),
-                      gnc_commodity_get_mnemonic(to));
-            }
-            else
-            {
-                price = gnc_price_create (xferData->book);
-                gnc_price_begin_edit (price);
-                gnc_price_set_commodity (price, from);
-                gnc_price_set_currency (price, to);
-                gnc_price_set_time (price, ts);
-                gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
-                gnc_price_set_value (price, value);
-                gnc_pricedb_add_price (xferData->pricedb, price);
-                gnc_price_commit_edit (price);
-                PINFO("Created price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
-                      gnc_numeric_to_double(value), gnc_commodity_get_mnemonic(to));
-            }
-
-            gnc_price_unref (price);
-        }
-    }
-
+    if (xferData->pricedb && !gnc_commodity_equal (xferData->from_commodity,
+                                                   xferData->to_commodity))
+        create_price(xferData, ts);
     /* Refresh everything */
     gnc_resume_gui_refresh ();
 

commit e94622c9e32071598723862d989f76765e79c4cc
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Aug 22 09:17:49 2015 +0100

    Don't store prices of source invoice.
    
    They're intended to be temporary, for creating splits. They're also already
    stored from the transfer dialog.

diff --git a/src/backend/sql/gnc-price-sql.c b/src/backend/sql/gnc-price-sql.c
index d7e6b70..8729c91 100644
--- a/src/backend/sql/gnc-price-sql.c
+++ b/src/backend/sql/gnc-price-sql.c
@@ -203,7 +203,7 @@ write_price( GNCPrice* p, gpointer data )
     g_return_val_if_fail( p != NULL, FALSE );
     g_return_val_if_fail( data != NULL, FALSE );
 
-    if ( s->is_ok )
+    if ( s->is_ok && strcmp(gnc_price_get_source(p), PRICE_SOURCE_INVOICE) != 0)
     {
         s->is_ok = save_price( s->be, QOF_INSTANCE(p) );
     }

commit ab535fb1005cbda85585cbd91a272e370a82d112
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 21 15:54:58 2015 +0100

    Replace the price source and type strings with defines.

diff --git a/src/business/business-gnome/dialog-invoice.c b/src/business/business-gnome/dialog-invoice.c
index e840742..10c4d75 100644
--- a/src/business/business-gnome/dialog-invoice.c
+++ b/src/business/business-gnome/dialog-invoice.c
@@ -901,11 +901,8 @@ gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params)
             gnc_price_set_commodity (convprice, account_currency);
             gnc_price_set_currency (convprice, gncInvoiceGetCurrency (invoice));
             gnc_price_set_time (convprice, postdate);
-            gnc_price_set_source (convprice, "user:invoice-post");
-
-            /* Yes, magic strings are evil but I can't find any defined constants
-               for this..*/
-            gnc_price_set_typestr (convprice, "last");
+            gnc_price_set_source (convprice, PRICE_SOURCE_INVOICE);
+            gnc_price_set_typestr (convprice, PRICE_TYPE_LAST);
             gnc_price_set_value (convprice, exch_rate);
             gncInvoiceAddPrice(invoice, convprice);
             gnc_price_commit_edit (convprice);
@@ -1702,7 +1699,7 @@ gnc_invoice_update_window (InvoiceWindow *iw, GtkWidget *widget)
     }
 
     /* Set the type label */
-    gtk_label_set_text (GTK_LABEL(iw->type_label), iw->is_credit_note ? _("Credit Note") 
+    gtk_label_set_text (GTK_LABEL(iw->type_label), iw->is_credit_note ? _("Credit Note")
                         : gtk_label_get_text (GTK_LABEL(iw->type_label)));
 
     if (iw->owner_choice)
@@ -1820,14 +1817,14 @@ gnc_invoice_update_window (InvoiceWindow *iw, GtkWidget *widget)
             gtk_widget_hide (hide);
             hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide4"));
             gtk_widget_hide (hide);
-            
+
             show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "posted_label"));
             gtk_widget_show (show);
             gtk_widget_show (iw->posted_date_hbox);
             show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "acct_label"));
             gtk_widget_show (show);
             gtk_widget_show (acct_entry);
-            
+
             show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide1"));
             gtk_widget_show (show);
             show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide2"));
@@ -2347,17 +2344,17 @@ gnc_invoice_create_page (InvoiceWindow *iw, gpointer page)
         {
         case GNC_OWNER_VENDOR:
             gtk_label_set_text (GTK_LABEL(iw->info_label),  _("Bill Information"));
-            gtk_label_set_text (GTK_LABEL(iw->type_label),  _("Bill")); 
-            gtk_label_set_text (GTK_LABEL(iw->id_label),  _("Bill ID")); 
+            gtk_label_set_text (GTK_LABEL(iw->type_label),  _("Bill"));
+            gtk_label_set_text (GTK_LABEL(iw->id_label),  _("Bill ID"));
             break;
         case GNC_OWNER_EMPLOYEE:
             gtk_label_set_text (GTK_LABEL(iw->info_label),  _("Voucher Information"));
             gtk_label_set_text (GTK_LABEL(iw->type_label),  _("Voucher"));
-            gtk_label_set_text (GTK_LABEL(iw->id_label),  _("Voucher ID")); 
+            gtk_label_set_text (GTK_LABEL(iw->id_label),  _("Voucher ID"));
         default:
             break;
         }
-    
+
     entry_ledger = gnc_entry_ledger_new (iw->book, ledger_type);
 
     /* Save the ledger... */
@@ -2430,7 +2427,7 @@ gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
     const GncOwner *start_owner;
     GncBillTerm *owner_terms = NULL;
     GncOwnerType owner_type;
-    
+
     g_assert (dialog_type == NEW_INVOICE || dialog_type == MOD_INVOICE || dialog_type == DUP_INVOICE);
 
     if (invoice)
@@ -2513,20 +2510,20 @@ gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
     iw->id_label = GTK_WIDGET (gtk_builder_get_object (builder, "label14"));
     iw->info_label = GTK_WIDGET (gtk_builder_get_object (builder, "label1"));
     invoice_radio = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_invoice_type"));
-     
+
     iw->type_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_choice_hbox"));
     iw->type_choice = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_invoice"));
-    
+
     /* The default GUI lables are for invoices, so change them if it isn't. */
     owner_type = gncOwnerGetType (&iw->owner);
     switch(owner_type)
     {
         case GNC_OWNER_VENDOR:
             gtk_label_set_text (GTK_LABEL(iw->info_label),  _("Bill Information"));
-            gtk_label_set_text (GTK_LABEL(iw->type_label),  _("Bill")); 
+            gtk_label_set_text (GTK_LABEL(iw->type_label),  _("Bill"));
             gtk_button_set_label (GTK_BUTTON(invoice_radio),  _("Bill"));
             gtk_label_set_text (GTK_LABEL(iw->id_label),  _("Bill ID"));
-             
+
             break;
         case GNC_OWNER_EMPLOYEE:
             gtk_label_set_text (GTK_LABEL(iw->info_label),  _("Voucher Information"));
@@ -2536,7 +2533,7 @@ gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
         default:
         break;
     }
-    
+
     /* configure the type related widgets based on dialog type and invoice type */
     switch (dialog_type)
     {
@@ -3306,4 +3303,3 @@ gnc_invoice_remind_bills_due_cb (void)
 
     gnc_invoice_remind_bills_due();
 }
-
diff --git a/src/engine/gnc-pricedb.c b/src/engine/gnc-pricedb.c
index 54b7385..81febf1 100644
--- a/src/engine/gnc-pricedb.c
+++ b/src/engine/gnc-pricedb.c
@@ -966,6 +966,18 @@ gnc_pricedb_equal (GNCPriceDB *db1, GNCPriceDB *db2)
     return equal_data.equal;
 }
 
+static gboolean
+insert_or_replace_price(GNCPriceDB *db, GNCPrice *p)
+{
+    GNCPrice *old_price = gnc_pricedb_lookup_day (db, p->commodity,
+                                                  p->currency, p->tmspec);
+    if (old_price == NULL)
+        return TRUE;
+    if (strcmp(p->source, "PRICE_SOURCE_FQ"))
+        return TRUE;
+    return FALSE;
+}
+
 /* ==================================================================== */
 /* The add_price() function is a utility that only manages the
  * dual hash table instertion */
@@ -1030,6 +1042,12 @@ add_price(GNCPriceDB *db, GNCPrice *p)
         LEAVE (" no price list");
         return FALSE;
     }
+
+    if (!insert_or_replace_price(db, p))
+    {
+        LEAVE("A better price already exists");
+        return FALSE;
+    }
     g_hash_table_insert(currency_hash, currency, price_list);
     p->db = db;
     qof_event_gen (&p->inst, QOF_EVENT_ADD, NULL);
diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h
index ce077a5..befff2d 100644
--- a/src/engine/gnc-pricedb.h
+++ b/src/engine/gnc-pricedb.h
@@ -207,6 +207,15 @@ void gnc_price_set_typestr(GNCPrice *p, const char* type);
 void gnc_price_set_value(GNCPrice *p, gnc_numeric value);
 /** @} */
 
+#define PRICE_SOURCE_FQ  "Finance::Quote"
+#define PRICE_SOURCE_INVOICE "user:invoice-post"
+#define PRICE_SOURCE_STOCK_SPLIT "user:stock-split"
+#define PRICE_SOURCE_XFER_DLG "user:xfer-dialog"
+#define PRICE_SOURCE_SPLIT_REG "user:split-register"
+#define PRICE_SOURCE_EDIT_DLG "user:price-editor"
+#define PRICE_TYPE_LAST "last"
+#define PRICE_TYPE_UNK "unknown"
+
 /* ------------------ */
 /** @name  Getters
     All of the getters return data that's internal
diff --git a/src/gnome-utils/dialog-transfer.c b/src/gnome-utils/dialog-transfer.c
index cd87d46..aaa7a24 100644
--- a/src/gnome-utils/dialog-transfer.c
+++ b/src/gnome-utils/dialog-transfer.c
@@ -1648,7 +1648,7 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
                 gnc_price_set_commodity (price, from);
                 gnc_price_set_currency (price, to);
                 gnc_price_set_time (price, ts);
-                gnc_price_set_source (price, "user:xfer-dialog");
+                gnc_price_set_source (price, PRICE_SOURCE_XFER_DLG);
                 gnc_price_set_value (price, value);
                 gnc_pricedb_add_price (xferData->pricedb, price);
                 gnc_price_commit_edit (price);
diff --git a/src/gnome/assistant-stock-split.c b/src/gnome/assistant-stock-split.c
index 7c19634..262df75 100644
--- a/src/gnome/assistant-stock-split.c
+++ b/src/gnome/assistant-stock-split.c
@@ -398,8 +398,8 @@ gnc_stock_split_assistant_finish (GtkAssistant *assistant,
         gnc_price_set_commodity (price, xaccAccountGetCommodity (account));
         gnc_price_set_currency (price, gnc_currency_edit_get_currency (ce));
         gnc_price_set_time (price, ts);
-        gnc_price_set_source (price, "user:stock-split");
-        gnc_price_set_typestr (price, "unknown");
+        gnc_price_set_source (price, PRICE_SOURCE_STOCK_SPLIT);
+        gnc_price_set_typestr (price, PRICE_TYPE_UNK);
         gnc_price_set_value (price, amount);
         gnc_price_commit_edit (price);
 
diff --git a/src/gnome/dialog-price-editor.c b/src/gnome/dialog-price-editor.c
index ac980dc..c7c5b9e 100644
--- a/src/gnome/dialog-price-editor.c
+++ b/src/gnome/dialog-price-editor.c
@@ -50,8 +50,6 @@
 
 #define DIALOG_PRICE_EDIT_CM_CLASS "dialog-price-edit"
 #define GNC_PREFS_GROUP "dialogs.price-editor"
-#define DIALOG_PRICE_EDIT_SOURCE "user:price-editor"
-
 
 /* This static indicates the debugging module that this .o belongs to.  */
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
@@ -172,7 +170,7 @@ price_to_gui (PriceEditDialog *pedit_dialog)
         currency = gnc_default_currency ();
         date.tv_sec = gnc_time (NULL);
         date.tv_nsec = 0;
-        source = DIALOG_PRICE_EDIT_SOURCE;
+        source = PRICE_SOURCE_EDIT_DLG;
         type = "";
         value = gnc_numeric_zero ();
     }
@@ -553,7 +551,7 @@ gnc_price_edit_dialog (GtkWidget * parent,
             price = gnc_price_clone(price, pedit_dialog->book);
 //  } else {
 //      price = gnc_price_create (pedit_dialog->book);
-            gnc_price_set_source (price, DIALOG_PRICE_EDIT_SOURCE);
+            gnc_price_set_source (price, PRICE_SOURCE_EDIT_DLG);
         }
 
         pedit_dialog->is_new = TRUE;
diff --git a/src/register/ledger-core/split-register.c b/src/register/ledger-core/split-register.c
index 3531633..fc4f587 100644
--- a/src/register/ledger-core/split-register.c
+++ b/src/register/ledger-core/split-register.c
@@ -2067,7 +2067,7 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
     gnc_price_set_commodity (price, comm);
     gnc_price_set_currency (price, curr);
     gnc_price_set_time (price, ts);
-    gnc_price_set_source (price, "user:split-register");
+    gnc_price_set_source (price, PRICE_SOURCE_SPLIT_REG);
     gnc_price_set_value (price, value);
     gnc_pricedb_add_price (pricedb, price);
     gnc_price_commit_edit (price);



Summary of changes:
 src/app-utils/gnc-sx-instance-model.c        |    7 +-
 src/backend/sql/gnc-price-sql.c              |    2 +-
 src/backend/xml/gnc-pricedb-xml-v2.c         |    4 +-
 src/backend/xml/io-gncxml-v1.cpp             |    2 +-
 src/business/business-gnome/dialog-invoice.c |   36 +-
 src/engine/engine.i                          |   11 +-
 src/engine/gnc-pricedb-p.h                   |   13 +-
 src/engine/gnc-pricedb.c                     |  250 ++-----
 src/engine/gnc-pricedb.h                     |   38 +-
 src/engine/test-core/test-engine-stuff.cpp   |    7 +-
 src/engine/test/utest-Account.cpp            |   11 +-
 src/engine/test/utest-Budget.c               |   21 +-
 src/engine/test/utest-Split.cpp              |    8 +-
 src/engine/test/utest-Transaction.cpp        |   10 +-
 src/gnome-utils/dialog-transfer.c            | 1031 ++++++++++++++------------
 src/gnome-utils/dialog-transfer.h            |    8 +-
 src/gnome-utils/gnc-tree-model-price.c       |    2 +-
 src/gnome-utils/gnc-tree-view-price.c        |    3 +-
 src/gnome/assistant-stock-split.c            |    4 +-
 src/gnome/dialog-commodities.c               |    3 +-
 src/gnome/dialog-price-editor.c              |   10 +-
 src/libqof/qof/gnc-numeric.cpp               |    7 +
 src/libqof/qof/gnc-numeric.h                 |    7 +
 src/libqof/qof/gnc-rational.cpp              |    4 +-
 src/libqof/qof/gnc-rational.hpp              |    1 +
 src/register/ledger-core/split-register.c    |   68 +-
 src/scm/price-quotes.scm                     |   57 +-
 27 files changed, 868 insertions(+), 757 deletions(-)



More information about the gnucash-changes mailing list