32 #include "gnc-pricedb-p.h" 33 #include <qofinstance-p.h> 36 static QofLogModule log_module = GNC_MOD_PRICE;
38 static gboolean add_price(GNCPriceDB *db, GNCPrice *p);
39 static gboolean remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup);
40 static GNCPrice *lookup_nearest_in_time(GNCPriceDB *db,
const gnc_commodity *c,
41 const gnc_commodity *currency,
42 time64 t, gboolean sameday);
44 pricedb_pricelist_traversal(GNCPriceDB *db,
45 gboolean (*f)(GList *p, gpointer user_data),
63 return a < b ? -1 : a > b ? 1 : 0;
66 using CommodityPtrPair = std::pair<const gnc_commodity*, gpointer>;
67 using CommodityPtrPairVec = std::vector<CommodityPtrPair>;
70 hash_entry_insert(
const gnc_commodity* key,
const gpointer val, CommodityPtrPairVec *result)
72 result->emplace_back (key, val);
75 static CommodityPtrPairVec
76 hash_table_to_vector (GHashTable *
table)
78 CommodityPtrPairVec result_vec;
79 result_vec.reserve (g_hash_table_size (
table));
80 g_hash_table_foreach(
table, (GHFunc)hash_entry_insert, &result_vec);
85 G_DEFINE_TYPE(GNCPrice, gnc_price, QOF_TYPE_INSTANCE)
88 gnc_price_init(GNCPrice* price)
91 price->value = gnc_numeric_zero();
92 price->type =
nullptr;
93 price->source = PRICE_SOURCE_INVALID;
103 static const char* source_names[(size_t)PRICE_SOURCE_INVALID + 1] =
111 "user:split-register",
114 "user:stock-transaction",
121 gnc_price_dispose(GObject *pricep)
123 G_OBJECT_CLASS(gnc_price_parent_class)->dispose(pricep);
127 gnc_price_finalize(GObject* pricep)
129 G_OBJECT_CLASS(gnc_price_parent_class)->finalize(pricep);
139 gnc_price_get_property(GObject*
object, guint prop_id, GValue* value, GParamSpec* pspec)
143 g_return_if_fail(GNC_IS_PRICE(
object));
145 price = GNC_PRICE(
object);
149 g_value_set_string(value, gnc_price_get_source_string(price));
152 g_value_set_string(value, price->type);
155 g_value_set_boxed(value, &price->value);
158 g_value_take_object(value, price->commodity);
161 g_value_take_object(value, price->currency);
164 g_value_set_boxed(value, &price->tmspec);
167 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
173 gnc_price_set_property(GObject*
object, guint prop_id,
const GValue* value, GParamSpec* pspec)
179 g_return_if_fail(GNC_IS_PRICE(
object));
181 price = GNC_PRICE(
object);
182 g_assert (qof_instance_get_editlevel(price));
187 gnc_price_set_source_string(price, g_value_get_string(value));
190 gnc_price_set_typestr(price, g_value_get_string(value));
193 number =
static_cast<gnc_numeric*
>(g_value_get_boxed(value));
194 gnc_price_set_value(price, *number);
197 gnc_price_set_commodity(price, static_cast<gnc_commodity*>(g_value_get_object(value)));
200 gnc_price_set_currency(price, static_cast<gnc_commodity*>(g_value_get_object(value)));
203 time =
static_cast<Time64*
>(g_value_get_boxed(value));
204 gnc_price_set_time64(price, time->t);
207 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
213 gnc_price_class_init(GNCPriceClass *klass)
215 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
217 gobject_class->dispose = gnc_price_dispose;
218 gobject_class->finalize = gnc_price_finalize;
219 gobject_class->set_property = gnc_price_set_property;
220 gobject_class->get_property = gnc_price_get_property;
222 g_object_class_install_property
225 g_param_spec_object (
"commodity",
227 "The commodity field denotes the base kind of " 228 "'stuff' for the units of this quote, whether " 229 "it is USD, gold, stock, etc.",
233 g_object_class_install_property
236 g_param_spec_object (
"currency",
238 "The currency field denotes the external kind " 239 "'stuff' for the units of this quote, whether " 240 "it is USD, gold, stock, etc.",
244 g_object_class_install_property
247 g_param_spec_string (
"source",
249 "The price source is PriceSource enum describing how" 250 " the price was created. This property works on the" 251 " string values in source_names for SQL database" 256 g_object_class_install_property
259 g_param_spec_string (
"type",
261 "The quote type is a string describing the " 262 "type of a price quote. Types possible now " 263 "are 'bid', 'ask', 'last', 'nav', 'transaction', " 268 g_object_class_install_property
271 g_param_spec_boxed(
"date",
273 "The date of the price quote.",
277 g_object_class_install_property
280 g_param_spec_boxed(
"value",
282 "The value of the price quote.",
297 g_return_val_if_fail (book,
nullptr);
300 p =
static_cast<GNCPrice*
>(g_object_new(GNC_TYPE_PRICE,
nullptr));
304 LEAVE (
"price created %p", p);
309 gnc_price_destroy (GNCPrice *p)
311 ENTER(
"destroy price %p", p);
314 if (p->type) CACHE_REMOVE(p->type);
332 if (p->refcount == 0)
339 if (p->refcount <= 0)
341 if (
nullptr != p->db)
343 PERR(
"last unref while price in database");
345 gnc_price_destroy (p);
357 g_return_val_if_fail (book,
nullptr);
363 LEAVE (
"return nullptr");
370 LEAVE (
"return nullptr");
374 qof_instance_copy_version(new_p, p);
376 gnc_price_begin_edit(new_p);
378 gnc_price_set_commodity(new_p, gnc_price_get_commodity(p));
379 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
380 gnc_price_set_source(new_p, gnc_price_get_source(p));
381 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
382 gnc_price_set_value(new_p, gnc_price_get_value(p));
383 gnc_price_set_currency(new_p, gnc_price_get_currency(p));
384 gnc_price_commit_edit(new_p);
385 LEAVE (
"return cloned price %p", new_p);
394 qof_instance_copy_version(new_p, p);
395 gnc_price_begin_edit(new_p);
396 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
397 gnc_price_set_source(new_p, PRICE_SOURCE_TEMP);
398 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
399 gnc_price_set_commodity(new_p, gnc_price_get_currency(p));
400 gnc_price_set_currency(new_p, gnc_price_get_commodity(p));
402 gnc_price_commit_edit(new_p);
409 gnc_price_begin_edit (GNCPrice *p)
416 PERR (
"Failed to commit: %d", errcode);
417 gnc_engine_signal_commit_error( errcode );
423 gnc_price_commit_edit (GNCPrice *p)
448 gnc_price_set_dirty (GNCPrice *p)
450 qof_instance_set_dirty(&p->inst);
455 gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c)
465 remove_price (p->db, p, TRUE);
466 gnc_price_begin_edit (p);
468 gnc_price_set_dirty(p);
469 gnc_price_commit_edit (p);
470 add_price (p->db, p);
477 gnc_price_set_currency(GNCPrice *p, gnc_commodity *c)
487 remove_price (p->db, p, TRUE);
488 gnc_price_begin_edit (p);
490 gnc_price_set_dirty(p);
491 gnc_price_commit_edit (p);
492 add_price (p->db, p);
498 gnc_price_set_time64(GNCPrice *p,
time64 t)
507 remove_price (p->db, p, FALSE);
508 gnc_price_begin_edit (p);
510 gnc_price_set_dirty(p);
511 gnc_price_commit_edit (p);
512 add_price (p->db, p);
521 gnc_price_begin_edit (p);
523 gnc_price_set_dirty(p);
524 gnc_price_commit_edit(p);
528 gnc_price_set_source_string(GNCPrice *p,
const char* str)
533 if (strcmp(source_names[s], str) == 0)
535 gnc_price_set_source(p, s);
542 gnc_price_set_typestr(GNCPrice *p,
const char* type)
545 if (g_strcmp0(p->type, type) != 0)
547 gnc_price_begin_edit (p);
548 CACHE_REPLACE(p->type, type);
549 gnc_price_set_dirty(p);
550 gnc_price_commit_edit (p);
555 gnc_price_set_value(GNCPrice *p, gnc_numeric value)
560 gnc_price_begin_edit (p);
562 gnc_price_set_dirty(p);
563 gnc_price_commit_edit (p);
571 gnc_price_lookup (
const GncGUID *guid, QofBook *book)
575 if (!guid || !book)
return nullptr;
581 gnc_price_get_commodity(
const GNCPrice *p)
583 if (!p)
return nullptr;
588 gnc_price_get_time64(
const GNCPrice *p)
590 return p ? p->tmspec : 0;
594 gnc_price_get_source(
const GNCPrice *p)
596 if (!p)
return PRICE_SOURCE_INVALID;
601 gnc_price_get_source_string(
const GNCPrice *p)
603 if (!p)
return nullptr;
604 return source_names[p->source];
608 gnc_price_get_typestr(
const GNCPrice *p)
610 if (!p)
return nullptr;
615 gnc_price_get_value(
const GNCPrice *p)
619 PERR(
"price nullptr.\n");
620 return gnc_numeric_zero();
626 gnc_price_get_currency(
const GNCPrice *p)
628 if (!p)
return nullptr;
633 gnc_price_equal (
const GNCPrice *p1,
const GNCPrice *p2)
637 if (p1 == p2)
return TRUE;
638 if (!p1 || !p2)
return FALSE;
641 gnc_price_get_commodity (p2)))
645 gnc_price_get_currency (p2)))
648 time1 = gnc_price_get_time64 (p1);
649 time2 = gnc_price_get_time64 (p2);
654 if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
657 if (g_strcmp0 (gnc_price_get_typestr (p1),
658 gnc_price_get_typestr (p2)) != 0)
662 gnc_price_get_value (p2)))
672 compare_prices_by_date(gconstpointer a, gconstpointer b)
677 if (!a && !b)
return 0;
681 time_a = gnc_price_get_time64((GNCPrice *) a);
682 time_b = gnc_price_get_time64((GNCPrice *) b);
685 result = time64_cmp(time_b, time_a);
686 if (result)
return result;
689 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
690 gnc_price_get_guid((GNCPrice *) b));
694 price_is_duplicate (
const GNCPrice *p_price,
const GNCPrice *c_price)
706 if (!prices || !p)
return FALSE;
709 if (check_dupl && g_list_find_custom (*prices, p, (GCompareFunc)price_is_duplicate))
712 auto result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
716 *prices = result_list;
724 GList *found_element;
726 if (!prices || !p)
return FALSE;
728 found_element = g_list_find(*prices, p);
729 if (!found_element)
return TRUE;
731 result_list = g_list_remove_link(*prices, found_element);
733 g_list_free(found_element);
735 *prices = result_list;
746 gnc_price_list_equal(PriceList *prices1, PriceList *prices2)
748 if (prices1 == prices2)
return TRUE;
750 for (
auto n1 = prices1, n2 = prices2; n1 || n2; n1 = g_list_next (n1), n2 = g_list_next (n2))
754 PINFO (
"prices2 has extra prices");
759 PINFO (
"prices1 has extra prices");
762 if (!gnc_price_equal (static_cast<GNCPrice*>(n1->data), static_cast<GNCPrice*>(n2->data)))
781 QOF_GOBJECT_IMPL(gnc_pricedb, GNCPriceDB, QOF_TYPE_INSTANCE)
784 gnc_pricedb_init(GNCPriceDB* pdb)
786 pdb->reset_nth_price_cache = FALSE;
790 gnc_pricedb_dispose_real (GObject *pdbp)
795 gnc_pricedb_finalize_real(GObject* pdbp)
800 gnc_pricedb_create(QofBook * book)
805 g_return_val_if_fail (book,
nullptr);
814 PWARN (
"A price database already exists for this book!");
818 result =
static_cast<GNCPriceDB*
>(g_object_new(GNC_TYPE_PRICEDB,
nullptr));
825 qof_collection_set_data (col, result);
827 result->commodity_hash = g_hash_table_new(
nullptr,
nullptr);
828 g_return_val_if_fail (result->commodity_hash,
nullptr);
833 destroy_pricedb_currency_hash_data(gpointer key,
837 GList *price_list = (GList *) data;
841 for (node = price_list; node; node = node->next)
843 p =
static_cast<GNCPrice*
>(node->data);
852 destroy_pricedb_commodity_hash_data(gpointer key,
856 GHashTable *currency_hash = (GHashTable *) data;
857 if (!currency_hash)
return;
858 g_hash_table_foreach (currency_hash,
859 destroy_pricedb_currency_hash_data,
861 g_hash_table_destroy(currency_hash);
868 if (db->commodity_hash)
870 g_hash_table_foreach (db->commodity_hash,
871 destroy_pricedb_commodity_hash_data,
874 g_hash_table_destroy (db->commodity_hash);
875 db->commodity_hash =
nullptr;
883 db->bulk_update = bulk_update;
904 if (!col)
return nullptr;
913 if (!book)
return nullptr;
921 num_prices_helper (GNCPrice *p, gpointer user_data)
923 auto count =
static_cast<guint*
>(user_data);
950 gnc_commodity *commodity;
954 pricedb_equal_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
957 auto currency =
static_cast<gnc_commodity*
>(key);
958 auto price_list1 =
static_cast<GList*
>(val);
960 equal_data->commodity,
963 if (!gnc_price_list_equal (price_list1, price_list2))
964 equal_data->equal = FALSE;
970 pricedb_equal_foreach_currencies_hash (gpointer key, gpointer val,
973 auto currencies_hash =
static_cast<GHashTable*
>(val);
976 equal_data->commodity =
static_cast<gnc_commodity*
>(key);
978 g_hash_table_foreach (currencies_hash,
979 pricedb_equal_foreach_pricelist,
988 if (db1 == db2)
return TRUE;
992 PWARN (
"one is nullptr");
996 equal_data.equal = TRUE;
997 equal_data.db2 = db2;
999 g_hash_table_foreach (db1->commodity_hash,
1000 pricedb_equal_foreach_currencies_hash,
1003 return equal_data.equal;
1011 add_price(GNCPriceDB *db, GNCPrice *p)
1016 gnc_commodity *commodity;
1017 gnc_commodity *currency;
1018 GHashTable *currency_hash;
1020 if (!db || !p)
return FALSE;
1021 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1027 PERR (
"attempted to mix up prices across different books");
1032 commodity = gnc_price_get_commodity(p);
1035 PWARN(
"no commodity");
1039 currency = gnc_price_get_currency(p);
1042 PWARN(
"no currency");
1046 if (!db->commodity_hash)
1048 LEAVE (
"no commodity hash found ");
1055 if (!db->bulk_update)
1058 p->currency, p->tmspec);
1059 if (old_price !=
nullptr)
1061 if (p->source > old_price->source)
1064 LEAVE (
"Better price already in DB.");
1071 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1074 currency_hash = g_hash_table_new(
nullptr,
nullptr);
1075 g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
1078 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1081 LEAVE (
"gnc_price_list_insert failed");
1087 LEAVE (
" no price list");
1091 g_hash_table_insert(currency_hash, currency, price_list);
1096 LEAVE (
"db=%p, pr=%p dirty=%d dextroying=%d commodity=%s/%s currency_hash=%p",
1112 if (!db || !p)
return FALSE;
1114 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1118 if (FALSE == add_price(db, p))
1120 LEAVE (
" failed to add price");
1125 qof_instance_set_dirty(&db->inst);
1128 LEAVE (
"db=%p, pr=%p dirty=%d destroying=%d",
1140 remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup)
1143 gnc_commodity *commodity;
1144 gnc_commodity *currency;
1145 GHashTable *currency_hash;
1147 if (!db || !p)
return FALSE;
1148 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1152 commodity = gnc_price_get_commodity(p);
1155 LEAVE (
" no commodity");
1158 currency = gnc_price_get_currency(p);
1161 LEAVE (
" no currency");
1164 if (!db->commodity_hash)
1166 LEAVE (
" no commodity hash");
1170 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1173 LEAVE (
" no currency hash");
1178 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1183 LEAVE (
" cannot remove price list");
1191 g_hash_table_insert(currency_hash, currency, price_list);
1195 g_hash_table_remove(currency_hash, currency);
1202 guint num_currencies = g_hash_table_size (currency_hash);
1203 if (0 == num_currencies)
1205 g_hash_table_remove (db->commodity_hash, commodity);
1206 g_hash_table_destroy (currency_hash);
1212 LEAVE (
"db=%p, pr=%p", db, p);
1221 memset(datebuff, 0,
sizeof(datebuff));
1222 if (!db || !p)
return FALSE;
1223 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1229 DEBUG(
"Remove Date is %s, Commodity is %s, Source is %s", datebuff,
1231 gnc_price_get_source_string (p));
1233 rc = remove_price (db, p, TRUE);
1235 qof_instance_set_dirty(&db->inst);
1239 gnc_price_begin_edit (p);
1240 qof_instance_set_destroying(p, TRUE);
1241 gnc_price_commit_edit (p);
1244 LEAVE (
"db=%p, pr=%p", db, p);
1253 gboolean delete_user;
1254 gboolean delete_app;
1259 check_one_price_date (GNCPrice *price, gpointer user_data)
1265 ENTER(
"price %p (%s), data %p", price,
1269 source = gnc_price_get_source (price);
1271 if ((source == PRICE_SOURCE_FQ) && data->delete_fq)
1272 PINFO (
"Delete Quote Source");
1273 else if ((source == PRICE_SOURCE_USER_PRICE) && data->delete_user)
1274 PINFO (
"Delete User Source");
1275 else if ((source != PRICE_SOURCE_FQ) && (source != PRICE_SOURCE_USER_PRICE) && data->delete_app)
1276 PINFO (
"Delete App Source");
1279 LEAVE(
"Not a matching source");
1283 time = gnc_price_get_time64 (price);
1287 DEBUG(
"checking date %s", buf);
1289 if (time < data->cutoff)
1291 data->list = g_slist_prepend(data->list, price);
1292 DEBUG(
"will delete");
1299 pricedb_remove_foreach_pricelist (gpointer key,
1303 GList *price_list = (GList *) val;
1304 GList *node = price_list;
1307 ENTER(
"key %p, value %p, data %p", key, val, user_data);
1310 g_list_foreach(node, (GFunc)check_one_price_date, data);
1316 compare_prices_by_commodity_date (gconstpointer a, gconstpointer b)
1319 gnc_commodity *comma;
1320 gnc_commodity *commb;
1321 gnc_commodity *curra;
1322 gnc_commodity *currb;
1325 if (!a && !b)
return 0;
1330 comma = gnc_price_get_commodity ((GNCPrice *) a);
1331 commb = gnc_price_get_commodity ((GNCPrice *) b);
1336 curra = gnc_price_get_currency ((GNCPrice *) a);
1337 currb = gnc_price_get_currency ((GNCPrice *) b);
1342 time_a = gnc_price_get_time64((GNCPrice *) a);
1343 time_b = gnc_price_get_time64((GNCPrice *) b);
1346 result = time64_cmp(time_b, time_a);
1347 if (result)
return result;
1350 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
1351 gnc_price_get_guid((GNCPrice *) b));
1355 price_commodity_and_currency_equal (GNCPrice *a, GNCPrice *b)
1357 gboolean ret_comm = FALSE;
1358 gboolean ret_curr = FALSE;
1366 return (ret_comm && ret_curr);
1370 gnc_pricedb_remove_old_prices_pinfo (GNCPrice *price, gboolean keep_message)
1375 if (g_date_valid (&price_date))
1381 PINFO(
"#### Keep price with date %s, commodity is %s, currency is %s", date_buf,
1386 PINFO(
"## Remove price with date %s", date_buf);
1389 PINFO(
"Keep price date is invalid");
1393 clone_price (GNCPrice **price, GNCPrice *source_price)
1397 if (!source_price)
return;
1398 if (price ==
nullptr)
return;
1407 gnc_pricedb_remove_old_prices_pinfo (source_price, TRUE);
1411 roundUp (gint numToRound, gint multiple)
1418 remainder = numToRound % multiple;
1422 return numToRound + multiple - remainder;
1426 get_fiscal_quarter (GDate *date, GDateMonth fiscal_start)
1428 GDateMonth month = g_date_get_month (date);
1430 gint q = ((roundUp (22 - fiscal_start + month, 3)/3) % 4) + 1;
1432 PINFO(
"Return fiscal quarter is %d", q);
1437 gnc_pricedb_process_removal_list (GNCPriceDB *db, GDate *fiscal_end_date,
1441 gboolean save_first_price = FALSE;
1442 gint saved_test_value = 0, next_test_value = 0;
1443 GNCPrice *cloned_price =
nullptr;
1444 GDateMonth fiscal_month_start;
1445 GDate *tmp_date = g_date_new_dmy (g_date_get_day (fiscal_end_date),
1446 g_date_get_month (fiscal_end_date),
1447 g_date_get_year (fiscal_end_date));
1450 g_date_subtract_months (tmp_date, 12);
1451 fiscal_month_start =
static_cast<GDateMonth
>(g_date_get_month (tmp_date) + 1);
1452 g_date_free (tmp_date);
1455 data.list = g_slist_sort (data.list, compare_prices_by_commodity_date);
1458 for (item = data.list; item; item = g_slist_next(item))
1460 GDate saved_price_date;
1461 GDate next_price_date;
1462 auto price =
static_cast<GNCPrice*
>(item->data);
1465 if (keep == PRICE_REMOVE_KEEP_NONE)
1467 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1472 save_first_price = !price_commodity_and_currency_equal (price, cloned_price);
1473 if (save_first_price == TRUE)
1475 clone_price (&cloned_price, price);
1480 saved_price_date =
time64_to_gdate (gnc_price_get_time64 (cloned_price));
1484 if (keep == PRICE_REMOVE_KEEP_LAST_PERIOD && save_first_price == FALSE)
1486 GDate *saved_fiscal_end = g_date_new_dmy (g_date_get_day (&saved_price_date),
1487 g_date_get_month (&saved_price_date),
1488 g_date_get_year (&saved_price_date));
1490 GDate *next_fiscal_end = g_date_new_dmy (g_date_get_day (&next_price_date),
1491 g_date_get_month (&next_price_date),
1492 g_date_get_year (&next_price_date));
1497 saved_test_value = g_date_get_year (saved_fiscal_end);
1498 next_test_value = g_date_get_year (next_fiscal_end);
1500 PINFO(
"Keep last price in fiscal year");
1502 g_date_free (saved_fiscal_end);
1503 g_date_free (next_fiscal_end);
1507 if (keep == PRICE_REMOVE_KEEP_LAST_QUARTERLY && save_first_price == FALSE)
1509 saved_test_value = get_fiscal_quarter (&saved_price_date, fiscal_month_start);
1510 next_test_value = get_fiscal_quarter (&next_price_date, fiscal_month_start);
1512 PINFO(
"Keep last price in fiscal quarter");
1516 if (keep == PRICE_REMOVE_KEEP_LAST_MONTHLY && save_first_price == FALSE)
1518 saved_test_value = g_date_get_month (&saved_price_date);
1519 next_test_value = g_date_get_month (&next_price_date);
1521 PINFO(
"Keep last price of every month");
1525 if (keep == PRICE_REMOVE_KEEP_LAST_WEEKLY && save_first_price == FALSE)
1527 saved_test_value = g_date_get_iso8601_week_of_year (&saved_price_date);
1528 next_test_value = g_date_get_iso8601_week_of_year (&next_price_date);
1530 PINFO(
"Keep last price of every week");
1534 if (saved_test_value == next_test_value)
1536 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1540 clone_price (&cloned_price, price);
1548 GDate *fiscal_end_date,
time64 cutoff,
1549 PriceRemoveSourceFlags source,
1550 PriceRemoveKeepOptions keep)
1555 memset (datebuff, 0,
sizeof(datebuff));
1558 data.cutoff = cutoff;
1559 data.list =
nullptr;
1560 data.delete_fq = FALSE;
1561 data.delete_user = FALSE;
1562 data.delete_app = FALSE;
1564 ENTER(
"Remove Prices for Source %d, keeping %d", source, keep);
1567 if (source & PRICE_REMOVE_SOURCE_APP)
1568 data.delete_app = TRUE;
1570 if (source & PRICE_REMOVE_SOURCE_FQ)
1571 data.delete_fq = TRUE;
1573 if (source & PRICE_REMOVE_SOURCE_USER)
1574 data.delete_user = TRUE;
1577 for (node = g_list_first (comm_list); node; node = g_list_next (node))
1579 auto currencies_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, node->data));
1580 g_hash_table_foreach (currencies_hash, pricedb_remove_foreach_pricelist, &data);
1583 if (data.list ==
nullptr)
1585 LEAVE(
"Empty price list");
1589 DEBUG(
"Number of Prices in list is %d, Cutoff date is %s",
1590 g_slist_length (data.list), datebuff);
1593 if (fiscal_end_date ==
nullptr)
1596 fiscal_end_date = g_date_new ();
1597 g_date_set_dmy (fiscal_end_date, 31, GDateMonth(12), year_now);
1599 else if (g_date_valid (fiscal_end_date) == FALSE)
1602 g_date_clear (fiscal_end_date, 1);
1603 g_date_set_dmy (fiscal_end_date, 31, GDateMonth(12), year_now);
1605 gnc_pricedb_process_removal_list (db, fiscal_end_date, data, keep);
1607 g_slist_free (data.list);
1615 static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b);
1618 hash_values_helper(gpointer key, gpointer value, gpointer data)
1620 auto l =
static_cast<GList**
>(data);
1624 new_l = pricedb_price_list_merge(*l, static_cast<PriceList*>(value));
1629 *l = g_list_copy (static_cast<GList*>(value));
1633 price_list_from_hashtable (GHashTable *hash,
const gnc_commodity *currency)
1635 GList *price_list =
nullptr, *result = nullptr ;
1638 price_list =
static_cast<GList*
>(g_hash_table_lookup(hash, currency));
1641 LEAVE (
" no price list");
1644 result = g_list_copy (price_list);
1648 g_hash_table_foreach(hash, hash_values_helper, (gpointer)&result);
1654 pricedb_price_list_merge (PriceList *a, PriceList *b)
1656 PriceList *merged_list =
nullptr;
1660 while (next_a || next_b)
1662 if (next_a ==
nullptr)
1664 merged_list = g_list_prepend (merged_list, next_b->data);
1665 next_b = next_b->next;
1667 else if (next_b ==
nullptr)
1669 merged_list = g_list_prepend (merged_list, next_a->data);
1670 next_a = next_a->next;
1673 else if (compare_prices_by_date (next_a->data, next_b->data) < 0)
1675 merged_list = g_list_prepend (merged_list, next_a->data);
1676 next_a = next_a->next;
1680 merged_list = g_list_prepend (merged_list, next_b->data);
1681 next_b = next_b->next;
1684 return g_list_reverse (merged_list);
1688 pricedb_get_prices_internal(GNCPriceDB *db,
const gnc_commodity *commodity,
1689 const gnc_commodity *currency, gboolean bidi)
1691 GHashTable *forward_hash =
nullptr, *reverse_hash =
nullptr;
1692 PriceList *forward_list =
nullptr, *reverse_list =
nullptr;
1693 g_return_val_if_fail (db !=
nullptr,
nullptr);
1694 g_return_val_if_fail (commodity !=
nullptr,
nullptr);
1695 forward_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1696 if (currency && bidi)
1697 reverse_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, currency));
1698 if (!forward_hash && !reverse_hash)
1700 LEAVE (
" no currency hash");
1704 forward_list = price_list_from_hashtable (forward_hash, currency);
1705 if (currency && reverse_hash)
1707 reverse_list = price_list_from_hashtable (reverse_hash, commodity);
1717 PriceList *merged_list;
1718 merged_list = pricedb_price_list_merge (forward_list, reverse_list);
1719 g_list_free (forward_list);
1720 g_list_free (reverse_list);
1721 forward_list = merged_list;
1725 forward_list = reverse_list;
1730 return forward_list;
1734 const gnc_commodity *commodity,
1735 const gnc_commodity *currency)
1740 if (!db || !commodity || !currency)
return nullptr;
1741 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
1743 price_list = pricedb_get_prices_internal(db, commodity, currency, TRUE);
1744 if (!price_list)
return nullptr;
1748 result =
static_cast<GNCPrice*
>(price_list->data);
1750 g_list_free (price_list);
1751 LEAVE(
"price is %p", result);
1758 const gnc_commodity *com;
1773 price_list_scan_any_currency(GList *price_list, gpointer data)
1782 auto price =
static_cast<GNCPrice*
>(price_list->data);
1783 com = gnc_price_get_commodity(price);
1784 cur = gnc_price_get_currency(price);
1788 if (com != helper->com && cur != helper->com)
1794 for (
auto node = price_list; node; node = g_list_next (node))
1796 price =
static_cast<GNCPrice*
>(node->data);
1797 time64 price_t = gnc_price_get_time64(price);
1798 if (price_t < helper->t)
1803 auto prev_price =
static_cast<GNCPrice*
>(node->prev->data);
1805 *helper->list = g_list_prepend(*helper->list, prev_price);
1809 *helper->list = g_list_prepend(*helper->list, price);
1813 else if (node->next ==
nullptr)
1817 *helper->list = g_list_prepend(*helper->list, price);
1828 latest_before (PriceList *prices,
const gnc_commodity* target,
time64 t)
1830 GList *node, *found_coms =
nullptr, *retval =
nullptr;
1831 for (node = prices; node !=
nullptr; node = g_list_next(node))
1833 GNCPrice *price = (GNCPrice*)node->data;
1834 gnc_commodity *com = gnc_price_get_commodity(price);
1835 gnc_commodity *cur = gnc_price_get_currency(price);
1836 time64 price_t = gnc_price_get_time64(price);
1839 (com == target && g_list_find (found_coms, cur)) ||
1840 (cur == target && g_list_find (found_coms, com)))
1844 retval = g_list_prepend (retval, price);
1845 found_coms = g_list_prepend (found_coms, com == target ? cur : com);
1847 g_list_free (found_coms);
1848 return g_list_reverse(retval);
1852 find_comtime(GPtrArray* array, gnc_commodity *com)
1854 unsigned int index = 0;
1855 GNCPrice** retval =
nullptr;
1856 for (index = 0; index < array->len; ++index)
1858 auto price_p =
static_cast<GNCPrice**
>(g_ptr_array_index(array, index));
1859 if (gnc_price_get_commodity(*price_p) == com ||
1860 gnc_price_get_currency(*price_p) == com)
1867 add_nearest_price(GList *target_list, GPtrArray *price_array, GNCPrice *price,
1868 const gnc_commodity *target,
time64 t)
1870 gnc_commodity *com = gnc_price_get_commodity(price);
1871 gnc_commodity *cur = gnc_price_get_currency(price);
1872 time64 price_t = gnc_price_get_time64(price);
1873 gnc_commodity *other = com == target ? cur : com;
1874 GNCPrice **com_price = find_comtime(price_array, other);
1876 if (com_price ==
nullptr)
1878 com_price = (GNCPrice**)g_slice_new(gpointer);
1880 g_ptr_array_add(price_array, com_price);
1886 target_list = g_list_prepend(target_list, price);
1890 com_t = gnc_price_get_time64(*com_price);
1903 time64 com_diff = com_t - t;
1904 time64 price_diff = t - price_t;
1905 if (com_diff < price_diff)
1908 target_list = g_list_prepend(target_list, *com_price);
1913 target_list = g_list_prepend(target_list, price);
1921 nearest_to (PriceList *prices,
const gnc_commodity* target,
time64 t)
1923 GList *node, *retval =
nullptr;
1924 const guint prealloc_size = 5;
1929 GPtrArray *price_array = g_ptr_array_sized_new(prealloc_size);
1931 for (node = prices; node !=
nullptr; node = g_list_next(node))
1933 GNCPrice *price = (GNCPrice*)node->data;
1934 retval = add_nearest_price(retval, price_array, price, target, t);
1940 for (index = 0; index < price_array->len; ++index)
1942 auto com_price =
static_cast<GNCPrice**
>(g_ptr_array_index(price_array, index));
1943 time64 price_t = gnc_price_get_time64(*com_price);
1947 retval = g_list_prepend(retval, *com_price);
1950 g_ptr_array_free(price_array, TRUE);
1951 return g_list_sort(retval, compare_prices_by_date);
1958 const gnc_commodity *commodity)
1966 const gnc_commodity *commodity,
1969 GList *prices =
nullptr, *result;
1973 if (!db || !commodity)
return nullptr;
1974 ENTER (
"db=%p commodity=%p", db, commodity);
1976 pricedb_pricelist_traversal(db, price_list_scan_any_currency, &helper);
1977 prices = g_list_sort(prices, compare_prices_by_date);
1978 result = nearest_to(prices, commodity, t);
1986 const gnc_commodity *commodity,
1989 GList *prices =
nullptr, *result;
1993 if (!db || !commodity)
return nullptr;
1994 ENTER (
"db=%p commodity=%p", db, commodity);
1996 pricedb_pricelist_traversal(db, price_list_scan_any_currency,
1998 prices = g_list_sort(prices, compare_prices_by_date);
1999 result = latest_before(prices, commodity, t);
2011 const gnc_commodity *commodity,
2012 const gnc_commodity *currency)
2015 GHashTable *currency_hash;
2018 if (!db || !commodity)
return FALSE;
2019 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2020 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
2023 LEAVE(
"no, no currency_hash table");
2029 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
2035 LEAVE(
"no, no price list");
2039 size = g_hash_table_size (currency_hash);
2040 LEAVE(
"%s", size > 0 ?
"yes" :
"no");
2050 const gnc_commodity *commodity,
2051 const gnc_commodity *currency)
2053 if (!db || !commodity)
return nullptr;
2054 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2055 auto result = pricedb_get_prices_internal (db, commodity, currency, FALSE);
2056 if (!result)
return nullptr;
2065 price_count_helper(gpointer key, gpointer value, gpointer data)
2067 auto result =
static_cast<int*
>(data);
2068 auto price_list =
static_cast<GList*
>(value);
2070 *result += g_list_length(price_list);
2075 const gnc_commodity *c)
2078 GHashTable *currency_hash;
2080 if (!db || !c)
return 0;
2081 ENTER (
"db=%p commodity=%p", db, c);
2083 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, c));
2086 g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result);
2089 LEAVE (
"count=%d", result);
2095 list_combine (gpointer element, gpointer data)
2097 GList *list = *(GList**)data;
2098 auto lst =
static_cast<GList*
>(element);
2099 if (list ==
nullptr)
2100 *(GList**)data = g_list_copy (lst);
2103 GList *new_list = g_list_concat (list, g_list_copy (lst));
2104 *(GList**)data = new_list;
2122 const gnc_commodity *c,
2125 static const gnc_commodity *last_c =
nullptr;
2126 static GList *prices =
nullptr;
2128 GNCPrice *result =
nullptr;
2129 GHashTable *currency_hash;
2130 g_return_val_if_fail (GNC_IS_COMMODITY (c),
nullptr);
2132 if (!db || !c || n < 0)
return nullptr;
2135 if (last_c && prices && last_c == c && db->reset_nth_price_cache == FALSE)
2137 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2138 LEAVE (
"price=%p", result);
2146 g_list_free (prices);
2150 db->reset_nth_price_cache = FALSE;
2152 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, c));
2155 GList *currencies = g_hash_table_get_values (currency_hash);
2156 g_list_foreach (currencies, list_combine, &prices);
2157 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2158 g_list_free (currencies);
2161 LEAVE (
"price=%p", result);
2166 gnc_pricedb_nth_price_reset_cache (GNCPriceDB *db)
2169 db->reset_nth_price_cache = TRUE;
2174 const gnc_commodity *c,
2175 const gnc_commodity *currency,
2178 return lookup_nearest_in_time(db, c, currency, t, TRUE);
2182 lookup_nearest_in_time(GNCPriceDB *db,
2183 const gnc_commodity *c,
2184 const gnc_commodity *currency,
2189 GNCPrice *current_price =
nullptr;
2190 GNCPrice *next_price =
nullptr;
2191 GNCPrice *result =
nullptr;
2193 if (!db || !c || !currency)
return nullptr;
2194 if (t == INT64_MAX)
return nullptr;
2195 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2196 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2197 if (!price_list)
return nullptr;
2200 current_price =
static_cast<GNCPrice*
>(price_list->data);
2204 for (
auto item = price_list; item; item = g_list_next (item))
2206 auto p =
static_cast<GNCPrice*
>(item->data);
2207 time64 price_time = gnc_price_get_time64(p);
2208 if (price_time <= t)
2210 next_price =
static_cast<GNCPrice*
>(item->data);
2213 current_price =
static_cast<GNCPrice*
>(item->data);
2221 result = current_price;
2229 if (price_day != t_day)
2237 time64 current_t = gnc_price_get_time64(current_price);
2238 time64 next_t = gnc_price_get_time64(next_price);
2239 time64 diff_current = current_t - t;
2240 time64 diff_next = next_t - t;
2241 time64 abs_current = llabs(diff_current);
2242 time64 abs_next = llabs(diff_next);
2250 if (current_day == t_day)
2252 if (next_day == t_day)
2255 if (abs_current < abs_next)
2256 result = current_price;
2258 result = next_price;
2262 result = current_price;
2264 else if (next_day == t_day)
2266 result = next_price;
2273 if (abs_current < abs_next)
2275 result = current_price;
2279 result = next_price;
2286 g_list_free (price_list);
2293 const gnc_commodity *c,
2294 const gnc_commodity *currency,
2297 return lookup_nearest_in_time(db, c, currency, t, FALSE);
2301 static int price_time64_less_or_equal (GNCPrice *p,
time64 *time)
2303 return !(gnc_price_get_time64 (p) <= *time);
2308 const gnc_commodity *c,
2309 const gnc_commodity *currency,
2312 GNCPrice *current_price =
nullptr;
2313 if (!db || !c || !currency)
return nullptr;
2314 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2315 auto price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2316 if (!price_list)
return nullptr;
2317 auto p = g_list_find_custom (price_list, &t, (GCompareFunc)price_time64_less_or_equal);
2320 current_price = GNC_PRICE (p->data);
2323 g_list_free (price_list);
2325 return current_price;
2336 extract_common_prices (PriceList *from_prices, PriceList *to_prices,
2337 const gnc_commodity *from,
const gnc_commodity *to)
2340 GList *from_node =
nullptr, *to_node =
nullptr;
2341 GNCPrice *from_price =
nullptr, *to_price =
nullptr;
2343 for (from_node = from_prices; from_node !=
nullptr;
2344 from_node = g_list_next(from_node))
2346 for (to_node = to_prices; to_node !=
nullptr;
2347 to_node = g_list_next(to_node))
2349 gnc_commodity *to_com, *to_cur;
2350 gnc_commodity *from_com, *from_cur;
2351 to_price = GNC_PRICE(to_node->data);
2352 from_price = GNC_PRICE(from_node->data);
2353 to_com = gnc_price_get_commodity (to_price);
2354 to_cur = gnc_price_get_currency (to_price);
2355 from_com = gnc_price_get_commodity (from_price);
2356 from_cur = gnc_price_get_currency (from_price);
2357 if (((to_com == from_com || to_com == from_cur) &&
2358 (to_com != from && to_com != to)) ||
2359 ((to_cur == from_com || to_cur == from_cur) &&
2360 (to_cur != from && to_cur != to)))
2363 from_price =
nullptr;
2365 if (to_price !=
nullptr && from_price !=
nullptr)
2368 if (from_price ==
nullptr || to_price ==
nullptr)
2372 retval.from = from_price;
2373 retval.to = to_price;
2379 convert_price (
const gnc_commodity *from,
const gnc_commodity *to,
PriceTuple tuple)
2381 gnc_commodity *from_com = gnc_price_get_commodity (tuple.from);
2382 gnc_commodity *from_cur = gnc_price_get_currency (tuple.from);
2383 gnc_commodity *to_com = gnc_price_get_commodity (tuple.to);
2384 gnc_commodity *to_cur = gnc_price_get_currency (tuple.to);
2385 gnc_numeric from_val = gnc_price_get_value (tuple.from);
2386 gnc_numeric to_val = gnc_price_get_value (tuple.to);
2395 if (from_cur == from && to_cur == to)
2398 if (from_com == from && to_com == to)
2403 if (from_cur == from)
2410 indirect_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2411 const gnc_commodity *to,
time64 t, gboolean before_date)
2413 GList *from_prices =
nullptr, *to_prices =
nullptr;
2415 gnc_numeric zero = gnc_numeric_zero();
2426 else if (before_date)
2438 if (!from_prices || !to_prices)
2444 tuple = extract_common_prices (from_prices, to_prices, from, to);
2448 return convert_price (from, to, tuple);
2454 direct_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2455 const gnc_commodity *to,
time64 t, gboolean before_date)
2458 gnc_numeric retval = gnc_numeric_zero();
2460 if (!from || !to)
return retval;
2464 else if (before_date)
2469 if (!price)
return retval;
2471 retval = gnc_price_get_value (price);
2473 if (gnc_price_get_commodity (price) != from)
2481 get_nearest_price (GNCPriceDB *pdb,
2482 const gnc_commodity *orig_curr,
2483 const gnc_commodity *new_curr,
2490 return gnc_numeric_create (1, 1);
2493 price = direct_price_conversion (pdb, orig_curr, new_curr, t, before);
2499 price = indirect_price_conversion (pdb, orig_curr, new_curr, t, before);
2506 const gnc_commodity *orig_currency,
2507 const gnc_commodity *new_currency,
2510 return get_nearest_price (pdb, orig_currency, new_currency, t, TRUE);
2515 const gnc_commodity *orig_currency,
2516 const gnc_commodity *new_currency,
2519 return get_nearest_price (pdb, orig_currency, new_currency, t, FALSE);
2524 const gnc_commodity *orig_currency,
2525 const gnc_commodity *new_currency)
2527 return get_nearest_price (pdb, orig_currency, new_currency, INT64_MAX, FALSE);
2531 convert_amount_at_date (GNCPriceDB *pdb,
2533 const gnc_commodity *orig_currency,
2534 const gnc_commodity *new_currency,
2536 gboolean before_date)
2543 price = get_nearest_price (pdb, orig_currency, new_currency, t, before_date);
2547 return gnc_numeric_zero ();
2559 gnc_numeric balance,
2560 const gnc_commodity *balance_currency,
2561 const gnc_commodity *new_currency)
2563 return convert_amount_at_date
2564 (pdb, balance, balance_currency, new_currency, INT64_MAX, FALSE);
2569 gnc_numeric balance,
2570 const gnc_commodity *balance_currency,
2571 const gnc_commodity *new_currency,
2574 return convert_amount_at_date
2575 (pdb, balance, balance_currency, new_currency, t, FALSE);
2580 gnc_numeric balance,
2581 const gnc_commodity *balance_currency,
2582 const gnc_commodity *new_currency,
2585 return convert_amount_at_date
2586 (pdb, balance, balance_currency, new_currency, t, TRUE);
2596 gboolean (*func)(GNCPrice *p, gpointer user_data);
2601 pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2603 GList *price_list = (GList *) val;
2607 foreach_data->ok = g_list_find_custom (price_list, foreach_data->user_data, (GCompareFunc)foreach_data->func)
2612 pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2614 GHashTable *currencies_hash = (GHashTable *) val;
2615 g_hash_table_foreach(currencies_hash, pricedb_foreach_pricelist, user_data);
2619 unstable_price_traversal(GNCPriceDB *db,
2620 gboolean (*f)(GNCPrice *p, gpointer user_data),
2625 if (!db || !f)
return FALSE;
2626 foreach_data.ok = TRUE;
2627 foreach_data.func = f;
2628 foreach_data.user_data = user_data;
2629 if (db->commodity_hash ==
nullptr)
2633 g_hash_table_foreach(db->commodity_hash,
2634 pricedb_foreach_currencies_hash,
2637 return foreach_data.ok;
2644 gboolean (*func)(GList *p, gpointer user_data);
2649 pricedb_pricelist_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2651 GList *price_list = (GList *) val;
2653 if (foreach_data->ok)
2655 foreach_data->ok = foreach_data->func(price_list, foreach_data->user_data);
2660 pricedb_pricelist_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2662 GHashTable *currencies_hash = (GHashTable *) val;
2663 g_hash_table_foreach(currencies_hash, pricedb_pricelist_foreach_pricelist, user_data);
2667 pricedb_pricelist_traversal(GNCPriceDB *db,
2668 gboolean (*f)(GList *p, gpointer user_data),
2673 if (!db || !f)
return FALSE;
2674 foreach_data.ok = TRUE;
2675 foreach_data.func = f;
2676 foreach_data.user_data = user_data;
2677 if (db->commodity_hash ==
nullptr)
2681 g_hash_table_foreach(db->commodity_hash,
2682 pricedb_pricelist_foreach_currencies_hash,
2685 return foreach_data.ok;
2689 compare_hash_entries_by_commodity_key (
const CommodityPtrPair& he_a,
const CommodityPtrPair& he_b)
2691 auto ca = he_a.first;
2692 auto cb = he_b.first;
2694 if (ca == cb || !cb)
2703 return (cmp_result < 0);
2709 stable_price_traversal(GNCPriceDB *db,
2710 gboolean (*f)(GNCPrice *p, gpointer user_data),
2713 g_return_val_if_fail (db && f,
false);
2715 auto currency_hashes = hash_table_to_vector (db->commodity_hash);
2716 std::sort (currency_hashes.begin(), currency_hashes.end(), compare_hash_entries_by_commodity_key);
2718 for (
const auto& entry : currency_hashes)
2720 auto price_lists = hash_table_to_vector (static_cast<GHashTable*>(entry.second));
2721 std::sort (price_lists.begin(), price_lists.end(), compare_hash_entries_by_commodity_key);
2723 for (
const auto& pricelist_entry : price_lists)
2724 if (g_list_find_custom (static_cast<GList*>(pricelist_entry.second), user_data, (GCompareFunc)f))
2733 GncPriceForeachFunc f,
2735 gboolean stable_order)
2737 ENTER (
"db=%p f=%p", db, f);
2740 LEAVE (
" stable order found");
2741 return stable_price_traversal(db, f, user_data);
2743 LEAVE (
" use unstable order");
2744 return unstable_price_traversal(db, f, user_data);
2754 gnc_commodity *commodity;
2755 gnc_commodity *currency;
2756 gchar *istr =
nullptr;
2762 commodity = gnc_price_get_commodity(p);
2763 currency = gnc_price_get_currency(p);
2765 if (!commodity)
return;
2766 if (!currency)
return;
2768 istr = g_strnfill(indent,
' ');
2770 fprintf(f,
"%s<pdb:price>\n", istr);
2771 fprintf(f,
"%s <pdb:commodity pointer=%p>\n", istr, commodity);
2773 str = str ? str :
"(null)";
2774 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2776 str = str ? str :
"(null)";
2777 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2778 fprintf(f,
"%s </pdb:commodity>\n", istr);
2779 fprintf(f,
"%s <pdb:currency pointer=%p>\n", istr, currency);
2781 str = str ? str :
"(null)";
2782 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2784 str = str ? str :
"(null)";
2785 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2786 fprintf(f,
"%s </pdb:currency>\n", istr);
2787 str = source_names[gnc_price_get_source(p)];
2788 str = str ? str :
"invalid";
2789 fprintf(f,
"%s %s\n", istr, str);
2790 str = gnc_price_get_typestr(p);
2791 str = str ? str :
"(null)";
2792 fprintf(f,
"%s %s\n", istr, str);
2794 fprintf(f,
"%s</pdb:price>\n", istr);
2800 print_pricedb_adapter(GNCPrice *p, gpointer user_data)
2802 FILE *f = (FILE *) user_data;
2812 PERR(
"nullptr PriceDB\n");
2817 PERR(
"nullptr FILE*\n");
2821 fprintf(f,
"<gnc:pricedb>\n");
2823 fprintf(f,
"</gnc:pricedb>\n");
2830 pricedb_book_begin (QofBook *book)
2832 gnc_pricedb_create(book);
2836 pricedb_book_end (QofBook *book)
2844 qof_collection_set_data(col,
nullptr);
2849 price_create (QofBook *book)
2859 void (*func)(GNCPrice *p, gpointer user_data);
2865 void_pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2867 GList *price_list = (GList *) val;
2870 g_list_foreach (price_list, (GFunc)foreach_data->func, foreach_data->user_data);
2874 void_pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2876 GHashTable *currencies_hash = (GHashTable *) val;
2877 g_hash_table_foreach(currencies_hash, void_pricedb_foreach_pricelist, user_data);
2881 void_unstable_price_traversal(GNCPriceDB *db,
2882 void (*f)(GNCPrice *p, gpointer user_data),
2887 if (!db || !f)
return;
2888 foreach_data.func = f;
2889 foreach_data.user_data = user_data;
2891 g_hash_table_foreach(db->commodity_hash,
2892 void_pricedb_foreach_currencies_hash,
2902 void_unstable_price_traversal(db,
2903 (
void (*)(GNCPrice *, gpointer)) cb,
2909 #ifdef DUMP_FUNCTIONS 2911 static void price_list_dump(GList *price_list,
const char *tag);
2915 price_printable(gpointer obj)
2917 auto pr =
static_cast<GNCPrice*
>(obj);
2918 gnc_commodity *commodity;
2919 gnc_commodity *currency;
2920 static char buff[2048];
2925 #ifdef DUMP_FUNCTIONS 2929 price_list_dump(
nullptr,
"");
2935 commodity = gnc_price_get_commodity(pr);
2936 currency = gnc_price_get_currency(pr);
2938 g_snprintf (buff, 2048,
"%s %s / %s on %s", val,
2947 #ifdef DUMP_FUNCTIONS 2950 price_list_dump(GList *price_list,
const char *tag)
2954 printf(
"Price list %s\n", tag);
2955 for (node = price_list; node !=
nullptr; node = node->next)
2957 printf(
"%s\n", price_printable(node->data));
2969 static QofObject price_object_def =
2972 DI(.e_type = ) GNC_ID_PRICE,
2973 DI(.type_label = ) "Price",
2974 DI(.create = ) price_create,
2975 DI(.book_begin = )
nullptr,
2976 DI(.book_end = )
nullptr,
2979 DI(.foreach = ) price_foreach,
2980 DI(.printable = ) price_printable,
2981 DI(.version_cmp = )
nullptr,
2984 static QofObject pricedb_object_def =
2987 DI(.e_type = ) GNC_ID_PRICEDB,
2988 DI(.type_label = ) "PriceDB",
2989 DI(.create = )
nullptr,
2990 DI(.book_begin = ) pricedb_book_begin,
2991 DI(.book_end = ) pricedb_book_end,
2994 DI(.foreach = )
nullptr,
2995 DI(.printable = )
nullptr,
2996 DI(.version_cmp = )
nullptr,
3000 gnc_pricedb_register (
void)
3002 static QofParam params[] =
void gnc_price_list_destroy(PriceList *prices)
gnc_price_list_destroy - destroy the given price list, calling gnc_price_unref on all the prices incl...
GNCPrice * gnc_pricedb_lookup_day_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commodities on the indicated day.
Never round at all, and signal an error if there is a fractional result in a computation.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
GNCPrice * gnc_pricedb_nth_price(GNCPriceDB *db, const gnc_commodity *c, const int n)
Get the nth price for the given commodity in reverse date order.
Date and Time handling routines.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
void gnc_gdate_set_fiscal_year_end(GDate *date, const GDate *year_end)
This function modifies a GDate to set it to the last day of the fiscal year in which it falls...
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
#define PINFO(format, args...)
Print an informational note.
GNCPrice * gnc_price_invert(GNCPrice *p)
Return a newly-allocated price that's the inverse of the given price, p.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
int gnc_pricedb_num_prices(GNCPriceDB *db, const gnc_commodity *c)
Get the number of prices, in any currency, for a given commodity.
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
#define DEBUG(format, args...)
Print a debugging message.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
void(* QofInstanceForeachCB)(QofInstance *, gpointer user_data)
Callback type for qof_collection_foreach.
GNCPriceDB * gnc_collection_get_pricedb(QofCollection *col)
Return the pricedb via the Book's collection.
gboolean gnc_pricedb_equal(GNCPriceDB *db1, GNCPriceDB *db2)
Test equality of two pricedbs.
gnc_numeric gnc_pricedb_get_nearest_price(GNCPriceDB *pdb, const gnc_commodity *orig_currency, const gnc_commodity *new_currency, const time64 t)
Retrieve the price one currency to another using the price nearest to the given time.
gboolean gnc_pricedb_remove_old_prices(GNCPriceDB *db, GList *comm_list, GDate *fiscal_end_date, time64 cutoff, PriceRemoveSourceFlags source, PriceRemoveKeepOptions keep)
Remove and unref prices older than a certain time.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
GDate time64_to_gdate(time64 t)
Returns the GDate in which the time64 occurs.
gnc_numeric gnc_pricedb_get_latest_price(GNCPriceDB *pdb, const gnc_commodity *orig_currency, const gnc_commodity *new_currency)
Retrieve the price one currency to another using the latest price.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
gboolean gnc_price_list_insert(PriceList **prices, GNCPrice *p, gboolean check_dupl)
gnc_price_list_insert - insert a price into the given list, calling gnc_price_ref on it during the pr...
#define ENTER(format, args...)
Print a function entry debugging message.
gnc_numeric gnc_pricedb_convert_balance_nearest_before_price_t64(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency, time64 t)
Convert a balance from one currency to another using the price nearest to before the given time...
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gnc_numeric gnc_numeric_reduce(gnc_numeric in)
Return input after reducing it by Greater Common Factor (GCF) elimination.
gnc_numeric gnc_pricedb_convert_balance_latest_price(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency)
Convert a balance from one currency to another using the most recent price between the two...
#define PWARN(format, args...)
Log a warning.
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
void gnc_pricedb_destroy(GNCPriceDB *db)
Destroy the given pricedb and unref all of the prices it contains.
guint gnc_pricedb_get_num_prices(GNCPriceDB *db)
Return the number of prices in the database.
void gnc_price_print(GNCPrice *p, FILE *f, int indent)
This simple function can be useful for debugging the price code.
gnc_numeric gnc_pricedb_get_nearest_before_price(GNCPriceDB *pdb, const gnc_commodity *orig_currency, const gnc_commodity *new_currency, const time64 t)
Retrieve the price one currency to another using the price nearest to before the given time...
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
PriceList * gnc_pricedb_lookup_nearest_in_time_any_currency_t64(GNCPriceDB *db, const gnc_commodity *commodity, time64 t)
Return the price nearest in time to that given between the given commodity and every other...
void gnc_pricedb_begin_edit(GNCPriceDB *pdb)
Begin an edit.
PriceList * gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db, const gnc_commodity *commodity)
Find the most recent price between a commodity and all other commodities.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and ...
gboolean qof_instance_books_equal(gconstpointer ptr1, gconstpointer ptr2)
See if two QofInstances share the same book.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
GNCPrice * gnc_pricedb_lookup_nearest_in_time64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commoditiesz nearest to the given time.
gboolean gnc_pricedb_has_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Report whether the pricedb contains prices for one commodity in another.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the 'print' name for the specified commodity.
int gnc_commodity_compare(const gnc_commodity *a, const gnc_commodity *b)
This routine returns 0 if the two commodities are equal, 1 otherwise.
gboolean gnc_price_list_remove(PriceList **prices, GNCPrice *p)
gnc_price_list_remove - remove the price, p, from the given list, calling gnc_price_unref on it durin...
GNCPrice * gnc_price_clone(GNCPrice *p, QofBook *book)
gnc_price_clone - returns a newly allocated price that's a content-wise duplicate of the given price...
gnc_numeric gnc_pricedb_convert_balance_nearest_price_t64(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency, time64 t)
Convert a balance from one currency to another using the price nearest to the given time...
PriceSource
Price source enum.
#define LEAVE(format, args...)
Print a function exit debugging message.
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the 'unique' name for the specified commodity.
void gnc_pricedb_set_bulk_update(GNCPriceDB *db, gboolean bulk_update)
Set flag to indicate whether duplication checks should be performed.
time64 gnc_time(time64 *tbuf)
get the current time
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
gboolean gnc_pricedb_foreach_price(GNCPriceDB *db, GncPriceForeachFunc f, gpointer user_data, gboolean stable_order)
Call a GncPriceForeachFunction once for each price in db, until the function returns FALSE...
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
GNCPrice * gnc_pricedb_lookup_latest(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Find the most recent price between the two commodities.
gpointer qof_collection_get_data(const QofCollection *col)
Store and retrieve arbitrary object-defined data.
Use unbiased ("banker's") rounding.
PriceList * gnc_pricedb_lookup_nearest_before_any_currency_t64(GNCPriceDB *db, const gnc_commodity *commodity, time64 t)
Return the nearest price between the given commodity and any other before the given time...
time64 time64CanonicalDayTime(time64 t)
convert a time64 on a certain day (localtime) to the time64 representing midday on that day...
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
The type used to store guids in C.
char * gnc_time64_to_iso8601_buff(time64 time, char *buff)
The gnc_time64_to_iso8601_buff() routine takes the input UTC time64 value and prints it as an ISO-860...
void gnc_pricedb_print_contents(GNCPriceDB *db, FILE *f)
This simple function can be useful for debugging the pricedb code.
GNCPrice * gnc_pricedb_lookup_nearest_before_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the nearest price between the given commodities before the given time.
void gnc_price_ref(GNCPrice *p)
gnc_price_ref - indicate your need for a given price to stick around (i.e.
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
void gnc_pricedb_commit_edit(GNCPriceDB *pdb)
Commit an edit.
PriceList * gnc_pricedb_get_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Return all the prices for a given commodity in another.
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).