mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Rewrite GNCPriceDB queries to check for prices in both directions.
Currency exchange rates can be recorded in either direction depending upon which one provides the larger fraction. Queries therefore need to check both directions when creating the date-ordered list for selecting latest, nearest in time, or latest before.
This commit is contained in:
parent
dde310bf71
commit
62c7693860
@ -81,6 +81,7 @@ typedef enum
|
|||||||
typedef struct gnc_price_lookup_helper_s
|
typedef struct gnc_price_lookup_helper_s
|
||||||
{
|
{
|
||||||
GList **return_list;
|
GList **return_list;
|
||||||
|
gnc_commodity *key;
|
||||||
Timespec time;
|
Timespec time;
|
||||||
} GNCPriceLookupHelper;
|
} GNCPriceLookupHelper;
|
||||||
|
|
||||||
|
@ -1390,6 +1390,70 @@ gnc_pricedb_remove_old_prices(GNCPriceDB *db,
|
|||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
/* lookup/query functions */
|
/* lookup/query functions */
|
||||||
|
|
||||||
|
static void
|
||||||
|
hash_values_helper(gpointer key, gpointer value, gpointer data)
|
||||||
|
{
|
||||||
|
GList ** l = data;
|
||||||
|
*l = g_list_concat(*l, g_list_copy (value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PriceList *
|
||||||
|
price_list_from_hashtable (GHashTable *hash, const gnc_commodity *currency)
|
||||||
|
{
|
||||||
|
GList *price_list = NULL, *result = NULL ;
|
||||||
|
if (currency)
|
||||||
|
{
|
||||||
|
price_list = g_hash_table_lookup(hash, currency);
|
||||||
|
if (!price_list)
|
||||||
|
{
|
||||||
|
LEAVE (" no price list");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result = g_list_copy (price_list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_hash_table_foreach(hash, hash_values_helper, (gpointer)&result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PriceList*
|
||||||
|
pricedb_get_prices_internal(GNCPriceDB *db, const gnc_commodity *commodity,
|
||||||
|
const gnc_commodity *currency, gboolean bidi)
|
||||||
|
{
|
||||||
|
GHashTable *forward_hash = NULL, *reverse_hash = NULL;
|
||||||
|
PriceList *forward_list = NULL, *reverse_list = NULL;
|
||||||
|
g_return_val_if_fail (db != NULL, NULL);
|
||||||
|
g_return_val_if_fail (commodity != NULL, NULL);
|
||||||
|
forward_hash = g_hash_table_lookup(db->commodity_hash, commodity);
|
||||||
|
if (currency && bidi)
|
||||||
|
reverse_hash = g_hash_table_lookup(db->commodity_hash, currency);
|
||||||
|
if (!forward_hash && !reverse_hash)
|
||||||
|
{
|
||||||
|
LEAVE (" no currency hash");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (forward_hash)
|
||||||
|
forward_list = price_list_from_hashtable (forward_hash, currency);
|
||||||
|
if (currency && reverse_hash)
|
||||||
|
{
|
||||||
|
reverse_list = price_list_from_hashtable (reverse_hash, commodity);
|
||||||
|
if (reverse_list)
|
||||||
|
{
|
||||||
|
if (forward_list)
|
||||||
|
forward_list = g_list_concat (forward_list, reverse_list);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
forward_list = reverse_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (forward_list)
|
||||||
|
forward_list = g_list_sort (forward_list, compare_prices_by_date);
|
||||||
|
return forward_list;
|
||||||
|
}
|
||||||
|
|
||||||
GNCPrice *
|
GNCPrice *
|
||||||
gnc_pricedb_lookup_latest(GNCPriceDB *db,
|
gnc_pricedb_lookup_latest(GNCPriceDB *db,
|
||||||
const gnc_commodity *commodity,
|
const gnc_commodity *commodity,
|
||||||
@ -1402,20 +1466,8 @@ gnc_pricedb_lookup_latest(GNCPriceDB *db,
|
|||||||
if (!db || !commodity || !currency) return NULL;
|
if (!db || !commodity || !currency) return NULL;
|
||||||
ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
|
ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
|
||||||
|
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
|
price_list = pricedb_get_prices_internal(db, commodity, currency, TRUE);
|
||||||
if (!currency_hash)
|
if (!price_list) return NULL;
|
||||||
{
|
|
||||||
LEAVE (" no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
price_list = g_hash_table_lookup(currency_hash, currency);
|
|
||||||
if (!price_list)
|
|
||||||
{
|
|
||||||
LEAVE (" no price list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This works magically because prices are inserted in date-sorted
|
/* This works magically because prices are inserted in date-sorted
|
||||||
* order, and the latest date always comes first. So return the
|
* order, and the latest date always comes first. So return the
|
||||||
* first in the list. */
|
* first in the list. */
|
||||||
@ -1439,46 +1491,227 @@ lookup_latest(gpointer key, gpointer val, gpointer user_data)
|
|||||||
gnc_price_list_insert(return_list, price_list->data, FALSE);
|
gnc_price_list_insert(return_list, price_list->data, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GList **list;
|
||||||
|
const gnc_commodity *com;
|
||||||
|
} UsesCommodity;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
price_uses_commodity(GNCPrice *price, gpointer data)
|
||||||
|
{
|
||||||
|
UsesCommodity *helper = (UsesCommodity*)data;
|
||||||
|
gnc_commodity *com = gnc_price_get_commodity(price);
|
||||||
|
gnc_commodity *cur = gnc_price_get_currency(price);
|
||||||
|
if (helper->com == com || helper->com == cur)
|
||||||
|
{
|
||||||
|
gnc_price_ref(price);
|
||||||
|
*helper->list = g_list_prepend(*helper->list, price);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
is_in_list (GList *list, const gnc_commodity *c)
|
||||||
|
{
|
||||||
|
GList *node;
|
||||||
|
for (node = list; node != NULL; node = g_list_next(node))
|
||||||
|
if ((gnc_commodity*)node->data == c)
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This operates on the principal that the prices are sorted by date and that we
|
||||||
|
* want only the first one before the specified time containing both the target
|
||||||
|
* and some other commodity. */
|
||||||
|
static PriceList*
|
||||||
|
latest_before (PriceList *prices, const gnc_commodity* target, Timespec t)
|
||||||
|
{
|
||||||
|
GList *node, *found_coms = NULL, *retval = NULL;
|
||||||
|
for (node = prices; node != NULL; node = g_list_next(node))
|
||||||
|
{
|
||||||
|
GNCPrice *price = (GNCPrice*)node->data;
|
||||||
|
gnc_commodity *com = gnc_price_get_commodity(price);
|
||||||
|
gnc_commodity *cur = gnc_price_get_currency(price);
|
||||||
|
Timespec price_t = gnc_price_get_time(price);
|
||||||
|
if (timespec_cmp(&t, &price_t) <= 0 ||
|
||||||
|
(com == target && is_in_list(found_coms, cur)) ||
|
||||||
|
(cur == target && is_in_list(found_coms, com)))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gnc_price_ref(price);
|
||||||
|
retval = g_list_prepend(retval, price);
|
||||||
|
found_coms = g_list_prepend(found_coms, com == target ? cur : com);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GNCPrice**
|
||||||
|
find_comtime(GPtrArray* array, gnc_commodity *com)
|
||||||
|
{
|
||||||
|
unsigned int index = 0;
|
||||||
|
GNCPrice** retval = NULL;
|
||||||
|
for (index = 0; index < array->len; ++index)
|
||||||
|
{
|
||||||
|
GNCPrice **price_p = g_ptr_array_index(array, index);
|
||||||
|
if (gnc_price_get_commodity(*price_p) == com ||
|
||||||
|
gnc_price_get_currency(*price_p) == com)
|
||||||
|
retval = price_p;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList*
|
||||||
|
add_nearest_price(GList *target_list, GPtrArray *price_array, GNCPrice *price,
|
||||||
|
const gnc_commodity *target, Timespec t)
|
||||||
|
{
|
||||||
|
gnc_commodity *com = gnc_price_get_commodity(price);
|
||||||
|
gnc_commodity *cur = gnc_price_get_currency(price);
|
||||||
|
Timespec price_t = gnc_price_get_time(price);
|
||||||
|
gnc_commodity *other = com == target ? cur : com;
|
||||||
|
GNCPrice **com_price = find_comtime(price_array, other);
|
||||||
|
Timespec com_t;
|
||||||
|
if (com_price == NULL)
|
||||||
|
{
|
||||||
|
com_price = (GNCPrice**)g_slice_new(gpointer);
|
||||||
|
*com_price = price;
|
||||||
|
g_ptr_array_add(price_array, com_price);
|
||||||
|
return target_list;
|
||||||
|
}
|
||||||
|
com_t = gnc_price_get_time(*com_price);
|
||||||
|
if (timespec_cmp(&com_t, &t) <= 0)
|
||||||
|
/* No point in checking any more prices, they'll all be further from
|
||||||
|
* t. */
|
||||||
|
return target_list;
|
||||||
|
if (timespec_cmp(&price_t, &t) > 0)
|
||||||
|
/* The price list is sorted newest->oldest, so as long as this price
|
||||||
|
* is newer than t then it should replace the saved one. */
|
||||||
|
{
|
||||||
|
*com_price = price;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Timespec com_diff = timespec_diff(&com_t, &t);
|
||||||
|
Timespec price_diff = timespec_diff(&t, &price_t);
|
||||||
|
if (timespec_cmp(&com_diff, &price_diff) < 0)
|
||||||
|
{
|
||||||
|
gnc_price_ref(*com_price);
|
||||||
|
target_list = g_list_prepend(target_list, *com_price);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gnc_price_ref(price);
|
||||||
|
target_list = g_list_prepend(target_list, price);
|
||||||
|
}
|
||||||
|
*com_price = price;
|
||||||
|
}
|
||||||
|
return target_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PriceList *
|
||||||
|
nearest_to (PriceList *prices, const gnc_commodity* target, Timespec t)
|
||||||
|
{
|
||||||
|
GList *node, *retval = NULL;
|
||||||
|
const guint prealloc_size = 5; /*More than 5 "other" is unlikely as long as
|
||||||
|
* target isn't the book's default
|
||||||
|
* currency. */
|
||||||
|
|
||||||
|
|
||||||
|
GPtrArray *price_array = g_ptr_array_sized_new(prealloc_size);
|
||||||
|
guint index;
|
||||||
|
for (node = prices; node != NULL; node = g_list_next(node))
|
||||||
|
{
|
||||||
|
GNCPrice *price = (GNCPrice*)node->data;
|
||||||
|
retval = add_nearest_price(retval, price_array, price, target, t);
|
||||||
|
}
|
||||||
|
/* There might be some prices in price_array that are newer than t. Those
|
||||||
|
* will be cases where there wasn't a price older than t to push one or the
|
||||||
|
* other into the retval, so we need to get them now.
|
||||||
|
*/
|
||||||
|
for (index = 0; index < price_array->len; ++index)
|
||||||
|
{
|
||||||
|
GNCPrice **com_price = g_ptr_array_index(price_array, index);
|
||||||
|
Timespec price_t = gnc_price_get_time(*com_price);
|
||||||
|
if (timespec_cmp(&price_t, &t) >= 0)
|
||||||
|
{
|
||||||
|
gnc_price_ref(*com_price);
|
||||||
|
retval = g_list_prepend(retval, *com_price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_ptr_array_free(price_array, TRUE);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PriceList *
|
PriceList *
|
||||||
gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db,
|
gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db,
|
||||||
const gnc_commodity *commodity)
|
const gnc_commodity *commodity)
|
||||||
{
|
{
|
||||||
GList *result;
|
GList *prices = NULL, *result;
|
||||||
GHashTable *currency_hash;
|
UsesCommodity helper = {&prices, commodity};
|
||||||
|
|
||||||
result = NULL;
|
result = NULL;
|
||||||
|
|
||||||
if (!db || !commodity) return NULL;
|
if (!db || !commodity) return NULL;
|
||||||
ENTER ("db=%p commodity=%p", db, commodity);
|
ENTER ("db=%p commodity=%p", db, commodity);
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
|
|
||||||
if (!currency_hash)
|
|
||||||
{
|
|
||||||
LEAVE (" no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_hash_table_foreach(currency_hash, lookup_latest, &result);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
LEAVE (" ");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = g_list_sort(result, compare_prices_by_date);
|
|
||||||
|
|
||||||
|
gnc_pricedb_foreach_price(db, price_uses_commodity,
|
||||||
|
&helper, FALSE);
|
||||||
|
prices = g_list_sort(prices, compare_prices_by_date);
|
||||||
|
result = latest_before(prices, commodity, timespec_now());
|
||||||
|
gnc_price_list_destroy(prices);
|
||||||
LEAVE(" ");
|
LEAVE(" ");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PriceList *
|
||||||
static void
|
gnc_pricedb_lookup_nearest_in_time_any_currency(GNCPriceDB *db,
|
||||||
hash_values_helper(gpointer key, gpointer value, gpointer data)
|
const gnc_commodity *commodity,
|
||||||
|
Timespec t)
|
||||||
{
|
{
|
||||||
GList ** l = data;
|
GList *prices = NULL, *result;
|
||||||
*l = g_list_concat(*l, g_list_copy (value));
|
UsesCommodity helper = {&prices, commodity};
|
||||||
|
result = NULL;
|
||||||
|
|
||||||
|
if (!db || !commodity) return NULL;
|
||||||
|
ENTER ("db=%p commodity=%p", db, commodity);
|
||||||
|
|
||||||
|
gnc_pricedb_foreach_price(db, price_uses_commodity,
|
||||||
|
&helper, FALSE);
|
||||||
|
prices = g_list_sort(prices, compare_prices_by_date);
|
||||||
|
result = nearest_to(prices, commodity, t);
|
||||||
|
gnc_price_list_destroy(prices);
|
||||||
|
LEAVE(" ");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PriceList *
|
||||||
|
gnc_pricedb_lookup_latest_before_any_currency(GNCPriceDB *db,
|
||||||
|
const gnc_commodity *commodity,
|
||||||
|
Timespec t)
|
||||||
|
{
|
||||||
|
GList *prices = NULL, *result;
|
||||||
|
UsesCommodity helper = {&prices, commodity};
|
||||||
|
result = NULL;
|
||||||
|
|
||||||
|
if (!db || !commodity) return NULL;
|
||||||
|
ENTER ("db=%p commodity=%p", db, commodity);
|
||||||
|
|
||||||
|
gnc_pricedb_foreach_price(db, price_uses_commodity,
|
||||||
|
&helper, FALSE);
|
||||||
|
prices = g_list_sort(prices, compare_prices_by_date);
|
||||||
|
result = latest_before(prices, commodity, t);
|
||||||
|
gnc_price_list_destroy(prices);
|
||||||
|
LEAVE(" ");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gnc_pricedb_has_prices is used explicitly for filtering cases where the
|
||||||
|
* commodity is the left side of commodity->currency price, so it checks only in
|
||||||
|
* that direction.
|
||||||
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
gnc_pricedb_has_prices(GNCPriceDB *db,
|
gnc_pricedb_has_prices(GNCPriceDB *db,
|
||||||
const gnc_commodity *commodity,
|
const gnc_commodity *commodity,
|
||||||
@ -1515,41 +1748,22 @@ gnc_pricedb_has_prices(GNCPriceDB *db,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* gnc_pricedb_get_prices is used to construct the tree in the Price Editor and
|
||||||
|
* so needs to be single-direction.
|
||||||
|
*/
|
||||||
PriceList *
|
PriceList *
|
||||||
gnc_pricedb_get_prices(GNCPriceDB *db,
|
gnc_pricedb_get_prices(GNCPriceDB *db,
|
||||||
const gnc_commodity *commodity,
|
const gnc_commodity *commodity,
|
||||||
const gnc_commodity *currency)
|
const gnc_commodity *currency)
|
||||||
{
|
{
|
||||||
GList *price_list;
|
|
||||||
GList *result;
|
GList *result;
|
||||||
GList *node;
|
GList *node;
|
||||||
GHashTable *currency_hash;
|
|
||||||
|
|
||||||
if (!db || !commodity) return NULL;
|
if (!db || !commodity) return NULL;
|
||||||
ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
|
ENTER ("db=%p commodity=%p currency=%p", db, commodity, currency);
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
|
result = pricedb_get_prices_internal (db, commodity, currency, FALSE);
|
||||||
if (!currency_hash)
|
if (!result) return NULL;
|
||||||
{
|
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
|
|
||||||
LEAVE (" no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currency)
|
|
||||||
{
|
|
||||||
price_list = g_hash_table_lookup(currency_hash, currency);
|
|
||||||
if (!price_list)
|
|
||||||
{
|
|
||||||
LEAVE (" no price list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
result = g_list_copy (price_list);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = NULL;
|
|
||||||
g_hash_table_foreach(currency_hash, hash_values_helper, (gpointer)&result);
|
|
||||||
}
|
|
||||||
for (node = result; node; node = node->next)
|
for (node = result; node; node = node->next)
|
||||||
gnc_price_ref (node->data);
|
gnc_price_ref (node->data);
|
||||||
|
|
||||||
@ -1567,33 +1781,19 @@ gnc_pricedb_lookup_day(GNCPriceDB *db,
|
|||||||
return lookup_nearest_in_time(db, c, currency, t, TRUE);
|
return lookup_nearest_in_time(db, c, currency, t, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
PriceList *
|
GNCPrice *
|
||||||
gnc_pricedb_lookup_at_time(GNCPriceDB *db,
|
gnc_pricedb_lookup_at_time(GNCPriceDB *db,
|
||||||
const gnc_commodity *c,
|
const gnc_commodity *c,
|
||||||
const gnc_commodity *currency,
|
const gnc_commodity *currency,
|
||||||
Timespec t)
|
Timespec t)
|
||||||
{
|
{
|
||||||
GList *price_list;
|
GList *price_list;
|
||||||
GList *result = NULL;
|
|
||||||
GList *item = NULL;
|
GList *item = NULL;
|
||||||
GHashTable *currency_hash;
|
GHashTable *currency_hash;
|
||||||
|
|
||||||
if (!db || !c || !currency) return NULL;
|
if (!db || !c || !currency) return NULL;
|
||||||
ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
|
ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
|
price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
|
||||||
if (!currency_hash)
|
|
||||||
{
|
|
||||||
LEAVE (" no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
price_list = g_hash_table_lookup(currency_hash, currency);
|
|
||||||
if (!price_list)
|
|
||||||
{
|
|
||||||
LEAVE (" no price list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = price_list;
|
item = price_list;
|
||||||
while (item)
|
while (item)
|
||||||
{
|
{
|
||||||
@ -1601,13 +1801,13 @@ gnc_pricedb_lookup_at_time(GNCPriceDB *db,
|
|||||||
Timespec price_time = gnc_price_get_time(p);
|
Timespec price_time = gnc_price_get_time(p);
|
||||||
if (timespec_equal(&price_time, &t))
|
if (timespec_equal(&price_time, &t))
|
||||||
{
|
{
|
||||||
result = g_list_prepend(result, p);
|
|
||||||
gnc_price_ref(p);
|
gnc_price_ref(p);
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
item = item->next;
|
item = item->next;
|
||||||
}
|
}
|
||||||
LEAVE (" ");
|
LEAVE (" ");
|
||||||
return result;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GNCPrice *
|
static GNCPrice *
|
||||||
@ -1626,20 +1826,8 @@ lookup_nearest_in_time(GNCPriceDB *db,
|
|||||||
|
|
||||||
if (!db || !c || !currency) return NULL;
|
if (!db || !c || !currency) return NULL;
|
||||||
ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
|
ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
|
price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
|
||||||
if (!currency_hash)
|
if (!price_list) return NULL;
|
||||||
{
|
|
||||||
LEAVE ("no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
price_list = g_hash_table_lookup(currency_hash, currency);
|
|
||||||
if (!price_list)
|
|
||||||
{
|
|
||||||
LEAVE ("no price list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = price_list;
|
item = price_list;
|
||||||
|
|
||||||
/* default answer */
|
/* default answer */
|
||||||
@ -1759,20 +1947,8 @@ gnc_pricedb_lookup_latest_before (GNCPriceDB *db,
|
|||||||
|
|
||||||
if (!db || !c || !currency) return NULL;
|
if (!db || !c || !currency) return NULL;
|
||||||
ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
|
ENTER ("db=%p commodity=%p currency=%p", db, c, currency);
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
|
price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
|
||||||
if (!currency_hash)
|
if (!price_list) return NULL;
|
||||||
{
|
|
||||||
LEAVE ("no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
price_list = g_hash_table_lookup(currency_hash, currency);
|
|
||||||
if (!price_list)
|
|
||||||
{
|
|
||||||
LEAVE ("no price list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = price_list;
|
item = price_list;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -1787,168 +1963,143 @@ gnc_pricedb_lookup_latest_before (GNCPriceDB *db,
|
|||||||
return current_price;
|
return current_price;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gnc_numeric
|
||||||
static void
|
direct_balance_conversion (GNCPriceDB *db, gnc_numeric bal,
|
||||||
lookup_nearest(gpointer key, gpointer val, gpointer user_data)
|
const gnc_commodity *from, const gnc_commodity *to,
|
||||||
|
Timespec *t)
|
||||||
{
|
{
|
||||||
//gnc_commodity *currency = (gnc_commodity *)key;
|
GNCPrice *price;
|
||||||
GList *price_list = (GList *)val;
|
gnc_numeric retval = gnc_numeric_zero();
|
||||||
GNCPrice *current_price = NULL;
|
if (from == NULL || to == NULL)
|
||||||
GNCPrice *next_price = NULL;
|
return retval;
|
||||||
GNCPrice *result = NULL;
|
if (gnc_numeric_zero_p(bal))
|
||||||
GList *item = NULL;
|
return retval;
|
||||||
GNCPriceLookupHelper *lookup_helper = (GNCPriceLookupHelper *)user_data;
|
if (t != NULL)
|
||||||
GList **return_list = lookup_helper->return_list;
|
price = gnc_pricedb_lookup_nearest_in_time(db, from, to, *t);
|
||||||
Timespec t = lookup_helper->time;
|
else
|
||||||
|
price = gnc_pricedb_lookup_latest(db, from, to);
|
||||||
|
if (price == NULL)
|
||||||
|
return retval;
|
||||||
|
if (gnc_price_get_commodity(price) == from)
|
||||||
|
retval = gnc_numeric_mul (bal, gnc_price_get_value (price),
|
||||||
|
gnc_commodity_get_fraction (to),
|
||||||
|
GNC_HOW_RND_ROUND);
|
||||||
|
else
|
||||||
|
retval = gnc_numeric_div (bal, gnc_price_get_value (price),
|
||||||
|
gnc_commodity_get_fraction (to),
|
||||||
|
GNC_HOW_RND_ROUND);
|
||||||
|
gnc_price_unref (price);
|
||||||
|
return retval;
|
||||||
|
|
||||||
item = price_list;
|
}
|
||||||
|
|
||||||
/* default answer */
|
typedef struct
|
||||||
current_price = item->data;
|
{
|
||||||
|
GNCPrice *from;
|
||||||
|
GNCPrice *to;
|
||||||
|
} PriceTuple;
|
||||||
|
|
||||||
/* find the first candidate past the one we want. Remember that
|
static PriceTuple
|
||||||
prices are in most-recent-first order. */
|
extract_common_prices (PriceList *from_prices, PriceList *to_prices)
|
||||||
while (!next_price && item)
|
{
|
||||||
|
PriceTuple retval = {NULL, NULL};
|
||||||
|
GList *from_node = NULL, *to_node = NULL;
|
||||||
|
GNCPrice *from_price, *to_price;
|
||||||
|
|
||||||
|
for (from_node = from_prices; from_node != NULL;
|
||||||
|
from_node = g_list_next(from_node))
|
||||||
{
|
{
|
||||||
GNCPrice *p = item->data;
|
for (to_node = to_prices; to_node != NULL;
|
||||||
Timespec price_time = gnc_price_get_time(p);
|
to_node = g_list_next(to_price))
|
||||||
if (timespec_cmp(&price_time, &t) <= 0)
|
|
||||||
{
|
{
|
||||||
next_price = item->data;
|
gnc_commodity *to_com, *to_cur;
|
||||||
|
gnc_commodity *from_com, *from_cur;
|
||||||
|
to_price = GNC_PRICE(to_node->data);
|
||||||
|
from_price = GNC_PRICE(from_node->data);
|
||||||
|
to_com = gnc_price_get_commodity (to_price);
|
||||||
|
to_cur = gnc_price_get_currency (to_price);
|
||||||
|
from_com = gnc_price_get_commodity (from_price);
|
||||||
|
from_cur = gnc_price_get_currency (from_price);
|
||||||
|
if (to_com == from_com || to_com == from_cur ||
|
||||||
|
to_cur == from_com || to_cur == from_cur)
|
||||||
|
break;
|
||||||
|
to_price = NULL;
|
||||||
|
from_price = NULL;
|
||||||
|
}
|
||||||
|
if (to_price != NULL && from_price != NULL)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
current_price = item->data;
|
if (from_price == NULL || to_price == NULL)
|
||||||
item = item->next;
|
return retval;
|
||||||
}
|
gnc_price_ref(from_price);
|
||||||
|
gnc_price_ref(to_price);
|
||||||
|
retval.from = from_price;
|
||||||
|
retval.to = to_price;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
if (current_price)
|
static gnc_numeric
|
||||||
|
convert_balance(gnc_numeric bal, const gnc_commodity *from,
|
||||||
|
const gnc_commodity *to, PriceTuple tuple)
|
||||||
|
{
|
||||||
|
gnc_commodity *from_com = gnc_price_get_commodity(tuple.from);
|
||||||
|
gnc_commodity *from_cur = gnc_price_get_currency(tuple.from);
|
||||||
|
gnc_commodity *to_com = gnc_price_get_commodity(tuple.to);
|
||||||
|
gnc_commodity *to_cur = gnc_price_get_currency(tuple.to);
|
||||||
|
gnc_numeric from_val = gnc_price_get_value(tuple.from);
|
||||||
|
gnc_numeric to_val = gnc_price_get_value(tuple.to);
|
||||||
|
int fraction = gnc_commodity_get_fraction(to);
|
||||||
|
|
||||||
|
int no_round = GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER;
|
||||||
|
if (from_cur == from && to_cur == to)
|
||||||
|
return gnc_numeric_div(gnc_numeric_mul(bal, to_val, GNC_DENOM_AUTO,
|
||||||
|
no_round),
|
||||||
|
from_val, fraction, GNC_HOW_RND_ROUND);
|
||||||
|
if (from_com == from && to_com == to)
|
||||||
|
return gnc_numeric_div(gnc_numeric_mul(bal, from_val, GNC_DENOM_AUTO,
|
||||||
|
no_round),
|
||||||
|
to_val, fraction, GNC_HOW_RND_ROUND);
|
||||||
|
if (from_cur == from)
|
||||||
|
return gnc_numeric_div(bal, gnc_numeric_mul(from_val, to_val,
|
||||||
|
GNC_DENOM_AUTO, no_round),
|
||||||
|
fraction, GNC_HOW_RND_ROUND);
|
||||||
|
return gnc_numeric_mul(bal, gnc_numeric_mul(from_val, to_val,
|
||||||
|
GNC_DENOM_AUTO, no_round),
|
||||||
|
fraction, GNC_HOW_RND_ROUND);
|
||||||
|
|
||||||
|
}
|
||||||
|
static gnc_numeric
|
||||||
|
indirect_balance_conversion (GNCPriceDB *db, gnc_numeric bal,
|
||||||
|
const gnc_commodity *from, const gnc_commodity *to,
|
||||||
|
Timespec *t )
|
||||||
|
{
|
||||||
|
GList *from_prices = NULL, *to_prices = NULL;
|
||||||
|
PriceTuple tuple;
|
||||||
|
gnc_numeric zero = gnc_numeric_zero();
|
||||||
|
if (from == NULL || to == NULL)
|
||||||
|
return zero;
|
||||||
|
if (gnc_numeric_zero_p(bal))
|
||||||
|
return zero;
|
||||||
|
if (t == NULL)
|
||||||
{
|
{
|
||||||
if (!next_price)
|
from_prices = gnc_pricedb_lookup_latest_any_currency(db, from);
|
||||||
{
|
to_prices = gnc_pricedb_lookup_latest_any_currency(db, to);
|
||||||
result = current_price;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Timespec current_t = gnc_price_get_time(current_price);
|
from_prices = gnc_pricedb_lookup_nearest_in_time_any_currency(db,
|
||||||
Timespec next_t = gnc_price_get_time(next_price);
|
from, *t);
|
||||||
Timespec diff_current = timespec_diff(¤t_t, &t);
|
to_prices = gnc_pricedb_lookup_nearest_in_time_any_currency(db,
|
||||||
Timespec diff_next = timespec_diff(&next_t, &t);
|
to, *t);
|
||||||
Timespec abs_current = timespec_abs(&diff_current);
|
|
||||||
Timespec abs_next = timespec_abs(&diff_next);
|
|
||||||
|
|
||||||
if (timespec_cmp(&abs_current, &abs_next) <= 0)
|
|
||||||
{
|
|
||||||
result = current_price;
|
|
||||||
}
|
}
|
||||||
else
|
if (from_prices == NULL || to_prices == NULL)
|
||||||
{
|
return zero;
|
||||||
result = next_price;
|
tuple = extract_common_prices(from_prices, to_prices);
|
||||||
}
|
gnc_price_list_destroy(from_prices);
|
||||||
}
|
gnc_price_list_destroy(to_prices);
|
||||||
}
|
if (tuple.from)
|
||||||
|
return convert_balance(bal, from, to, tuple);
|
||||||
gnc_price_list_insert(return_list, result, FALSE);
|
return zero;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
lookup_latest_before(gpointer key, gpointer val, gpointer user_data)
|
|
||||||
{
|
|
||||||
//gnc_commodity *currency = (gnc_commodity *)key;
|
|
||||||
GList *price_list = (GList *)val;
|
|
||||||
GNCPrice *current_price = NULL;
|
|
||||||
/* GNCPrice *next_price = NULL;
|
|
||||||
GNCPrice *result = NULL;*/
|
|
||||||
GList *item = NULL;
|
|
||||||
GNCPriceLookupHelper *lookup_helper = (GNCPriceLookupHelper *)user_data;
|
|
||||||
GList **return_list = lookup_helper->return_list;
|
|
||||||
Timespec t = lookup_helper->time;
|
|
||||||
Timespec price_time;
|
|
||||||
|
|
||||||
if (price_list)
|
|
||||||
{
|
|
||||||
item = price_list;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
price_time = gnc_price_get_time (item->data);
|
|
||||||
if (timespec_cmp(&price_time, &t) <= 0)
|
|
||||||
current_price = item->data;
|
|
||||||
item = item->next;
|
|
||||||
}
|
|
||||||
while (timespec_cmp(&price_time, &t) > 0 && item);
|
|
||||||
}
|
|
||||||
|
|
||||||
gnc_price_list_insert(return_list, current_price, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PriceList *
|
|
||||||
gnc_pricedb_lookup_nearest_in_time_any_currency(GNCPriceDB *db,
|
|
||||||
const gnc_commodity *c,
|
|
||||||
Timespec t)
|
|
||||||
{
|
|
||||||
GList *result = NULL;
|
|
||||||
GHashTable *currency_hash;
|
|
||||||
GNCPriceLookupHelper lookup_helper;
|
|
||||||
|
|
||||||
if (!db || !c) return NULL;
|
|
||||||
ENTER ("db=%p commodity=%p", db, c);
|
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
|
|
||||||
if (!currency_hash)
|
|
||||||
{
|
|
||||||
LEAVE (" no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
lookup_helper.return_list = &result;
|
|
||||||
lookup_helper.time = t;
|
|
||||||
g_hash_table_foreach(currency_hash, lookup_nearest, &lookup_helper);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
LEAVE (" ");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = g_list_sort(result, compare_prices_by_date);
|
|
||||||
|
|
||||||
LEAVE (" ");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PriceList *
|
|
||||||
gnc_pricedb_lookup_latest_before_any_currency(GNCPriceDB *db,
|
|
||||||
gnc_commodity *c,
|
|
||||||
Timespec t)
|
|
||||||
{
|
|
||||||
GList *result = NULL;
|
|
||||||
GHashTable *currency_hash;
|
|
||||||
GNCPriceLookupHelper lookup_helper;
|
|
||||||
|
|
||||||
if (!db || !c) return NULL;
|
|
||||||
ENTER ("db=%p commodity=%p", db, c);
|
|
||||||
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
|
|
||||||
if (!currency_hash)
|
|
||||||
{
|
|
||||||
LEAVE (" no currency hash");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
lookup_helper.return_list = &result;
|
|
||||||
lookup_helper.time = t;
|
|
||||||
g_hash_table_foreach(currency_hash, lookup_latest_before, &lookup_helper);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
LEAVE (" ");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = g_list_sort(result, compare_prices_by_date);
|
|
||||||
|
|
||||||
LEAVE (" ");
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1961,94 +2112,24 @@ gnc_pricedb_convert_balance_latest_price(GNCPriceDB *pdb,
|
|||||||
const gnc_commodity *balance_currency,
|
const gnc_commodity *balance_currency,
|
||||||
const gnc_commodity *new_currency)
|
const gnc_commodity *new_currency)
|
||||||
{
|
{
|
||||||
GNCPrice *price, *currency_price;
|
gnc_numeric new_value;
|
||||||
GList *price_list, *list_helper;
|
|
||||||
gnc_numeric currency_price_value;
|
|
||||||
gnc_commodity *intermediate_currency;
|
|
||||||
|
|
||||||
if (gnc_numeric_zero_p (balance) ||
|
if (gnc_numeric_zero_p (balance) ||
|
||||||
gnc_commodity_equiv (balance_currency, new_currency))
|
gnc_commodity_equiv (balance_currency, new_currency))
|
||||||
return balance;
|
return balance;
|
||||||
|
|
||||||
/* Look for a direct price. */
|
/* Look for a direct price. */
|
||||||
price = gnc_pricedb_lookup_latest (pdb, balance_currency, new_currency);
|
new_value = direct_balance_conversion(pdb, balance, balance_currency,
|
||||||
if (price)
|
new_currency, NULL);
|
||||||
{
|
if (!gnc_numeric_zero_p(new_value))
|
||||||
balance = gnc_numeric_mul (balance, gnc_price_get_value (price),
|
return new_value;
|
||||||
gnc_commodity_get_fraction (new_currency),
|
|
||||||
GNC_HOW_RND_ROUND);
|
|
||||||
gnc_price_unref (price);
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Look for a price of the new currency in the balance currency and use
|
|
||||||
* the reciprocal if we find it
|
|
||||||
*/
|
|
||||||
price = gnc_pricedb_lookup_latest (pdb, new_currency, balance_currency);
|
|
||||||
if (price)
|
|
||||||
{
|
|
||||||
balance = gnc_numeric_div (balance, gnc_price_get_value (price),
|
|
||||||
gnc_commodity_get_fraction (new_currency),
|
|
||||||
GNC_HOW_RND_ROUND);
|
|
||||||
gnc_price_unref (price);
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* no direct price found, try if we find a price in another currency
|
* no direct price found, try if we find a price in another currency
|
||||||
* and convert in two stages
|
* and convert in two stages
|
||||||
*/
|
*/
|
||||||
price_list = gnc_pricedb_lookup_latest_any_currency(pdb, balance_currency);
|
return indirect_balance_conversion(pdb, balance, balance_currency,
|
||||||
if (!price_list)
|
new_currency, NULL);
|
||||||
{
|
|
||||||
balance = gnc_numeric_zero ();
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_helper = price_list;
|
|
||||||
currency_price_value = gnc_numeric_zero();
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
price = (GNCPrice *)(list_helper->data);
|
|
||||||
|
|
||||||
intermediate_currency = gnc_price_get_currency(price);
|
|
||||||
currency_price = gnc_pricedb_lookup_latest(pdb, intermediate_currency,
|
|
||||||
new_currency);
|
|
||||||
if (currency_price)
|
|
||||||
{
|
|
||||||
currency_price_value = gnc_price_get_value(currency_price);
|
|
||||||
gnc_price_unref(currency_price);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currency_price = gnc_pricedb_lookup_latest(pdb, new_currency,
|
|
||||||
intermediate_currency);
|
|
||||||
if (currency_price)
|
|
||||||
{
|
|
||||||
/* here we need the reciprocal */
|
|
||||||
currency_price_value = gnc_numeric_div(gnc_numeric_create(1, 1),
|
|
||||||
gnc_price_get_value(currency_price),
|
|
||||||
GNC_DENOM_AUTO,
|
|
||||||
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
||||||
gnc_price_unref(currency_price);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list_helper = list_helper->next;
|
|
||||||
}
|
|
||||||
while ((list_helper != NULL) &&
|
|
||||||
(gnc_numeric_zero_p(currency_price_value)));
|
|
||||||
|
|
||||||
balance = gnc_numeric_mul (balance, currency_price_value,
|
|
||||||
GNC_DENOM_AUTO,
|
|
||||||
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
||||||
balance = gnc_numeric_mul (balance, gnc_price_get_value (price),
|
|
||||||
gnc_commodity_get_fraction (new_currency),
|
|
||||||
GNC_HOW_RND_ROUND);
|
|
||||||
|
|
||||||
gnc_price_list_destroy(price_list);
|
|
||||||
return balance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gnc_numeric
|
gnc_numeric
|
||||||
@ -2058,95 +2139,24 @@ gnc_pricedb_convert_balance_nearest_price(GNCPriceDB *pdb,
|
|||||||
const gnc_commodity *new_currency,
|
const gnc_commodity *new_currency,
|
||||||
Timespec t)
|
Timespec t)
|
||||||
{
|
{
|
||||||
GNCPrice *price, *currency_price;
|
gnc_numeric new_value;
|
||||||
GList *price_list, *list_helper;
|
|
||||||
gnc_numeric currency_price_value;
|
|
||||||
gnc_commodity *intermediate_currency;
|
|
||||||
|
|
||||||
if (gnc_numeric_zero_p (balance) ||
|
if (gnc_numeric_zero_p (balance) ||
|
||||||
gnc_commodity_equiv (balance_currency, new_currency))
|
gnc_commodity_equiv (balance_currency, new_currency))
|
||||||
return balance;
|
return balance;
|
||||||
|
|
||||||
/* Look for a direct price. */
|
/* Look for a direct price. */
|
||||||
price = gnc_pricedb_lookup_nearest_in_time (pdb, balance_currency, new_currency, t);
|
new_value = direct_balance_conversion(pdb, balance, balance_currency,
|
||||||
if (price)
|
new_currency, &t);
|
||||||
{
|
if (!gnc_numeric_zero_p(new_value))
|
||||||
balance = gnc_numeric_mul (balance, gnc_price_get_value (price),
|
return new_value;
|
||||||
gnc_commodity_get_fraction (new_currency),
|
|
||||||
GNC_HOW_RND_ROUND);
|
|
||||||
gnc_price_unref (price);
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Look for a price of the new currency in the balance currency and use
|
|
||||||
* the reciprocal if we find it
|
|
||||||
*/
|
|
||||||
price = gnc_pricedb_lookup_nearest_in_time (pdb, new_currency, balance_currency, t);
|
|
||||||
if (price)
|
|
||||||
{
|
|
||||||
balance = gnc_numeric_div (balance, gnc_price_get_value (price),
|
|
||||||
gnc_commodity_get_fraction (new_currency),
|
|
||||||
GNC_HOW_RND_ROUND);
|
|
||||||
gnc_price_unref (price);
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* no direct price found, try if we find a price in another currency
|
* no direct price found, try if we find a price in another currency
|
||||||
* and convert in two stages
|
* and convert in two stages
|
||||||
*/
|
*/
|
||||||
price_list = gnc_pricedb_lookup_nearest_in_time_any_currency(pdb, balance_currency, t);
|
return indirect_balance_conversion(pdb, balance, balance_currency,
|
||||||
if (!price_list)
|
new_currency, &t);
|
||||||
{
|
|
||||||
balance = gnc_numeric_zero ();
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_helper = price_list;
|
|
||||||
currency_price_value = gnc_numeric_zero();
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
price = (GNCPrice *)(list_helper->data);
|
|
||||||
|
|
||||||
intermediate_currency = gnc_price_get_currency(price);
|
|
||||||
currency_price = gnc_pricedb_lookup_nearest_in_time(pdb, intermediate_currency,
|
|
||||||
new_currency, t);
|
|
||||||
if (currency_price)
|
|
||||||
{
|
|
||||||
currency_price_value = gnc_price_get_value(currency_price);
|
|
||||||
gnc_price_unref(currency_price);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currency_price = gnc_pricedb_lookup_nearest_in_time(pdb, new_currency,
|
|
||||||
intermediate_currency, t);
|
|
||||||
if (currency_price)
|
|
||||||
{
|
|
||||||
/* here we need the reciprocal */
|
|
||||||
currency_price_value = gnc_numeric_div(gnc_numeric_create(1, 1),
|
|
||||||
gnc_price_get_value(currency_price),
|
|
||||||
GNC_DENOM_AUTO,
|
|
||||||
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
||||||
gnc_price_unref(currency_price);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list_helper = list_helper->next;
|
|
||||||
}
|
|
||||||
while ((list_helper != NULL) &&
|
|
||||||
(gnc_numeric_zero_p(currency_price_value)));
|
|
||||||
|
|
||||||
balance = gnc_numeric_mul (balance, currency_price_value,
|
|
||||||
GNC_DENOM_AUTO,
|
|
||||||
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
||||||
|
|
||||||
balance = gnc_numeric_mul (balance, gnc_price_get_value (price),
|
|
||||||
gnc_commodity_get_fraction (new_currency),
|
|
||||||
GNC_HOW_RND_ROUND);
|
|
||||||
|
|
||||||
gnc_price_list_destroy(price_list);
|
|
||||||
return balance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2288,7 +2298,7 @@ stable_price_traversal(GNCPriceDB *db,
|
|||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gnc_pricedb_foreach_price(GNCPriceDB *db,
|
gnc_pricedb_foreach_price(GNCPriceDB *db,
|
||||||
gboolean (*f)(GNCPrice *p, gpointer user_data),
|
GncPriceForeachFunc f,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
gboolean stable_order)
|
gboolean stable_order)
|
||||||
{
|
{
|
||||||
|
@ -518,7 +518,7 @@ GNCPrice * gnc_pricedb_lookup_latest_before(GNCPriceDB *db,
|
|||||||
*/
|
*/
|
||||||
/* NOT USED, but see bug 743753 */
|
/* NOT USED, but see bug 743753 */
|
||||||
PriceList * gnc_pricedb_lookup_latest_before_any_currency(GNCPriceDB *db,
|
PriceList * gnc_pricedb_lookup_latest_before_any_currency(GNCPriceDB *db,
|
||||||
gnc_commodity *c,
|
const gnc_commodity *c,
|
||||||
Timespec t);
|
Timespec t);
|
||||||
|
|
||||||
|
|
||||||
@ -554,7 +554,7 @@ gnc_pricedb_convert_balance_nearest_price(GNCPriceDB *pdb,
|
|||||||
const gnc_commodity *new_currency,
|
const gnc_commodity *new_currency,
|
||||||
Timespec t);
|
Timespec t);
|
||||||
|
|
||||||
typedef gboolean (*f)(GNCPrice *p, gpointer user_data) GncPriceForeachFunction;
|
typedef gboolean (*GncPriceForeachFunc)(GNCPrice *p, gpointer user_data);
|
||||||
|
|
||||||
/** @brief Call a GncPriceForeachFunction once for each price in db, until the
|
/** @brief Call a GncPriceForeachFunction once for each price in db, until the
|
||||||
* function returns FALSE.
|
* function returns FALSE.
|
||||||
@ -571,7 +571,7 @@ typedef gboolean (*f)(GNCPrice *p, gpointer user_data) GncPriceForeachFunction;
|
|||||||
* processing was the same as the previous invocation (stable), FALSE otherwise.
|
* processing was the same as the previous invocation (stable), FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
gboolean gnc_pricedb_foreach_price(GNCPriceDB *db,
|
gboolean gnc_pricedb_foreach_price(GNCPriceDB *db,
|
||||||
GncPriceForeachFunction *f,
|
GncPriceForeachFunc f,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
gboolean stable_order);
|
gboolean stable_order);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user