32 #include "gnc-datetime.hpp" 34 #include "gnc-pricedb-p.h" 35 #include <qofinstance-p.h> 38 static QofLogModule log_module = GNC_MOD_PRICE;
40 static gboolean add_price(GNCPriceDB *db, GNCPrice *p);
41 static gboolean remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup);
42 static GNCPrice *lookup_nearest_in_time(GNCPriceDB *db,
const gnc_commodity *c,
43 const gnc_commodity *currency,
44 time64 t, gboolean sameday);
46 pricedb_pricelist_traversal(GNCPriceDB *db,
47 gboolean (*f)(GList *p, gpointer user_data),
65 return a < b ? -1 : a > b ? 1 : 0;
68 using CommodityPtrPair = std::pair<const gnc_commodity*, gpointer>;
69 using CommodityPtrPairVec = std::vector<CommodityPtrPair>;
72 hash_entry_insert(
const gnc_commodity* key,
const gpointer val, CommodityPtrPairVec *result)
74 result->emplace_back (key, val);
77 static CommodityPtrPairVec
78 hash_table_to_vector (GHashTable *
table)
80 CommodityPtrPairVec result_vec;
81 result_vec.reserve (g_hash_table_size (
table));
82 g_hash_table_foreach(
table, (GHFunc)hash_entry_insert, &result_vec);
87 G_DEFINE_TYPE(GNCPrice, gnc_price, QOF_TYPE_INSTANCE)
90 gnc_price_init(GNCPrice* price)
93 price->value = gnc_numeric_zero();
94 price->type =
nullptr;
95 price->source = PRICE_SOURCE_INVALID;
105 static const char* source_names[(size_t)PRICE_SOURCE_INVALID + 1] =
113 "user:split-register",
116 "user:stock-transaction",
123 gnc_price_dispose(GObject *pricep)
125 G_OBJECT_CLASS(gnc_price_parent_class)->dispose(pricep);
129 gnc_price_finalize(GObject* pricep)
131 G_OBJECT_CLASS(gnc_price_parent_class)->finalize(pricep);
141 gnc_price_get_property(GObject*
object, guint prop_id, GValue* value, GParamSpec* pspec)
145 g_return_if_fail(GNC_IS_PRICE(
object));
147 price = GNC_PRICE(
object);
151 g_value_set_string(value, gnc_price_get_source_string(price));
154 g_value_set_string(value, price->type);
157 g_value_set_boxed(value, &price->value);
160 g_value_take_object(value, price->commodity);
163 g_value_take_object(value, price->currency);
166 g_value_set_boxed(value, &price->tmspec);
169 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
175 gnc_price_set_property(GObject*
object, guint prop_id,
const GValue* value, GParamSpec* pspec)
181 g_return_if_fail(GNC_IS_PRICE(
object));
183 price = GNC_PRICE(
object);
184 g_assert (qof_instance_get_editlevel(price));
189 gnc_price_set_source_string(price, g_value_get_string(value));
192 gnc_price_set_typestr(price, g_value_get_string(value));
195 number =
static_cast<gnc_numeric*
>(g_value_get_boxed(value));
196 gnc_price_set_value(price, *number);
199 gnc_price_set_commodity(price, static_cast<gnc_commodity*>(g_value_get_object(value)));
202 gnc_price_set_currency(price, static_cast<gnc_commodity*>(g_value_get_object(value)));
205 time =
static_cast<Time64*
>(g_value_get_boxed(value));
206 gnc_price_set_time64(price, time->t);
209 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
215 gnc_price_class_init(GNCPriceClass *klass)
217 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
219 gobject_class->dispose = gnc_price_dispose;
220 gobject_class->finalize = gnc_price_finalize;
221 gobject_class->set_property = gnc_price_set_property;
222 gobject_class->get_property = gnc_price_get_property;
224 g_object_class_install_property
227 g_param_spec_object (
"commodity",
229 "The commodity field denotes the base kind of " 230 "'stuff' for the units of this quote, whether " 231 "it is USD, gold, stock, etc.",
235 g_object_class_install_property
238 g_param_spec_object (
"currency",
240 "The currency field denotes the external kind " 241 "'stuff' for the units of this quote, whether " 242 "it is USD, gold, stock, etc.",
246 g_object_class_install_property
249 g_param_spec_string (
"source",
251 "The price source is PriceSource enum describing how" 252 " the price was created. This property works on the" 253 " string values in source_names for SQL database" 258 g_object_class_install_property
261 g_param_spec_string (
"type",
263 "The quote type is a string describing the " 264 "type of a price quote. Types possible now " 265 "are 'bid', 'ask', 'last', 'nav', 'transaction', " 270 g_object_class_install_property
273 g_param_spec_boxed(
"date",
275 "The date of the price quote.",
279 g_object_class_install_property
282 g_param_spec_boxed(
"value",
284 "The value of the price quote.",
299 g_return_val_if_fail (book,
nullptr);
302 p =
static_cast<GNCPrice*
>(g_object_new(GNC_TYPE_PRICE,
nullptr));
306 LEAVE (
"price created %p", p);
311 gnc_price_destroy (GNCPrice *p)
313 ENTER(
"destroy price %p", p);
316 if (p->type) CACHE_REMOVE(p->type);
334 if (p->refcount == 0)
341 if (p->refcount <= 0)
343 if (
nullptr != p->db)
345 PERR(
"last unref while price in database");
347 gnc_price_destroy (p);
359 g_return_val_if_fail (book,
nullptr);
365 LEAVE (
"return nullptr");
372 LEAVE (
"return nullptr");
376 qof_instance_copy_version(new_p, p);
378 gnc_price_begin_edit(new_p);
380 gnc_price_set_commodity(new_p, gnc_price_get_commodity(p));
381 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
382 gnc_price_set_source(new_p, gnc_price_get_source(p));
383 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
384 gnc_price_set_value(new_p, gnc_price_get_value(p));
385 gnc_price_set_currency(new_p, gnc_price_get_currency(p));
386 gnc_price_commit_edit(new_p);
387 LEAVE (
"return cloned price %p", new_p);
396 qof_instance_copy_version(new_p, p);
397 gnc_price_begin_edit(new_p);
398 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
399 gnc_price_set_source(new_p, PRICE_SOURCE_TEMP);
400 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
401 gnc_price_set_commodity(new_p, gnc_price_get_currency(p));
402 gnc_price_set_currency(new_p, gnc_price_get_commodity(p));
404 gnc_price_commit_edit(new_p);
411 gnc_price_begin_edit (GNCPrice *p)
418 PERR (
"Failed to commit: %d", errcode);
419 gnc_engine_signal_commit_error( errcode );
425 gnc_price_commit_edit (GNCPrice *p)
450 gnc_price_set_dirty (GNCPrice *p)
452 qof_instance_set_dirty(&p->inst);
457 gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c)
467 remove_price (p->db, p, TRUE);
468 gnc_price_begin_edit (p);
470 gnc_price_set_dirty(p);
471 gnc_price_commit_edit (p);
472 add_price (p->db, p);
479 gnc_price_set_currency(GNCPrice *p, gnc_commodity *c)
489 remove_price (p->db, p, TRUE);
490 gnc_price_begin_edit (p);
492 gnc_price_set_dirty(p);
493 gnc_price_commit_edit (p);
494 add_price (p->db, p);
500 gnc_price_set_time64(GNCPrice *p,
time64 t)
509 remove_price (p->db, p, FALSE);
510 gnc_price_begin_edit (p);
512 gnc_price_set_dirty(p);
513 gnc_price_commit_edit (p);
514 add_price (p->db, p);
523 gnc_price_begin_edit (p);
525 gnc_price_set_dirty(p);
526 gnc_price_commit_edit(p);
530 gnc_price_set_source_string(GNCPrice *p,
const char* str)
535 if (strcmp(source_names[s], str) == 0)
537 gnc_price_set_source(p, s);
545 gnc_commodity *commodity = gnc_price_get_commodity(p);
546 gnc_commodity *currency = gnc_price_get_currency(p);
548 memset(datebuff, 0,
sizeof(datebuff));
550 PWARN(
"unknown price source string \"%s\" for %s:%s -> %s on %s;" 551 " leaving the source unchanged", str,
558 gnc_price_set_typestr(GNCPrice *p,
const char* type)
561 if (g_strcmp0(p->type, type) != 0)
563 gnc_price_begin_edit (p);
564 CACHE_REPLACE(p->type, type);
565 gnc_price_set_dirty(p);
566 gnc_price_commit_edit (p);
571 gnc_price_set_value(GNCPrice *p, gnc_numeric value)
576 gnc_price_begin_edit (p);
578 gnc_price_set_dirty(p);
579 gnc_price_commit_edit (p);
591 if (!guid || !book)
return nullptr;
597 gnc_price_get_commodity(
const GNCPrice *p)
599 if (!p)
return nullptr;
604 gnc_price_get_time64(
const GNCPrice *p)
606 return p ? p->tmspec : 0;
610 gnc_price_get_source(
const GNCPrice *p)
612 if (!p)
return PRICE_SOURCE_INVALID;
617 gnc_price_get_source_string(
const GNCPrice *p)
619 if (!p)
return nullptr;
620 return source_names[p->source];
624 gnc_price_get_typestr(
const GNCPrice *p)
626 if (!p)
return nullptr;
631 gnc_price_get_value(
const GNCPrice *p)
635 PERR(
"price nullptr.\n");
636 return gnc_numeric_zero();
642 gnc_price_get_currency(
const GNCPrice *p)
644 if (!p)
return nullptr;
649 gnc_price_equal (
const GNCPrice *p1,
const GNCPrice *p2)
653 if (p1 == p2)
return TRUE;
654 if (!p1 || !p2)
return FALSE;
657 gnc_price_get_commodity (p2)))
661 gnc_price_get_currency (p2)))
664 time1 = gnc_price_get_time64 (p1);
665 time2 = gnc_price_get_time64 (p2);
670 if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
673 if (g_strcmp0 (gnc_price_get_typestr (p1),
674 gnc_price_get_typestr (p2)) != 0)
678 gnc_price_get_value (p2)))
688 compare_prices_by_date(gconstpointer a, gconstpointer b)
693 if (!a && !b)
return 0;
697 time_a = gnc_price_get_time64((GNCPrice *) a);
698 time_b = gnc_price_get_time64((GNCPrice *) b);
701 result = time64_cmp(time_b, time_a);
702 if (result)
return result;
705 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
706 gnc_price_get_guid((GNCPrice *) b));
710 price_is_duplicate (
const GNCPrice *p_price,
const GNCPrice *c_price)
722 if (!prices || !p)
return FALSE;
725 if (check_dupl && g_list_find_custom (*prices, p, (GCompareFunc)price_is_duplicate))
728 auto result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
732 *prices = result_list;
740 GList *found_element;
742 if (!prices || !p)
return FALSE;
744 found_element = g_list_find(*prices, p);
745 if (!found_element)
return TRUE;
747 result_list = g_list_remove_link(*prices, found_element);
749 g_list_free(found_element);
751 *prices = result_list;
762 gnc_price_list_equal(PriceList *prices1, PriceList *prices2)
764 if (prices1 == prices2)
return TRUE;
766 for (
auto n1 = prices1, n2 = prices2; n1 || n2; n1 = g_list_next (n1), n2 = g_list_next (n2))
770 PINFO (
"prices2 has extra prices");
775 PINFO (
"prices1 has extra prices");
778 if (!gnc_price_equal (static_cast<GNCPrice*>(n1->data), static_cast<GNCPrice*>(n2->data)))
797 QOF_GOBJECT_IMPL(gnc_pricedb, GNCPriceDB, QOF_TYPE_INSTANCE)
800 gnc_pricedb_init(GNCPriceDB* pdb)
802 pdb->reset_nth_price_cache = FALSE;
806 gnc_pricedb_dispose_real (GObject *pdbp)
811 gnc_pricedb_finalize_real(GObject* pdbp)
816 gnc_pricedb_create(
QofBook * book)
821 g_return_val_if_fail (book,
nullptr);
830 PWARN (
"A price database already exists for this book!");
834 result =
static_cast<GNCPriceDB*
>(g_object_new(GNC_TYPE_PRICEDB,
nullptr));
841 qof_collection_set_data (col, result);
843 result->commodity_hash = g_hash_table_new(
nullptr,
nullptr);
844 g_return_val_if_fail (result->commodity_hash,
nullptr);
849 destroy_pricedb_currency_hash_data(gpointer key,
853 GList *price_list = (GList *) data;
857 for (node = price_list; node; node = node->next)
859 p =
static_cast<GNCPrice*
>(node->data);
868 destroy_pricedb_commodity_hash_data(gpointer key,
872 GHashTable *currency_hash = (GHashTable *) data;
873 if (!currency_hash)
return;
874 g_hash_table_foreach (currency_hash,
875 destroy_pricedb_currency_hash_data,
877 g_hash_table_destroy(currency_hash);
884 if (db->commodity_hash)
886 g_hash_table_foreach (db->commodity_hash,
887 destroy_pricedb_commodity_hash_data,
890 g_hash_table_destroy (db->commodity_hash);
891 db->commodity_hash =
nullptr;
899 db->bulk_update = bulk_update;
920 if (!col)
return nullptr;
929 if (!book)
return nullptr;
937 num_prices_helper (GNCPrice *p, gpointer user_data)
939 auto count =
static_cast<guint*
>(user_data);
966 gnc_commodity *commodity;
970 pricedb_equal_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
973 auto currency =
static_cast<gnc_commodity*
>(key);
974 auto price_list1 =
static_cast<GList*
>(val);
976 equal_data->commodity,
979 if (!gnc_price_list_equal (price_list1, price_list2))
980 equal_data->equal = FALSE;
986 pricedb_equal_foreach_currencies_hash (gpointer key, gpointer val,
989 auto currencies_hash =
static_cast<GHashTable*
>(val);
992 equal_data->commodity =
static_cast<gnc_commodity*
>(key);
994 g_hash_table_foreach (currencies_hash,
995 pricedb_equal_foreach_pricelist,
1004 if (db1 == db2)
return TRUE;
1008 PWARN (
"one is nullptr");
1012 equal_data.equal = TRUE;
1013 equal_data.db2 = db2;
1015 g_hash_table_foreach (db1->commodity_hash,
1016 pricedb_equal_foreach_currencies_hash,
1019 return equal_data.equal;
1027 add_price(GNCPriceDB *db, GNCPrice *p)
1032 gnc_commodity *commodity;
1033 gnc_commodity *currency;
1034 GHashTable *currency_hash;
1036 if (!db || !p)
return FALSE;
1037 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1043 PERR (
"attempted to mix up prices across different books");
1048 commodity = gnc_price_get_commodity(p);
1051 PWARN(
"no commodity");
1055 currency = gnc_price_get_currency(p);
1058 PWARN(
"no currency");
1062 if (!db->commodity_hash)
1064 LEAVE (
"no commodity hash found ");
1071 if (!db->bulk_update)
1074 p->currency, p->tmspec);
1075 if (old_price !=
nullptr)
1077 if (p->source > old_price->source)
1080 LEAVE (
"Better price already in DB.");
1087 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1090 currency_hash = g_hash_table_new(
nullptr,
nullptr);
1091 g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
1094 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1097 LEAVE (
"gnc_price_list_insert failed");
1103 LEAVE (
" no price list");
1107 g_hash_table_insert(currency_hash, currency, price_list);
1112 LEAVE (
"db=%p, pr=%p dirty=%d dextroying=%d commodity=%s/%s currency_hash=%p",
1128 if (!db || !p)
return FALSE;
1130 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1134 if (FALSE == add_price(db, p))
1136 LEAVE (
" failed to add price");
1141 qof_instance_set_dirty(&db->inst);
1144 LEAVE (
"db=%p, pr=%p dirty=%d destroying=%d",
1156 remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup)
1159 gnc_commodity *commodity;
1160 gnc_commodity *currency;
1161 GHashTable *currency_hash;
1163 if (!db || !p)
return FALSE;
1164 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1168 commodity = gnc_price_get_commodity(p);
1171 LEAVE (
" no commodity");
1174 currency = gnc_price_get_currency(p);
1177 LEAVE (
" no currency");
1180 if (!db->commodity_hash)
1182 LEAVE (
" no commodity hash");
1186 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1189 LEAVE (
" no currency hash");
1194 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1199 LEAVE (
" cannot remove price list");
1207 g_hash_table_insert(currency_hash, currency, price_list);
1211 g_hash_table_remove(currency_hash, currency);
1218 guint num_currencies = g_hash_table_size (currency_hash);
1219 if (0 == num_currencies)
1221 g_hash_table_remove (db->commodity_hash, commodity);
1222 g_hash_table_destroy (currency_hash);
1228 LEAVE (
"db=%p, pr=%p", db, p);
1237 memset(datebuff, 0,
sizeof(datebuff));
1238 if (!db || !p)
return FALSE;
1239 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1245 DEBUG(
"Remove Date is %s, Commodity is %s, Source is %s", datebuff,
1247 gnc_price_get_source_string (p));
1249 rc = remove_price (db, p, TRUE);
1251 qof_instance_set_dirty(&db->inst);
1255 gnc_price_begin_edit (p);
1256 qof_instance_set_destroying(p, TRUE);
1257 gnc_price_commit_edit (p);
1260 LEAVE (
"db=%p, pr=%p", db, p);
1269 gboolean delete_user;
1270 gboolean delete_app;
1275 check_one_price_date (GNCPrice *price, gpointer user_data)
1281 ENTER(
"price %p (%s), data %p", price,
1285 source = gnc_price_get_source (price);
1287 if ((source == PRICE_SOURCE_FQ) && data->delete_fq)
1288 PINFO (
"Delete Quote Source");
1289 else if ((source == PRICE_SOURCE_USER_PRICE) && data->delete_user)
1290 PINFO (
"Delete User Source");
1291 else if ((source != PRICE_SOURCE_FQ) && (source != PRICE_SOURCE_USER_PRICE) && data->delete_app)
1292 PINFO (
"Delete App Source");
1295 LEAVE(
"Not a matching source");
1299 time = gnc_price_get_time64 (price);
1303 DEBUG(
"checking date %s", buf);
1305 if (time < data->cutoff)
1307 data->list = g_slist_prepend(data->list, price);
1308 DEBUG(
"will delete");
1315 pricedb_remove_foreach_pricelist (gpointer key,
1319 GList *price_list = (GList *) val;
1320 GList *node = price_list;
1323 ENTER(
"key %p, value %p, data %p", key, val, user_data);
1326 g_list_foreach(node, (GFunc)check_one_price_date, data);
1332 compare_prices_by_commodity_date (gconstpointer a, gconstpointer b)
1335 gnc_commodity *comma;
1336 gnc_commodity *commb;
1337 gnc_commodity *curra;
1338 gnc_commodity *currb;
1341 if (!a && !b)
return 0;
1346 comma = gnc_price_get_commodity ((GNCPrice *) a);
1347 commb = gnc_price_get_commodity ((GNCPrice *) b);
1352 curra = gnc_price_get_currency ((GNCPrice *) a);
1353 currb = gnc_price_get_currency ((GNCPrice *) b);
1358 time_a = gnc_price_get_time64((GNCPrice *) a);
1359 time_b = gnc_price_get_time64((GNCPrice *) b);
1362 result = time64_cmp(time_b, time_a);
1363 if (result)
return result;
1366 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
1367 gnc_price_get_guid((GNCPrice *) b));
1371 price_commodity_and_currency_equal (GNCPrice *a, GNCPrice *b)
1373 gboolean ret_comm = FALSE;
1374 gboolean ret_curr = FALSE;
1382 return (ret_comm && ret_curr);
1386 gnc_pricedb_remove_old_prices_pinfo (GNCPrice *price, gboolean keep_message)
1391 if (g_date_valid (&price_date))
1397 PINFO(
"#### Keep price with date %s, commodity is %s, currency is %s", date_buf,
1402 PINFO(
"## Remove price with date %s", date_buf);
1405 PINFO(
"Keep price date is invalid");
1409 clone_price (GNCPrice **price, GNCPrice *source_price)
1413 if (!source_price)
return;
1414 if (price ==
nullptr)
return;
1423 gnc_pricedb_remove_old_prices_pinfo (source_price, TRUE);
1427 roundUp (gint numToRound, gint multiple)
1434 remainder = numToRound % multiple;
1438 return numToRound + multiple - remainder;
1442 get_fiscal_quarter (GDate *date, GDateMonth fiscal_start)
1444 GDateMonth month = g_date_get_month (date);
1446 gint q = ((roundUp (22 - fiscal_start + month, 3)/3) % 4) + 1;
1448 PINFO(
"Return fiscal quarter is %d", q);
1453 gnc_pricedb_process_removal_list (GNCPriceDB *db, GDate *fiscal_end_date,
1457 gboolean save_first_price = FALSE;
1458 gint saved_test_value = 0, next_test_value = 0;
1459 GNCPrice *cloned_price =
nullptr;
1460 GDateMonth fiscal_month_start;
1461 GDate *tmp_date = g_date_new_dmy (g_date_get_day (fiscal_end_date),
1462 g_date_get_month (fiscal_end_date),
1463 g_date_get_year (fiscal_end_date));
1466 g_date_subtract_months (tmp_date, 12);
1467 fiscal_month_start =
static_cast<GDateMonth
>(g_date_get_month (tmp_date) + 1);
1468 g_date_free (tmp_date);
1471 data.list = g_slist_sort (data.list, compare_prices_by_commodity_date);
1474 for (item = data.list; item; item = g_slist_next(item))
1476 GDate saved_price_date;
1477 GDate next_price_date;
1478 auto price =
static_cast<GNCPrice*
>(item->data);
1481 if (keep == PRICE_REMOVE_KEEP_NONE)
1483 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1488 save_first_price = !price_commodity_and_currency_equal (price, cloned_price);
1489 if (save_first_price == TRUE)
1491 clone_price (&cloned_price, price);
1496 saved_price_date =
time64_to_gdate (gnc_price_get_time64 (cloned_price));
1500 if (keep == PRICE_REMOVE_KEEP_LAST_PERIOD && save_first_price == FALSE)
1502 GDate *saved_fiscal_end = g_date_new_dmy (g_date_get_day (&saved_price_date),
1503 g_date_get_month (&saved_price_date),
1504 g_date_get_year (&saved_price_date));
1506 GDate *next_fiscal_end = g_date_new_dmy (g_date_get_day (&next_price_date),
1507 g_date_get_month (&next_price_date),
1508 g_date_get_year (&next_price_date));
1513 saved_test_value = g_date_get_year (saved_fiscal_end);
1514 next_test_value = g_date_get_year (next_fiscal_end);
1516 PINFO(
"Keep last price in fiscal year");
1518 g_date_free (saved_fiscal_end);
1519 g_date_free (next_fiscal_end);
1523 if (keep == PRICE_REMOVE_KEEP_LAST_QUARTERLY && save_first_price == FALSE)
1525 saved_test_value = get_fiscal_quarter (&saved_price_date, fiscal_month_start);
1526 next_test_value = get_fiscal_quarter (&next_price_date, fiscal_month_start);
1528 PINFO(
"Keep last price in fiscal quarter");
1532 if (keep == PRICE_REMOVE_KEEP_LAST_MONTHLY && save_first_price == FALSE)
1534 saved_test_value = g_date_get_month (&saved_price_date);
1535 next_test_value = g_date_get_month (&next_price_date);
1537 PINFO(
"Keep last price of every month");
1541 if (keep == PRICE_REMOVE_KEEP_LAST_WEEKLY && save_first_price == FALSE)
1543 saved_test_value = g_date_get_iso8601_week_of_year (&saved_price_date);
1544 next_test_value = g_date_get_iso8601_week_of_year (&next_price_date);
1546 PINFO(
"Keep last price of every week");
1550 if (saved_test_value == next_test_value)
1552 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1556 clone_price (&cloned_price, price);
1564 GDate *fiscal_end_date,
time64 cutoff,
1565 PriceRemoveSourceFlags source,
1566 PriceRemoveKeepOptions keep)
1571 memset (datebuff, 0,
sizeof(datebuff));
1574 data.cutoff = cutoff;
1575 data.list =
nullptr;
1576 data.delete_fq = FALSE;
1577 data.delete_user = FALSE;
1578 data.delete_app = FALSE;
1580 ENTER(
"Remove Prices for Source %d, keeping %d", source, keep);
1583 if (source & PRICE_REMOVE_SOURCE_APP)
1584 data.delete_app = TRUE;
1586 if (source & PRICE_REMOVE_SOURCE_FQ)
1587 data.delete_fq = TRUE;
1589 if (source & PRICE_REMOVE_SOURCE_USER)
1590 data.delete_user = TRUE;
1593 for (node = g_list_first (comm_list); node; node = g_list_next (node))
1595 auto currencies_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, node->data));
1596 g_hash_table_foreach (currencies_hash, pricedb_remove_foreach_pricelist, &data);
1599 if (data.list ==
nullptr)
1601 LEAVE(
"Empty price list");
1605 DEBUG(
"Number of Prices in list is %d, Cutoff date is %s",
1606 g_slist_length (data.list), datebuff);
1609 if (fiscal_end_date ==
nullptr || !g_date_valid (fiscal_end_date))
1612 GDate end_this_year;
1613 g_date_set_dmy (&end_this_year, 31, GDateMonth(12), ymd.year);
1614 gnc_pricedb_process_removal_list (db, &end_this_year, data, keep);
1617 gnc_pricedb_process_removal_list (db, fiscal_end_date, data, keep);
1619 g_slist_free (data.list);
1627 static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b);
1630 hash_values_helper(gpointer key, gpointer value, gpointer data)
1632 auto l =
static_cast<GList**
>(data);
1636 new_l = pricedb_price_list_merge(*l, static_cast<PriceList*>(value));
1641 *l = g_list_copy (static_cast<GList*>(value));
1645 price_list_from_hashtable (GHashTable *hash,
const gnc_commodity *currency)
1647 GList *price_list =
nullptr, *result = nullptr ;
1650 price_list =
static_cast<GList*
>(g_hash_table_lookup(hash, currency));
1653 LEAVE (
" no price list");
1656 result = g_list_copy (price_list);
1660 g_hash_table_foreach(hash, hash_values_helper, (gpointer)&result);
1666 pricedb_price_list_merge (PriceList *a, PriceList *b)
1668 PriceList *merged_list =
nullptr;
1672 while (next_a || next_b)
1674 if (next_a ==
nullptr)
1676 merged_list = g_list_prepend (merged_list, next_b->data);
1677 next_b = next_b->next;
1679 else if (next_b ==
nullptr)
1681 merged_list = g_list_prepend (merged_list, next_a->data);
1682 next_a = next_a->next;
1685 else if (compare_prices_by_date (next_a->data, next_b->data) < 0)
1687 merged_list = g_list_prepend (merged_list, next_a->data);
1688 next_a = next_a->next;
1692 merged_list = g_list_prepend (merged_list, next_b->data);
1693 next_b = next_b->next;
1696 return g_list_reverse (merged_list);
1700 pricedb_get_prices_internal(GNCPriceDB *db,
const gnc_commodity *commodity,
1701 const gnc_commodity *currency, gboolean bidi)
1703 GHashTable *forward_hash =
nullptr, *reverse_hash =
nullptr;
1704 PriceList *forward_list =
nullptr, *reverse_list =
nullptr;
1705 g_return_val_if_fail (db !=
nullptr,
nullptr);
1706 g_return_val_if_fail (commodity !=
nullptr,
nullptr);
1707 forward_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1708 if (currency && bidi)
1709 reverse_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, currency));
1710 if (!forward_hash && !reverse_hash)
1712 LEAVE (
" no currency hash");
1716 forward_list = price_list_from_hashtable (forward_hash, currency);
1717 if (currency && reverse_hash)
1719 reverse_list = price_list_from_hashtable (reverse_hash, commodity);
1729 PriceList *merged_list;
1730 merged_list = pricedb_price_list_merge (forward_list, reverse_list);
1731 g_list_free (forward_list);
1732 g_list_free (reverse_list);
1733 forward_list = merged_list;
1737 forward_list = reverse_list;
1742 return forward_list;
1746 const gnc_commodity *commodity,
1747 const gnc_commodity *currency)
1752 if (!db || !commodity || !currency)
return nullptr;
1753 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
1755 price_list = pricedb_get_prices_internal(db, commodity, currency, TRUE);
1756 if (!price_list)
return nullptr;
1760 result =
static_cast<GNCPrice*
>(price_list->data);
1762 g_list_free (price_list);
1763 LEAVE(
"price is %p", result);
1770 const gnc_commodity *com;
1785 price_list_scan_any_currency(GList *price_list, gpointer data)
1794 auto price =
static_cast<GNCPrice*
>(price_list->data);
1795 com = gnc_price_get_commodity(price);
1796 cur = gnc_price_get_currency(price);
1800 if (com != helper->com && cur != helper->com)
1806 for (
auto node = price_list; node; node = g_list_next (node))
1808 price =
static_cast<GNCPrice*
>(node->data);
1809 time64 price_t = gnc_price_get_time64(price);
1810 if (price_t < helper->t)
1815 auto prev_price =
static_cast<GNCPrice*
>(node->prev->data);
1817 *helper->list = g_list_prepend(*helper->list, prev_price);
1821 *helper->list = g_list_prepend(*helper->list, price);
1825 else if (node->next ==
nullptr)
1829 *helper->list = g_list_prepend(*helper->list, price);
1840 latest_before (PriceList *prices,
const gnc_commodity* target,
time64 t)
1842 GList *node, *found_coms =
nullptr, *retval =
nullptr;
1843 for (node = prices; node !=
nullptr; node = g_list_next(node))
1845 GNCPrice *price = (GNCPrice*)node->data;
1846 gnc_commodity *com = gnc_price_get_commodity(price);
1847 gnc_commodity *cur = gnc_price_get_currency(price);
1848 time64 price_t = gnc_price_get_time64(price);
1851 (com == target && g_list_find (found_coms, cur)) ||
1852 (cur == target && g_list_find (found_coms, com)))
1856 retval = g_list_prepend (retval, price);
1857 found_coms = g_list_prepend (found_coms, com == target ? cur : com);
1859 g_list_free (found_coms);
1860 return g_list_reverse(retval);
1864 find_comtime(GPtrArray* array, gnc_commodity *com)
1866 unsigned int index = 0;
1867 GNCPrice** retval =
nullptr;
1868 for (index = 0; index < array->len; ++index)
1870 auto price_p =
static_cast<GNCPrice**
>(g_ptr_array_index(array, index));
1871 if (gnc_price_get_commodity(*price_p) == com ||
1872 gnc_price_get_currency(*price_p) == com)
1879 add_nearest_price(GList *target_list, GPtrArray *price_array, GNCPrice *price,
1880 const gnc_commodity *target,
time64 t)
1882 gnc_commodity *com = gnc_price_get_commodity(price);
1883 gnc_commodity *cur = gnc_price_get_currency(price);
1884 time64 price_t = gnc_price_get_time64(price);
1885 gnc_commodity *other = com == target ? cur : com;
1886 GNCPrice **com_price = find_comtime(price_array, other);
1888 if (com_price ==
nullptr)
1890 com_price = (GNCPrice**)g_slice_new(gpointer);
1892 g_ptr_array_add(price_array, com_price);
1898 target_list = g_list_prepend(target_list, price);
1902 com_t = gnc_price_get_time64(*com_price);
1915 time64 com_diff = com_t - t;
1916 time64 price_diff = t - price_t;
1917 if (com_diff < price_diff)
1920 target_list = g_list_prepend(target_list, *com_price);
1925 target_list = g_list_prepend(target_list, price);
1933 nearest_to (PriceList *prices,
const gnc_commodity* target,
time64 t)
1935 GList *node, *retval =
nullptr;
1936 const guint prealloc_size = 5;
1941 GPtrArray *price_array = g_ptr_array_sized_new(prealloc_size);
1943 for (node = prices; node !=
nullptr; node = g_list_next(node))
1945 GNCPrice *price = (GNCPrice*)node->data;
1946 retval = add_nearest_price(retval, price_array, price, target, t);
1952 for (index = 0; index < price_array->len; ++index)
1954 auto com_price =
static_cast<GNCPrice**
>(g_ptr_array_index(price_array, index));
1955 time64 price_t = gnc_price_get_time64(*com_price);
1959 retval = g_list_prepend(retval, *com_price);
1962 g_ptr_array_free(price_array, TRUE);
1963 return g_list_sort(retval, compare_prices_by_date);
1970 const gnc_commodity *commodity)
1978 const gnc_commodity *commodity,
1981 GList *prices =
nullptr, *result;
1985 if (!db || !commodity)
return nullptr;
1986 ENTER (
"db=%p commodity=%p", db, commodity);
1988 pricedb_pricelist_traversal(db, price_list_scan_any_currency, &helper);
1989 prices = g_list_sort(prices, compare_prices_by_date);
1990 result = nearest_to(prices, commodity, t);
1998 const gnc_commodity *commodity,
2001 GList *prices =
nullptr, *result;
2005 if (!db || !commodity)
return nullptr;
2006 ENTER (
"db=%p commodity=%p", db, commodity);
2008 pricedb_pricelist_traversal(db, price_list_scan_any_currency,
2010 prices = g_list_sort(prices, compare_prices_by_date);
2011 result = latest_before(prices, commodity, t);
2023 const gnc_commodity *commodity,
2024 const gnc_commodity *currency)
2027 GHashTable *currency_hash;
2030 if (!db || !commodity)
return FALSE;
2031 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2032 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
2035 LEAVE(
"no, no currency_hash table");
2041 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
2047 LEAVE(
"no, no price list");
2051 size = g_hash_table_size (currency_hash);
2052 LEAVE(
"%s", size > 0 ?
"yes" :
"no");
2062 const gnc_commodity *commodity,
2063 const gnc_commodity *currency)
2065 if (!db || !commodity)
return nullptr;
2066 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2067 auto result = pricedb_get_prices_internal (db, commodity, currency, FALSE);
2068 if (!result)
return nullptr;
2077 price_count_helper(gpointer key, gpointer value, gpointer data)
2079 auto result =
static_cast<int*
>(data);
2080 auto price_list =
static_cast<GList*
>(value);
2082 *result += g_list_length(price_list);
2087 const gnc_commodity *c)
2090 GHashTable *currency_hash;
2092 if (!db || !c)
return 0;
2093 ENTER (
"db=%p commodity=%p", db, c);
2095 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, c));
2098 g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result);
2101 LEAVE (
"count=%d", result);
2107 list_combine (gpointer element, gpointer data)
2109 GList *list = *(GList**)data;
2110 auto lst =
static_cast<GList*
>(element);
2111 if (list ==
nullptr)
2112 *(GList**)data = g_list_copy (lst);
2115 GList *new_list = g_list_concat (list, g_list_copy (lst));
2116 *(GList**)data = new_list;
2134 const gnc_commodity *c,
2137 static const gnc_commodity *last_c =
nullptr;
2138 static GList *prices =
nullptr;
2140 GNCPrice *result =
nullptr;
2141 GHashTable *currency_hash;
2142 g_return_val_if_fail (GNC_IS_COMMODITY (c),
nullptr);
2144 if (!db || !c || n < 0)
return nullptr;
2147 if (last_c && prices && last_c == c && db->reset_nth_price_cache == FALSE)
2149 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2150 LEAVE (
"price=%p", result);
2158 g_list_free (prices);
2162 db->reset_nth_price_cache = FALSE;
2164 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, c));
2167 GList *currencies = g_hash_table_get_values (currency_hash);
2168 g_list_foreach (currencies, list_combine, &prices);
2169 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2170 g_list_free (currencies);
2173 LEAVE (
"price=%p", result);
2178 gnc_pricedb_nth_price_reset_cache (GNCPriceDB *db)
2181 db->reset_nth_price_cache = TRUE;
2186 const gnc_commodity *c,
2187 const gnc_commodity *currency,
2190 return lookup_nearest_in_time(db, c, currency, t, TRUE);
2194 lookup_nearest_in_time(GNCPriceDB *db,
2195 const gnc_commodity *c,
2196 const gnc_commodity *currency,
2201 GNCPrice *current_price =
nullptr;
2202 GNCPrice *next_price =
nullptr;
2203 GNCPrice *result =
nullptr;
2205 if (!db || !c || !currency)
return nullptr;
2206 if (t == INT64_MAX)
return nullptr;
2207 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2208 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2209 if (!price_list)
return nullptr;
2212 current_price =
static_cast<GNCPrice*
>(price_list->data);
2216 for (
auto item = price_list; item; item = g_list_next (item))
2218 auto p =
static_cast<GNCPrice*
>(item->data);
2219 time64 price_time = gnc_price_get_time64(p);
2220 if (price_time <= t)
2222 next_price =
static_cast<GNCPrice*
>(item->data);
2225 current_price =
static_cast<GNCPrice*
>(item->data);
2233 result = current_price;
2241 if (price_day != t_day)
2249 time64 current_t = gnc_price_get_time64(current_price);
2250 time64 next_t = gnc_price_get_time64(next_price);
2251 time64 diff_current = current_t - t;
2252 time64 diff_next = next_t - t;
2253 time64 abs_current = llabs(diff_current);
2254 time64 abs_next = llabs(diff_next);
2262 if (current_day == t_day)
2264 if (next_day == t_day)
2267 if (abs_current < abs_next)
2268 result = current_price;
2270 result = next_price;
2274 result = current_price;
2276 else if (next_day == t_day)
2278 result = next_price;
2285 if (abs_current < abs_next)
2287 result = current_price;
2291 result = next_price;
2298 g_list_free (price_list);
2305 const gnc_commodity *c,
2306 const gnc_commodity *currency,
2309 return lookup_nearest_in_time(db, c, currency, t, FALSE);
2313 static int price_time64_less_or_equal (GNCPrice *p,
time64 *time)
2315 return !(gnc_price_get_time64 (p) <= *time);
2320 const gnc_commodity *c,
2321 const gnc_commodity *currency,
2324 GNCPrice *current_price =
nullptr;
2325 if (!db || !c || !currency)
return nullptr;
2326 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2327 auto price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2328 if (!price_list)
return nullptr;
2329 auto p = g_list_find_custom (price_list, &t, (GCompareFunc)price_time64_less_or_equal);
2332 current_price = GNC_PRICE (p->data);
2335 g_list_free (price_list);
2337 return current_price;
2348 extract_common_prices (PriceList *from_prices, PriceList *to_prices,
2349 const gnc_commodity *from,
const gnc_commodity *to)
2352 GList *from_node =
nullptr, *to_node =
nullptr;
2353 GNCPrice *from_price =
nullptr, *to_price =
nullptr;
2355 for (from_node = from_prices; from_node !=
nullptr;
2356 from_node = g_list_next(from_node))
2358 for (to_node = to_prices; to_node !=
nullptr;
2359 to_node = g_list_next(to_node))
2361 gnc_commodity *to_com, *to_cur;
2362 gnc_commodity *from_com, *from_cur;
2363 to_price = GNC_PRICE(to_node->data);
2364 from_price = GNC_PRICE(from_node->data);
2365 to_com = gnc_price_get_commodity (to_price);
2366 to_cur = gnc_price_get_currency (to_price);
2367 from_com = gnc_price_get_commodity (from_price);
2368 from_cur = gnc_price_get_currency (from_price);
2369 if (((to_com == from_com || to_com == from_cur) &&
2370 (to_com != from && to_com != to)) ||
2371 ((to_cur == from_com || to_cur == from_cur) &&
2372 (to_cur != from && to_cur != to)))
2375 from_price =
nullptr;
2377 if (to_price !=
nullptr && from_price !=
nullptr)
2380 if (from_price ==
nullptr || to_price ==
nullptr)
2384 retval.from = from_price;
2385 retval.to = to_price;
2390 convert_price (
const gnc_commodity *from,
const gnc_commodity *to,
PriceTuple tuple)
2392 gnc_commodity *p1_com = gnc_price_get_commodity (tuple.from);
2393 gnc_commodity *p1_cur = gnc_price_get_currency (tuple.from);
2394 gnc_commodity *p2_com = gnc_price_get_commodity (tuple.to);
2395 gnc_commodity *p2_cur = gnc_price_get_currency (tuple.to);
2396 gnc_numeric p1_val = gnc_price_get_value (tuple.from);
2397 gnc_numeric p2_val = gnc_price_get_value (tuple.to);
2402 if ((p1_com == from && p2_com == to) ||
2403 (p1_cur == from && p2_cur == to))
2423 indirect_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2424 const gnc_commodity *to,
time64 t, gboolean before_date)
2426 GList *from_prices =
nullptr, *to_prices =
nullptr;
2428 gnc_numeric zero = gnc_numeric_zero();
2439 else if (before_date)
2451 if (!from_prices || !to_prices)
2457 tuple = extract_common_prices (from_prices, to_prices, from, to);
2461 return convert_price (from, to, tuple);
2467 direct_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2468 const gnc_commodity *to,
time64 t, gboolean before_date)
2471 gnc_numeric retval = gnc_numeric_zero();
2473 if (!from || !to)
return retval;
2477 else if (before_date)
2482 if (!price)
return retval;
2484 retval = gnc_price_get_value (price);
2486 if (gnc_price_get_commodity (price) != from)
2494 get_nearest_price (GNCPriceDB *pdb,
2495 const gnc_commodity *orig_curr,
2496 const gnc_commodity *new_curr,
2503 return gnc_numeric_create (1, 1);
2506 price = direct_price_conversion (pdb, orig_curr, new_curr, t, before);
2512 price = indirect_price_conversion (pdb, orig_curr, new_curr, t, before);
2519 const gnc_commodity *orig_currency,
2520 const gnc_commodity *new_currency,
2523 return get_nearest_price (pdb, orig_currency, new_currency, t, TRUE);
2528 const gnc_commodity *orig_currency,
2529 const gnc_commodity *new_currency,
2532 return get_nearest_price (pdb, orig_currency, new_currency, t, FALSE);
2537 const gnc_commodity *orig_currency,
2538 const gnc_commodity *new_currency)
2540 return get_nearest_price (pdb, orig_currency, new_currency, INT64_MAX, FALSE);
2544 convert_amount_at_date (GNCPriceDB *pdb,
2546 const gnc_commodity *orig_currency,
2547 const gnc_commodity *new_currency,
2549 gboolean before_date)
2556 price = get_nearest_price (pdb, orig_currency, new_currency, t, before_date);
2560 return gnc_numeric_zero ();
2572 gnc_numeric balance,
2573 const gnc_commodity *balance_currency,
2574 const gnc_commodity *new_currency)
2576 return convert_amount_at_date
2577 (pdb, balance, balance_currency, new_currency, INT64_MAX, FALSE);
2582 gnc_numeric balance,
2583 const gnc_commodity *balance_currency,
2584 const gnc_commodity *new_currency,
2587 return convert_amount_at_date
2588 (pdb, balance, balance_currency, new_currency, t, FALSE);
2593 gnc_numeric balance,
2594 const gnc_commodity *balance_currency,
2595 const gnc_commodity *new_currency,
2598 return convert_amount_at_date
2599 (pdb, balance, balance_currency, new_currency, t, TRUE);
2609 gboolean (*func)(GNCPrice *p, gpointer user_data);
2614 pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2616 GList *price_list = (GList *) val;
2620 foreach_data->ok = g_list_find_custom (price_list, foreach_data->user_data, (GCompareFunc)foreach_data->func)
2625 pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2627 GHashTable *currencies_hash = (GHashTable *) val;
2628 g_hash_table_foreach(currencies_hash, pricedb_foreach_pricelist, user_data);
2632 unstable_price_traversal(GNCPriceDB *db,
2633 gboolean (*f)(GNCPrice *p, gpointer user_data),
2638 if (!db || !f)
return FALSE;
2639 foreach_data.ok = TRUE;
2640 foreach_data.func = f;
2641 foreach_data.user_data = user_data;
2642 if (db->commodity_hash ==
nullptr)
2646 g_hash_table_foreach(db->commodity_hash,
2647 pricedb_foreach_currencies_hash,
2650 return foreach_data.ok;
2657 gboolean (*func)(GList *p, gpointer user_data);
2662 pricedb_pricelist_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2664 GList *price_list = (GList *) val;
2666 if (foreach_data->ok)
2668 foreach_data->ok = foreach_data->func(price_list, foreach_data->user_data);
2673 pricedb_pricelist_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2675 GHashTable *currencies_hash = (GHashTable *) val;
2676 g_hash_table_foreach(currencies_hash, pricedb_pricelist_foreach_pricelist, user_data);
2680 pricedb_pricelist_traversal(GNCPriceDB *db,
2681 gboolean (*f)(GList *p, gpointer user_data),
2686 if (!db || !f)
return FALSE;
2687 foreach_data.ok = TRUE;
2688 foreach_data.func = f;
2689 foreach_data.user_data = user_data;
2690 if (db->commodity_hash ==
nullptr)
2694 g_hash_table_foreach(db->commodity_hash,
2695 pricedb_pricelist_foreach_currencies_hash,
2698 return foreach_data.ok;
2702 compare_hash_entries_by_commodity_key (
const CommodityPtrPair& he_a,
const CommodityPtrPair& he_b)
2704 auto ca = he_a.first;
2705 auto cb = he_b.first;
2707 if (ca == cb || !cb)
2716 return (cmp_result < 0);
2722 stable_price_traversal(GNCPriceDB *db,
2723 gboolean (*f)(GNCPrice *p, gpointer user_data),
2726 g_return_val_if_fail (db && f,
false);
2728 auto currency_hashes = hash_table_to_vector (db->commodity_hash);
2729 std::sort (currency_hashes.begin(), currency_hashes.end(), compare_hash_entries_by_commodity_key);
2731 for (
const auto& entry : currency_hashes)
2733 auto price_lists = hash_table_to_vector (static_cast<GHashTable*>(entry.second));
2734 std::sort (price_lists.begin(), price_lists.end(), compare_hash_entries_by_commodity_key);
2736 for (
const auto& pricelist_entry : price_lists)
2737 if (g_list_find_custom (static_cast<GList*>(pricelist_entry.second), user_data, (GCompareFunc)f))
2746 GncPriceForeachFunc f,
2748 gboolean stable_order)
2750 ENTER (
"db=%p f=%p", db, f);
2753 LEAVE (
" stable order found");
2754 return stable_price_traversal(db, f, user_data);
2756 LEAVE (
" use unstable order");
2757 return unstable_price_traversal(db, f, user_data);
2767 gnc_commodity *commodity;
2768 gnc_commodity *currency;
2769 gchar *istr =
nullptr;
2775 commodity = gnc_price_get_commodity(p);
2776 currency = gnc_price_get_currency(p);
2778 if (!commodity)
return;
2779 if (!currency)
return;
2781 istr = g_strnfill(indent,
' ');
2783 fprintf(f,
"%s<pdb:price>\n", istr);
2784 fprintf(f,
"%s <pdb:commodity pointer=%p>\n", istr, commodity);
2786 str = str ? str :
"(null)";
2787 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2789 str = str ? str :
"(null)";
2790 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2791 fprintf(f,
"%s </pdb:commodity>\n", istr);
2792 fprintf(f,
"%s <pdb:currency pointer=%p>\n", istr, currency);
2794 str = str ? str :
"(null)";
2795 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2797 str = str ? str :
"(null)";
2798 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2799 fprintf(f,
"%s </pdb:currency>\n", istr);
2800 str = source_names[gnc_price_get_source(p)];
2801 str = str ? str :
"invalid";
2802 fprintf(f,
"%s %s\n", istr, str);
2803 str = gnc_price_get_typestr(p);
2804 str = str ? str :
"(null)";
2805 fprintf(f,
"%s %s\n", istr, str);
2807 fprintf(f,
"%s</pdb:price>\n", istr);
2813 print_pricedb_adapter(GNCPrice *p, gpointer user_data)
2815 FILE *f = (FILE *) user_data;
2825 PERR(
"nullptr PriceDB\n");
2830 PERR(
"nullptr FILE*\n");
2834 fprintf(f,
"<gnc:pricedb>\n");
2836 fprintf(f,
"</gnc:pricedb>\n");
2843 pricedb_book_begin (
QofBook *book)
2845 gnc_pricedb_create(book);
2849 pricedb_book_end (
QofBook *book)
2857 qof_collection_set_data(col,
nullptr);
2872 void (*func)(GNCPrice *p, gpointer user_data);
2878 void_pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2880 GList *price_list = (GList *) val;
2883 g_list_foreach (price_list, (GFunc)foreach_data->func, foreach_data->user_data);
2887 void_pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2889 GHashTable *currencies_hash = (GHashTable *) val;
2890 g_hash_table_foreach(currencies_hash, void_pricedb_foreach_pricelist, user_data);
2894 void_unstable_price_traversal(GNCPriceDB *db,
2895 void (*f)(GNCPrice *p, gpointer user_data),
2900 if (!db || !f)
return;
2901 foreach_data.func = f;
2902 foreach_data.user_data = user_data;
2904 g_hash_table_foreach(db->commodity_hash,
2905 void_pricedb_foreach_currencies_hash,
2915 void_unstable_price_traversal(db,
2916 (
void (*)(GNCPrice *, gpointer)) cb,
2922 #ifdef DUMP_FUNCTIONS 2924 static void price_list_dump(GList *price_list,
const char *tag);
2928 price_printable(gpointer obj)
2930 auto pr =
static_cast<GNCPrice*
>(obj);
2931 gnc_commodity *commodity;
2932 gnc_commodity *currency;
2933 static char buff[2048];
2938 #ifdef DUMP_FUNCTIONS 2942 price_list_dump(
nullptr,
"");
2948 commodity = gnc_price_get_commodity(pr);
2949 currency = gnc_price_get_currency(pr);
2951 g_snprintf (buff, 2048,
"%s %s / %s on %s", val,
2960 #ifdef DUMP_FUNCTIONS 2963 price_list_dump(GList *price_list,
const char *tag)
2967 printf(
"Price list %s\n", tag);
2968 for (node = price_list; node !=
nullptr; node = node->next)
2970 printf(
"%s\n", price_printable(node->data));
2982 static QofObject price_object_def =
2985 DI(.e_type = ) GNC_ID_PRICE,
2986 DI(.type_label = ) "Price",
2987 DI(.create = ) price_create,
2988 DI(.book_begin = )
nullptr,
2989 DI(.book_end = )
nullptr,
2992 DI(.foreach = ) price_foreach,
2993 DI(.printable = ) price_printable,
2994 DI(.version_cmp = )
nullptr,
2997 static QofObject pricedb_object_def =
3000 DI(.e_type = ) GNC_ID_PRICEDB,
3001 DI(.type_label = ) "PriceDB",
3002 DI(.create = )
nullptr,
3003 DI(.book_begin = ) pricedb_book_begin,
3004 DI(.book_end = ) pricedb_book_end,
3007 DI(.foreach = )
nullptr,
3008 DI(.printable = )
nullptr,
3009 DI(.version_cmp = )
nullptr,
3013 gnc_pricedb_register (
void)
3015 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.
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.
An exact-rational-number library for gnucash.
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.
gnc_ymd year_month_day() const
Get the year, month, and day from the date as a gnc_ymd.
gint 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 n)
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
gdouble gnc_numeric_to_double(gnc_numeric n)
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.
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
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 x, gnc_numeric y, 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 a)
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.