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);
544 gnc_price_set_typestr(GNCPrice *p,
const char* type)
547 if (g_strcmp0(p->type, type) != 0)
549 gnc_price_begin_edit (p);
550 CACHE_REPLACE(p->type, type);
551 gnc_price_set_dirty(p);
552 gnc_price_commit_edit (p);
557 gnc_price_set_value(GNCPrice *p, gnc_numeric value)
562 gnc_price_begin_edit (p);
564 gnc_price_set_dirty(p);
565 gnc_price_commit_edit (p);
573 gnc_price_lookup (
const GncGUID *guid, QofBook *book)
577 if (!guid || !book)
return nullptr;
583 gnc_price_get_commodity(
const GNCPrice *p)
585 if (!p)
return nullptr;
590 gnc_price_get_time64(
const GNCPrice *p)
592 return p ? p->tmspec : 0;
596 gnc_price_get_source(
const GNCPrice *p)
598 if (!p)
return PRICE_SOURCE_INVALID;
603 gnc_price_get_source_string(
const GNCPrice *p)
605 if (!p)
return nullptr;
606 return source_names[p->source];
610 gnc_price_get_typestr(
const GNCPrice *p)
612 if (!p)
return nullptr;
617 gnc_price_get_value(
const GNCPrice *p)
621 PERR(
"price nullptr.\n");
622 return gnc_numeric_zero();
628 gnc_price_get_currency(
const GNCPrice *p)
630 if (!p)
return nullptr;
635 gnc_price_equal (
const GNCPrice *p1,
const GNCPrice *p2)
639 if (p1 == p2)
return TRUE;
640 if (!p1 || !p2)
return FALSE;
643 gnc_price_get_commodity (p2)))
647 gnc_price_get_currency (p2)))
650 time1 = gnc_price_get_time64 (p1);
651 time2 = gnc_price_get_time64 (p2);
656 if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
659 if (g_strcmp0 (gnc_price_get_typestr (p1),
660 gnc_price_get_typestr (p2)) != 0)
664 gnc_price_get_value (p2)))
674 compare_prices_by_date(gconstpointer a, gconstpointer b)
679 if (!a && !b)
return 0;
683 time_a = gnc_price_get_time64((GNCPrice *) a);
684 time_b = gnc_price_get_time64((GNCPrice *) b);
687 result = time64_cmp(time_b, time_a);
688 if (result)
return result;
691 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
692 gnc_price_get_guid((GNCPrice *) b));
696 price_is_duplicate (
const GNCPrice *p_price,
const GNCPrice *c_price)
708 if (!prices || !p)
return FALSE;
711 if (check_dupl && g_list_find_custom (*prices, p, (GCompareFunc)price_is_duplicate))
714 auto result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
718 *prices = result_list;
726 GList *found_element;
728 if (!prices || !p)
return FALSE;
730 found_element = g_list_find(*prices, p);
731 if (!found_element)
return TRUE;
733 result_list = g_list_remove_link(*prices, found_element);
735 g_list_free(found_element);
737 *prices = result_list;
748 gnc_price_list_equal(PriceList *prices1, PriceList *prices2)
750 if (prices1 == prices2)
return TRUE;
752 for (
auto n1 = prices1, n2 = prices2; n1 || n2; n1 = g_list_next (n1), n2 = g_list_next (n2))
756 PINFO (
"prices2 has extra prices");
761 PINFO (
"prices1 has extra prices");
764 if (!gnc_price_equal (static_cast<GNCPrice*>(n1->data), static_cast<GNCPrice*>(n2->data)))
783 QOF_GOBJECT_IMPL(gnc_pricedb, GNCPriceDB, QOF_TYPE_INSTANCE)
786 gnc_pricedb_init(GNCPriceDB* pdb)
788 pdb->reset_nth_price_cache = FALSE;
792 gnc_pricedb_dispose_real (GObject *pdbp)
797 gnc_pricedb_finalize_real(GObject* pdbp)
802 gnc_pricedb_create(QofBook * book)
807 g_return_val_if_fail (book,
nullptr);
816 PWARN (
"A price database already exists for this book!");
820 result =
static_cast<GNCPriceDB*
>(g_object_new(GNC_TYPE_PRICEDB,
nullptr));
827 qof_collection_set_data (col, result);
829 result->commodity_hash = g_hash_table_new(
nullptr,
nullptr);
830 g_return_val_if_fail (result->commodity_hash,
nullptr);
835 destroy_pricedb_currency_hash_data(gpointer key,
839 GList *price_list = (GList *) data;
843 for (node = price_list; node; node = node->next)
845 p =
static_cast<GNCPrice*
>(node->data);
854 destroy_pricedb_commodity_hash_data(gpointer key,
858 GHashTable *currency_hash = (GHashTable *) data;
859 if (!currency_hash)
return;
860 g_hash_table_foreach (currency_hash,
861 destroy_pricedb_currency_hash_data,
863 g_hash_table_destroy(currency_hash);
870 if (db->commodity_hash)
872 g_hash_table_foreach (db->commodity_hash,
873 destroy_pricedb_commodity_hash_data,
876 g_hash_table_destroy (db->commodity_hash);
877 db->commodity_hash =
nullptr;
885 db->bulk_update = bulk_update;
906 if (!col)
return nullptr;
915 if (!book)
return nullptr;
923 num_prices_helper (GNCPrice *p, gpointer user_data)
925 auto count =
static_cast<guint*
>(user_data);
952 gnc_commodity *commodity;
956 pricedb_equal_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
959 auto currency =
static_cast<gnc_commodity*
>(key);
960 auto price_list1 =
static_cast<GList*
>(val);
962 equal_data->commodity,
965 if (!gnc_price_list_equal (price_list1, price_list2))
966 equal_data->equal = FALSE;
972 pricedb_equal_foreach_currencies_hash (gpointer key, gpointer val,
975 auto currencies_hash =
static_cast<GHashTable*
>(val);
978 equal_data->commodity =
static_cast<gnc_commodity*
>(key);
980 g_hash_table_foreach (currencies_hash,
981 pricedb_equal_foreach_pricelist,
990 if (db1 == db2)
return TRUE;
994 PWARN (
"one is nullptr");
998 equal_data.equal = TRUE;
999 equal_data.db2 = db2;
1001 g_hash_table_foreach (db1->commodity_hash,
1002 pricedb_equal_foreach_currencies_hash,
1005 return equal_data.equal;
1013 add_price(GNCPriceDB *db, GNCPrice *p)
1018 gnc_commodity *commodity;
1019 gnc_commodity *currency;
1020 GHashTable *currency_hash;
1022 if (!db || !p)
return FALSE;
1023 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1029 PERR (
"attempted to mix up prices across different books");
1034 commodity = gnc_price_get_commodity(p);
1037 PWARN(
"no commodity");
1041 currency = gnc_price_get_currency(p);
1044 PWARN(
"no currency");
1048 if (!db->commodity_hash)
1050 LEAVE (
"no commodity hash found ");
1057 if (!db->bulk_update)
1060 p->currency, p->tmspec);
1061 if (old_price !=
nullptr)
1063 if (p->source > old_price->source)
1066 LEAVE (
"Better price already in DB.");
1073 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1076 currency_hash = g_hash_table_new(
nullptr,
nullptr);
1077 g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
1080 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1083 LEAVE (
"gnc_price_list_insert failed");
1089 LEAVE (
" no price list");
1093 g_hash_table_insert(currency_hash, currency, price_list);
1098 LEAVE (
"db=%p, pr=%p dirty=%d dextroying=%d commodity=%s/%s currency_hash=%p",
1114 if (!db || !p)
return FALSE;
1116 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1120 if (FALSE == add_price(db, p))
1122 LEAVE (
" failed to add price");
1127 qof_instance_set_dirty(&db->inst);
1130 LEAVE (
"db=%p, pr=%p dirty=%d destroying=%d",
1142 remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup)
1145 gnc_commodity *commodity;
1146 gnc_commodity *currency;
1147 GHashTable *currency_hash;
1149 if (!db || !p)
return FALSE;
1150 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1154 commodity = gnc_price_get_commodity(p);
1157 LEAVE (
" no commodity");
1160 currency = gnc_price_get_currency(p);
1163 LEAVE (
" no currency");
1166 if (!db->commodity_hash)
1168 LEAVE (
" no commodity hash");
1172 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1175 LEAVE (
" no currency hash");
1180 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1185 LEAVE (
" cannot remove price list");
1193 g_hash_table_insert(currency_hash, currency, price_list);
1197 g_hash_table_remove(currency_hash, currency);
1204 guint num_currencies = g_hash_table_size (currency_hash);
1205 if (0 == num_currencies)
1207 g_hash_table_remove (db->commodity_hash, commodity);
1208 g_hash_table_destroy (currency_hash);
1214 LEAVE (
"db=%p, pr=%p", db, p);
1223 memset(datebuff, 0,
sizeof(datebuff));
1224 if (!db || !p)
return FALSE;
1225 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1231 DEBUG(
"Remove Date is %s, Commodity is %s, Source is %s", datebuff,
1233 gnc_price_get_source_string (p));
1235 rc = remove_price (db, p, TRUE);
1237 qof_instance_set_dirty(&db->inst);
1241 gnc_price_begin_edit (p);
1242 qof_instance_set_destroying(p, TRUE);
1243 gnc_price_commit_edit (p);
1246 LEAVE (
"db=%p, pr=%p", db, p);
1255 gboolean delete_user;
1256 gboolean delete_app;
1261 check_one_price_date (GNCPrice *price, gpointer user_data)
1267 ENTER(
"price %p (%s), data %p", price,
1271 source = gnc_price_get_source (price);
1273 if ((source == PRICE_SOURCE_FQ) && data->delete_fq)
1274 PINFO (
"Delete Quote Source");
1275 else if ((source == PRICE_SOURCE_USER_PRICE) && data->delete_user)
1276 PINFO (
"Delete User Source");
1277 else if ((source != PRICE_SOURCE_FQ) && (source != PRICE_SOURCE_USER_PRICE) && data->delete_app)
1278 PINFO (
"Delete App Source");
1281 LEAVE(
"Not a matching source");
1285 time = gnc_price_get_time64 (price);
1289 DEBUG(
"checking date %s", buf);
1291 if (time < data->cutoff)
1293 data->list = g_slist_prepend(data->list, price);
1294 DEBUG(
"will delete");
1301 pricedb_remove_foreach_pricelist (gpointer key,
1305 GList *price_list = (GList *) val;
1306 GList *node = price_list;
1309 ENTER(
"key %p, value %p, data %p", key, val, user_data);
1312 g_list_foreach(node, (GFunc)check_one_price_date, data);
1318 compare_prices_by_commodity_date (gconstpointer a, gconstpointer b)
1321 gnc_commodity *comma;
1322 gnc_commodity *commb;
1323 gnc_commodity *curra;
1324 gnc_commodity *currb;
1327 if (!a && !b)
return 0;
1332 comma = gnc_price_get_commodity ((GNCPrice *) a);
1333 commb = gnc_price_get_commodity ((GNCPrice *) b);
1338 curra = gnc_price_get_currency ((GNCPrice *) a);
1339 currb = gnc_price_get_currency ((GNCPrice *) b);
1344 time_a = gnc_price_get_time64((GNCPrice *) a);
1345 time_b = gnc_price_get_time64((GNCPrice *) b);
1348 result = time64_cmp(time_b, time_a);
1349 if (result)
return result;
1352 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
1353 gnc_price_get_guid((GNCPrice *) b));
1357 price_commodity_and_currency_equal (GNCPrice *a, GNCPrice *b)
1359 gboolean ret_comm = FALSE;
1360 gboolean ret_curr = FALSE;
1368 return (ret_comm && ret_curr);
1372 gnc_pricedb_remove_old_prices_pinfo (GNCPrice *price, gboolean keep_message)
1377 if (g_date_valid (&price_date))
1383 PINFO(
"#### Keep price with date %s, commodity is %s, currency is %s", date_buf,
1388 PINFO(
"## Remove price with date %s", date_buf);
1391 PINFO(
"Keep price date is invalid");
1395 clone_price (GNCPrice **price, GNCPrice *source_price)
1399 if (!source_price)
return;
1400 if (price ==
nullptr)
return;
1409 gnc_pricedb_remove_old_prices_pinfo (source_price, TRUE);
1413 roundUp (gint numToRound, gint multiple)
1420 remainder = numToRound % multiple;
1424 return numToRound + multiple - remainder;
1428 get_fiscal_quarter (GDate *date, GDateMonth fiscal_start)
1430 GDateMonth month = g_date_get_month (date);
1432 gint q = ((roundUp (22 - fiscal_start + month, 3)/3) % 4) + 1;
1434 PINFO(
"Return fiscal quarter is %d", q);
1439 gnc_pricedb_process_removal_list (GNCPriceDB *db, GDate *fiscal_end_date,
1443 gboolean save_first_price = FALSE;
1444 gint saved_test_value = 0, next_test_value = 0;
1445 GNCPrice *cloned_price =
nullptr;
1446 GDateMonth fiscal_month_start;
1447 GDate *tmp_date = g_date_new_dmy (g_date_get_day (fiscal_end_date),
1448 g_date_get_month (fiscal_end_date),
1449 g_date_get_year (fiscal_end_date));
1452 g_date_subtract_months (tmp_date, 12);
1453 fiscal_month_start =
static_cast<GDateMonth
>(g_date_get_month (tmp_date) + 1);
1454 g_date_free (tmp_date);
1457 data.list = g_slist_sort (data.list, compare_prices_by_commodity_date);
1460 for (item = data.list; item; item = g_slist_next(item))
1462 GDate saved_price_date;
1463 GDate next_price_date;
1464 auto price =
static_cast<GNCPrice*
>(item->data);
1467 if (keep == PRICE_REMOVE_KEEP_NONE)
1469 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1474 save_first_price = !price_commodity_and_currency_equal (price, cloned_price);
1475 if (save_first_price == TRUE)
1477 clone_price (&cloned_price, price);
1482 saved_price_date =
time64_to_gdate (gnc_price_get_time64 (cloned_price));
1486 if (keep == PRICE_REMOVE_KEEP_LAST_PERIOD && save_first_price == FALSE)
1488 GDate *saved_fiscal_end = g_date_new_dmy (g_date_get_day (&saved_price_date),
1489 g_date_get_month (&saved_price_date),
1490 g_date_get_year (&saved_price_date));
1492 GDate *next_fiscal_end = g_date_new_dmy (g_date_get_day (&next_price_date),
1493 g_date_get_month (&next_price_date),
1494 g_date_get_year (&next_price_date));
1499 saved_test_value = g_date_get_year (saved_fiscal_end);
1500 next_test_value = g_date_get_year (next_fiscal_end);
1502 PINFO(
"Keep last price in fiscal year");
1504 g_date_free (saved_fiscal_end);
1505 g_date_free (next_fiscal_end);
1509 if (keep == PRICE_REMOVE_KEEP_LAST_QUARTERLY && save_first_price == FALSE)
1511 saved_test_value = get_fiscal_quarter (&saved_price_date, fiscal_month_start);
1512 next_test_value = get_fiscal_quarter (&next_price_date, fiscal_month_start);
1514 PINFO(
"Keep last price in fiscal quarter");
1518 if (keep == PRICE_REMOVE_KEEP_LAST_MONTHLY && save_first_price == FALSE)
1520 saved_test_value = g_date_get_month (&saved_price_date);
1521 next_test_value = g_date_get_month (&next_price_date);
1523 PINFO(
"Keep last price of every month");
1527 if (keep == PRICE_REMOVE_KEEP_LAST_WEEKLY && save_first_price == FALSE)
1529 saved_test_value = g_date_get_iso8601_week_of_year (&saved_price_date);
1530 next_test_value = g_date_get_iso8601_week_of_year (&next_price_date);
1532 PINFO(
"Keep last price of every week");
1536 if (saved_test_value == next_test_value)
1538 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1542 clone_price (&cloned_price, price);
1550 GDate *fiscal_end_date,
time64 cutoff,
1551 PriceRemoveSourceFlags source,
1552 PriceRemoveKeepOptions keep)
1557 memset (datebuff, 0,
sizeof(datebuff));
1560 data.cutoff = cutoff;
1561 data.list =
nullptr;
1562 data.delete_fq = FALSE;
1563 data.delete_user = FALSE;
1564 data.delete_app = FALSE;
1566 ENTER(
"Remove Prices for Source %d, keeping %d", source, keep);
1569 if (source & PRICE_REMOVE_SOURCE_APP)
1570 data.delete_app = TRUE;
1572 if (source & PRICE_REMOVE_SOURCE_FQ)
1573 data.delete_fq = TRUE;
1575 if (source & PRICE_REMOVE_SOURCE_USER)
1576 data.delete_user = TRUE;
1579 for (node = g_list_first (comm_list); node; node = g_list_next (node))
1581 auto currencies_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, node->data));
1582 g_hash_table_foreach (currencies_hash, pricedb_remove_foreach_pricelist, &data);
1585 if (data.list ==
nullptr)
1587 LEAVE(
"Empty price list");
1591 DEBUG(
"Number of Prices in list is %d, Cutoff date is %s",
1592 g_slist_length (data.list), datebuff);
1595 if (fiscal_end_date ==
nullptr || !g_date_valid (fiscal_end_date))
1598 GDate end_this_year;
1599 g_date_set_dmy (&end_this_year, 31, GDateMonth(12), ymd.year);
1600 gnc_pricedb_process_removal_list (db, &end_this_year, data, keep);
1603 gnc_pricedb_process_removal_list (db, fiscal_end_date, data, keep);
1605 g_slist_free (data.list);
1613 static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b);
1616 hash_values_helper(gpointer key, gpointer value, gpointer data)
1618 auto l =
static_cast<GList**
>(data);
1622 new_l = pricedb_price_list_merge(*l, static_cast<PriceList*>(value));
1627 *l = g_list_copy (static_cast<GList*>(value));
1631 price_list_from_hashtable (GHashTable *hash,
const gnc_commodity *currency)
1633 GList *price_list =
nullptr, *result = nullptr ;
1636 price_list =
static_cast<GList*
>(g_hash_table_lookup(hash, currency));
1639 LEAVE (
" no price list");
1642 result = g_list_copy (price_list);
1646 g_hash_table_foreach(hash, hash_values_helper, (gpointer)&result);
1652 pricedb_price_list_merge (PriceList *a, PriceList *b)
1654 PriceList *merged_list =
nullptr;
1658 while (next_a || next_b)
1660 if (next_a ==
nullptr)
1662 merged_list = g_list_prepend (merged_list, next_b->data);
1663 next_b = next_b->next;
1665 else if (next_b ==
nullptr)
1667 merged_list = g_list_prepend (merged_list, next_a->data);
1668 next_a = next_a->next;
1671 else if (compare_prices_by_date (next_a->data, next_b->data) < 0)
1673 merged_list = g_list_prepend (merged_list, next_a->data);
1674 next_a = next_a->next;
1678 merged_list = g_list_prepend (merged_list, next_b->data);
1679 next_b = next_b->next;
1682 return g_list_reverse (merged_list);
1686 pricedb_get_prices_internal(GNCPriceDB *db,
const gnc_commodity *commodity,
1687 const gnc_commodity *currency, gboolean bidi)
1689 GHashTable *forward_hash =
nullptr, *reverse_hash =
nullptr;
1690 PriceList *forward_list =
nullptr, *reverse_list =
nullptr;
1691 g_return_val_if_fail (db !=
nullptr,
nullptr);
1692 g_return_val_if_fail (commodity !=
nullptr,
nullptr);
1693 forward_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1694 if (currency && bidi)
1695 reverse_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, currency));
1696 if (!forward_hash && !reverse_hash)
1698 LEAVE (
" no currency hash");
1702 forward_list = price_list_from_hashtable (forward_hash, currency);
1703 if (currency && reverse_hash)
1705 reverse_list = price_list_from_hashtable (reverse_hash, commodity);
1715 PriceList *merged_list;
1716 merged_list = pricedb_price_list_merge (forward_list, reverse_list);
1717 g_list_free (forward_list);
1718 g_list_free (reverse_list);
1719 forward_list = merged_list;
1723 forward_list = reverse_list;
1728 return forward_list;
1732 const gnc_commodity *commodity,
1733 const gnc_commodity *currency)
1738 if (!db || !commodity || !currency)
return nullptr;
1739 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
1741 price_list = pricedb_get_prices_internal(db, commodity, currency, TRUE);
1742 if (!price_list)
return nullptr;
1746 result =
static_cast<GNCPrice*
>(price_list->data);
1748 g_list_free (price_list);
1749 LEAVE(
"price is %p", result);
1756 const gnc_commodity *com;
1771 price_list_scan_any_currency(GList *price_list, gpointer data)
1780 auto price =
static_cast<GNCPrice*
>(price_list->data);
1781 com = gnc_price_get_commodity(price);
1782 cur = gnc_price_get_currency(price);
1786 if (com != helper->com && cur != helper->com)
1792 for (
auto node = price_list; node; node = g_list_next (node))
1794 price =
static_cast<GNCPrice*
>(node->data);
1795 time64 price_t = gnc_price_get_time64(price);
1796 if (price_t < helper->t)
1801 auto prev_price =
static_cast<GNCPrice*
>(node->prev->data);
1803 *helper->list = g_list_prepend(*helper->list, prev_price);
1807 *helper->list = g_list_prepend(*helper->list, price);
1811 else if (node->next ==
nullptr)
1815 *helper->list = g_list_prepend(*helper->list, price);
1826 latest_before (PriceList *prices,
const gnc_commodity* target,
time64 t)
1828 GList *node, *found_coms =
nullptr, *retval =
nullptr;
1829 for (node = prices; node !=
nullptr; node = g_list_next(node))
1831 GNCPrice *price = (GNCPrice*)node->data;
1832 gnc_commodity *com = gnc_price_get_commodity(price);
1833 gnc_commodity *cur = gnc_price_get_currency(price);
1834 time64 price_t = gnc_price_get_time64(price);
1837 (com == target && g_list_find (found_coms, cur)) ||
1838 (cur == target && g_list_find (found_coms, com)))
1842 retval = g_list_prepend (retval, price);
1843 found_coms = g_list_prepend (found_coms, com == target ? cur : com);
1845 g_list_free (found_coms);
1846 return g_list_reverse(retval);
1850 find_comtime(GPtrArray* array, gnc_commodity *com)
1852 unsigned int index = 0;
1853 GNCPrice** retval =
nullptr;
1854 for (index = 0; index < array->len; ++index)
1856 auto price_p =
static_cast<GNCPrice**
>(g_ptr_array_index(array, index));
1857 if (gnc_price_get_commodity(*price_p) == com ||
1858 gnc_price_get_currency(*price_p) == com)
1865 add_nearest_price(GList *target_list, GPtrArray *price_array, GNCPrice *price,
1866 const gnc_commodity *target,
time64 t)
1868 gnc_commodity *com = gnc_price_get_commodity(price);
1869 gnc_commodity *cur = gnc_price_get_currency(price);
1870 time64 price_t = gnc_price_get_time64(price);
1871 gnc_commodity *other = com == target ? cur : com;
1872 GNCPrice **com_price = find_comtime(price_array, other);
1874 if (com_price ==
nullptr)
1876 com_price = (GNCPrice**)g_slice_new(gpointer);
1878 g_ptr_array_add(price_array, com_price);
1884 target_list = g_list_prepend(target_list, price);
1888 com_t = gnc_price_get_time64(*com_price);
1901 time64 com_diff = com_t - t;
1902 time64 price_diff = t - price_t;
1903 if (com_diff < price_diff)
1906 target_list = g_list_prepend(target_list, *com_price);
1911 target_list = g_list_prepend(target_list, price);
1919 nearest_to (PriceList *prices,
const gnc_commodity* target,
time64 t)
1921 GList *node, *retval =
nullptr;
1922 const guint prealloc_size = 5;
1927 GPtrArray *price_array = g_ptr_array_sized_new(prealloc_size);
1929 for (node = prices; node !=
nullptr; node = g_list_next(node))
1931 GNCPrice *price = (GNCPrice*)node->data;
1932 retval = add_nearest_price(retval, price_array, price, target, t);
1938 for (index = 0; index < price_array->len; ++index)
1940 auto com_price =
static_cast<GNCPrice**
>(g_ptr_array_index(price_array, index));
1941 time64 price_t = gnc_price_get_time64(*com_price);
1945 retval = g_list_prepend(retval, *com_price);
1948 g_ptr_array_free(price_array, TRUE);
1949 return g_list_sort(retval, compare_prices_by_date);
1956 const gnc_commodity *commodity)
1964 const gnc_commodity *commodity,
1967 GList *prices =
nullptr, *result;
1971 if (!db || !commodity)
return nullptr;
1972 ENTER (
"db=%p commodity=%p", db, commodity);
1974 pricedb_pricelist_traversal(db, price_list_scan_any_currency, &helper);
1975 prices = g_list_sort(prices, compare_prices_by_date);
1976 result = nearest_to(prices, commodity, t);
1984 const gnc_commodity *commodity,
1987 GList *prices =
nullptr, *result;
1991 if (!db || !commodity)
return nullptr;
1992 ENTER (
"db=%p commodity=%p", db, commodity);
1994 pricedb_pricelist_traversal(db, price_list_scan_any_currency,
1996 prices = g_list_sort(prices, compare_prices_by_date);
1997 result = latest_before(prices, commodity, t);
2009 const gnc_commodity *commodity,
2010 const gnc_commodity *currency)
2013 GHashTable *currency_hash;
2016 if (!db || !commodity)
return FALSE;
2017 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2018 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
2021 LEAVE(
"no, no currency_hash table");
2027 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
2033 LEAVE(
"no, no price list");
2037 size = g_hash_table_size (currency_hash);
2038 LEAVE(
"%s", size > 0 ?
"yes" :
"no");
2048 const gnc_commodity *commodity,
2049 const gnc_commodity *currency)
2051 if (!db || !commodity)
return nullptr;
2052 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2053 auto result = pricedb_get_prices_internal (db, commodity, currency, FALSE);
2054 if (!result)
return nullptr;
2063 price_count_helper(gpointer key, gpointer value, gpointer data)
2065 auto result =
static_cast<int*
>(data);
2066 auto price_list =
static_cast<GList*
>(value);
2068 *result += g_list_length(price_list);
2073 const gnc_commodity *c)
2076 GHashTable *currency_hash;
2078 if (!db || !c)
return 0;
2079 ENTER (
"db=%p commodity=%p", db, c);
2081 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, c));
2084 g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result);
2087 LEAVE (
"count=%d", result);
2093 list_combine (gpointer element, gpointer data)
2095 GList *list = *(GList**)data;
2096 auto lst =
static_cast<GList*
>(element);
2097 if (list ==
nullptr)
2098 *(GList**)data = g_list_copy (lst);
2101 GList *new_list = g_list_concat (list, g_list_copy (lst));
2102 *(GList**)data = new_list;
2120 const gnc_commodity *c,
2123 static const gnc_commodity *last_c =
nullptr;
2124 static GList *prices =
nullptr;
2126 GNCPrice *result =
nullptr;
2127 GHashTable *currency_hash;
2128 g_return_val_if_fail (GNC_IS_COMMODITY (c),
nullptr);
2130 if (!db || !c || n < 0)
return nullptr;
2133 if (last_c && prices && last_c == c && db->reset_nth_price_cache == FALSE)
2135 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2136 LEAVE (
"price=%p", result);
2144 g_list_free (prices);
2148 db->reset_nth_price_cache = FALSE;
2150 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, c));
2153 GList *currencies = g_hash_table_get_values (currency_hash);
2154 g_list_foreach (currencies, list_combine, &prices);
2155 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2156 g_list_free (currencies);
2159 LEAVE (
"price=%p", result);
2164 gnc_pricedb_nth_price_reset_cache (GNCPriceDB *db)
2167 db->reset_nth_price_cache = TRUE;
2172 const gnc_commodity *c,
2173 const gnc_commodity *currency,
2176 return lookup_nearest_in_time(db, c, currency, t, TRUE);
2180 lookup_nearest_in_time(GNCPriceDB *db,
2181 const gnc_commodity *c,
2182 const gnc_commodity *currency,
2187 GNCPrice *current_price =
nullptr;
2188 GNCPrice *next_price =
nullptr;
2189 GNCPrice *result =
nullptr;
2191 if (!db || !c || !currency)
return nullptr;
2192 if (t == INT64_MAX)
return nullptr;
2193 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2194 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2195 if (!price_list)
return nullptr;
2198 current_price =
static_cast<GNCPrice*
>(price_list->data);
2202 for (
auto item = price_list; item; item = g_list_next (item))
2204 auto p =
static_cast<GNCPrice*
>(item->data);
2205 time64 price_time = gnc_price_get_time64(p);
2206 if (price_time <= t)
2208 next_price =
static_cast<GNCPrice*
>(item->data);
2211 current_price =
static_cast<GNCPrice*
>(item->data);
2219 result = current_price;
2227 if (price_day != t_day)
2235 time64 current_t = gnc_price_get_time64(current_price);
2236 time64 next_t = gnc_price_get_time64(next_price);
2237 time64 diff_current = current_t - t;
2238 time64 diff_next = next_t - t;
2239 time64 abs_current = llabs(diff_current);
2240 time64 abs_next = llabs(diff_next);
2248 if (current_day == t_day)
2250 if (next_day == t_day)
2253 if (abs_current < abs_next)
2254 result = current_price;
2256 result = next_price;
2260 result = current_price;
2262 else if (next_day == t_day)
2264 result = next_price;
2271 if (abs_current < abs_next)
2273 result = current_price;
2277 result = next_price;
2284 g_list_free (price_list);
2291 const gnc_commodity *c,
2292 const gnc_commodity *currency,
2295 return lookup_nearest_in_time(db, c, currency, t, FALSE);
2299 static int price_time64_less_or_equal (GNCPrice *p,
time64 *time)
2301 return !(gnc_price_get_time64 (p) <= *time);
2306 const gnc_commodity *c,
2307 const gnc_commodity *currency,
2310 GNCPrice *current_price =
nullptr;
2311 if (!db || !c || !currency)
return nullptr;
2312 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2313 auto price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2314 if (!price_list)
return nullptr;
2315 auto p = g_list_find_custom (price_list, &t, (GCompareFunc)price_time64_less_or_equal);
2318 current_price = GNC_PRICE (p->data);
2321 g_list_free (price_list);
2323 return current_price;
2334 extract_common_prices (PriceList *from_prices, PriceList *to_prices,
2335 const gnc_commodity *from,
const gnc_commodity *to)
2338 GList *from_node =
nullptr, *to_node =
nullptr;
2339 GNCPrice *from_price =
nullptr, *to_price =
nullptr;
2341 for (from_node = from_prices; from_node !=
nullptr;
2342 from_node = g_list_next(from_node))
2344 for (to_node = to_prices; to_node !=
nullptr;
2345 to_node = g_list_next(to_node))
2347 gnc_commodity *to_com, *to_cur;
2348 gnc_commodity *from_com, *from_cur;
2349 to_price = GNC_PRICE(to_node->data);
2350 from_price = GNC_PRICE(from_node->data);
2351 to_com = gnc_price_get_commodity (to_price);
2352 to_cur = gnc_price_get_currency (to_price);
2353 from_com = gnc_price_get_commodity (from_price);
2354 from_cur = gnc_price_get_currency (from_price);
2355 if (((to_com == from_com || to_com == from_cur) &&
2356 (to_com != from && to_com != to)) ||
2357 ((to_cur == from_com || to_cur == from_cur) &&
2358 (to_cur != from && to_cur != to)))
2361 from_price =
nullptr;
2363 if (to_price !=
nullptr && from_price !=
nullptr)
2366 if (from_price ==
nullptr || to_price ==
nullptr)
2370 retval.from = from_price;
2371 retval.to = to_price;
2376 convert_price (
const gnc_commodity *from,
const gnc_commodity *to,
PriceTuple tuple)
2378 gnc_commodity *p1_com = gnc_price_get_commodity (tuple.from);
2379 gnc_commodity *p1_cur = gnc_price_get_currency (tuple.from);
2380 gnc_commodity *p2_com = gnc_price_get_commodity (tuple.to);
2381 gnc_commodity *p2_cur = gnc_price_get_currency (tuple.to);
2382 gnc_numeric p1_val = gnc_price_get_value (tuple.from);
2383 gnc_numeric p2_val = gnc_price_get_value (tuple.to);
2388 if ((p1_com == from && p2_com == to) ||
2389 (p1_cur == from && p2_cur == to))
2409 indirect_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2410 const gnc_commodity *to,
time64 t, gboolean before_date)
2412 GList *from_prices =
nullptr, *to_prices =
nullptr;
2414 gnc_numeric zero = gnc_numeric_zero();
2425 else if (before_date)
2437 if (!from_prices || !to_prices)
2443 tuple = extract_common_prices (from_prices, to_prices, from, to);
2447 return convert_price (from, to, tuple);
2453 direct_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2454 const gnc_commodity *to,
time64 t, gboolean before_date)
2457 gnc_numeric retval = gnc_numeric_zero();
2459 if (!from || !to)
return retval;
2463 else if (before_date)
2468 if (!price)
return retval;
2470 retval = gnc_price_get_value (price);
2472 if (gnc_price_get_commodity (price) != from)
2480 get_nearest_price (GNCPriceDB *pdb,
2481 const gnc_commodity *orig_curr,
2482 const gnc_commodity *new_curr,
2489 return gnc_numeric_create (1, 1);
2492 price = direct_price_conversion (pdb, orig_curr, new_curr, t, before);
2498 price = indirect_price_conversion (pdb, orig_curr, new_curr, t, before);
2505 const gnc_commodity *orig_currency,
2506 const gnc_commodity *new_currency,
2509 return get_nearest_price (pdb, orig_currency, new_currency, t, TRUE);
2514 const gnc_commodity *orig_currency,
2515 const gnc_commodity *new_currency,
2518 return get_nearest_price (pdb, orig_currency, new_currency, t, FALSE);
2523 const gnc_commodity *orig_currency,
2524 const gnc_commodity *new_currency)
2526 return get_nearest_price (pdb, orig_currency, new_currency, INT64_MAX, FALSE);
2530 convert_amount_at_date (GNCPriceDB *pdb,
2532 const gnc_commodity *orig_currency,
2533 const gnc_commodity *new_currency,
2535 gboolean before_date)
2542 price = get_nearest_price (pdb, orig_currency, new_currency, t, before_date);
2546 return gnc_numeric_zero ();
2558 gnc_numeric balance,
2559 const gnc_commodity *balance_currency,
2560 const gnc_commodity *new_currency)
2562 return convert_amount_at_date
2563 (pdb, balance, balance_currency, new_currency, INT64_MAX, FALSE);
2568 gnc_numeric balance,
2569 const gnc_commodity *balance_currency,
2570 const gnc_commodity *new_currency,
2573 return convert_amount_at_date
2574 (pdb, balance, balance_currency, new_currency, t, FALSE);
2579 gnc_numeric balance,
2580 const gnc_commodity *balance_currency,
2581 const gnc_commodity *new_currency,
2584 return convert_amount_at_date
2585 (pdb, balance, balance_currency, new_currency, t, TRUE);
2595 gboolean (*func)(GNCPrice *p, gpointer user_data);
2600 pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2602 GList *price_list = (GList *) val;
2606 foreach_data->ok = g_list_find_custom (price_list, foreach_data->user_data, (GCompareFunc)foreach_data->func)
2611 pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2613 GHashTable *currencies_hash = (GHashTable *) val;
2614 g_hash_table_foreach(currencies_hash, pricedb_foreach_pricelist, user_data);
2618 unstable_price_traversal(GNCPriceDB *db,
2619 gboolean (*f)(GNCPrice *p, gpointer user_data),
2624 if (!db || !f)
return FALSE;
2625 foreach_data.ok = TRUE;
2626 foreach_data.func = f;
2627 foreach_data.user_data = user_data;
2628 if (db->commodity_hash ==
nullptr)
2632 g_hash_table_foreach(db->commodity_hash,
2633 pricedb_foreach_currencies_hash,
2636 return foreach_data.ok;
2643 gboolean (*func)(GList *p, gpointer user_data);
2648 pricedb_pricelist_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2650 GList *price_list = (GList *) val;
2652 if (foreach_data->ok)
2654 foreach_data->ok = foreach_data->func(price_list, foreach_data->user_data);
2659 pricedb_pricelist_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2661 GHashTable *currencies_hash = (GHashTable *) val;
2662 g_hash_table_foreach(currencies_hash, pricedb_pricelist_foreach_pricelist, user_data);
2666 pricedb_pricelist_traversal(GNCPriceDB *db,
2667 gboolean (*f)(GList *p, gpointer user_data),
2672 if (!db || !f)
return FALSE;
2673 foreach_data.ok = TRUE;
2674 foreach_data.func = f;
2675 foreach_data.user_data = user_data;
2676 if (db->commodity_hash ==
nullptr)
2680 g_hash_table_foreach(db->commodity_hash,
2681 pricedb_pricelist_foreach_currencies_hash,
2684 return foreach_data.ok;
2688 compare_hash_entries_by_commodity_key (
const CommodityPtrPair& he_a,
const CommodityPtrPair& he_b)
2690 auto ca = he_a.first;
2691 auto cb = he_b.first;
2693 if (ca == cb || !cb)
2702 return (cmp_result < 0);
2708 stable_price_traversal(GNCPriceDB *db,
2709 gboolean (*f)(GNCPrice *p, gpointer user_data),
2712 g_return_val_if_fail (db && f,
false);
2714 auto currency_hashes = hash_table_to_vector (db->commodity_hash);
2715 std::sort (currency_hashes.begin(), currency_hashes.end(), compare_hash_entries_by_commodity_key);
2717 for (
const auto& entry : currency_hashes)
2719 auto price_lists = hash_table_to_vector (static_cast<GHashTable*>(entry.second));
2720 std::sort (price_lists.begin(), price_lists.end(), compare_hash_entries_by_commodity_key);
2722 for (
const auto& pricelist_entry : price_lists)
2723 if (g_list_find_custom (static_cast<GList*>(pricelist_entry.second), user_data, (GCompareFunc)f))
2732 GncPriceForeachFunc f,
2734 gboolean stable_order)
2736 ENTER (
"db=%p f=%p", db, f);
2739 LEAVE (
" stable order found");
2740 return stable_price_traversal(db, f, user_data);
2742 LEAVE (
" use unstable order");
2743 return unstable_price_traversal(db, f, user_data);
2753 gnc_commodity *commodity;
2754 gnc_commodity *currency;
2755 gchar *istr =
nullptr;
2761 commodity = gnc_price_get_commodity(p);
2762 currency = gnc_price_get_currency(p);
2764 if (!commodity)
return;
2765 if (!currency)
return;
2767 istr = g_strnfill(indent,
' ');
2769 fprintf(f,
"%s<pdb:price>\n", istr);
2770 fprintf(f,
"%s <pdb:commodity pointer=%p>\n", istr, commodity);
2772 str = str ? str :
"(null)";
2773 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2775 str = str ? str :
"(null)";
2776 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2777 fprintf(f,
"%s </pdb:commodity>\n", istr);
2778 fprintf(f,
"%s <pdb:currency pointer=%p>\n", istr, currency);
2780 str = str ? str :
"(null)";
2781 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2783 str = str ? str :
"(null)";
2784 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2785 fprintf(f,
"%s </pdb:currency>\n", istr);
2786 str = source_names[gnc_price_get_source(p)];
2787 str = str ? str :
"invalid";
2788 fprintf(f,
"%s %s\n", istr, str);
2789 str = gnc_price_get_typestr(p);
2790 str = str ? str :
"(null)";
2791 fprintf(f,
"%s %s\n", istr, str);
2793 fprintf(f,
"%s</pdb:price>\n", istr);
2799 print_pricedb_adapter(GNCPrice *p, gpointer user_data)
2801 FILE *f = (FILE *) user_data;
2811 PERR(
"nullptr PriceDB\n");
2816 PERR(
"nullptr FILE*\n");
2820 fprintf(f,
"<gnc:pricedb>\n");
2822 fprintf(f,
"</gnc:pricedb>\n");
2829 pricedb_book_begin (QofBook *book)
2831 gnc_pricedb_create(book);
2835 pricedb_book_end (QofBook *book)
2843 qof_collection_set_data(col,
nullptr);
2848 price_create (QofBook *book)
2858 void (*func)(GNCPrice *p, gpointer user_data);
2864 void_pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2866 GList *price_list = (GList *) val;
2869 g_list_foreach (price_list, (GFunc)foreach_data->func, foreach_data->user_data);
2873 void_pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2875 GHashTable *currencies_hash = (GHashTable *) val;
2876 g_hash_table_foreach(currencies_hash, void_pricedb_foreach_pricelist, user_data);
2880 void_unstable_price_traversal(GNCPriceDB *db,
2881 void (*f)(GNCPrice *p, gpointer user_data),
2886 if (!db || !f)
return;
2887 foreach_data.func = f;
2888 foreach_data.user_data = user_data;
2890 g_hash_table_foreach(db->commodity_hash,
2891 void_pricedb_foreach_currencies_hash,
2901 void_unstable_price_traversal(db,
2902 (
void (*)(GNCPrice *, gpointer)) cb,
2908 #ifdef DUMP_FUNCTIONS 2910 static void price_list_dump(GList *price_list,
const char *tag);
2914 price_printable(gpointer obj)
2916 auto pr =
static_cast<GNCPrice*
>(obj);
2917 gnc_commodity *commodity;
2918 gnc_commodity *currency;
2919 static char buff[2048];
2924 #ifdef DUMP_FUNCTIONS 2928 price_list_dump(
nullptr,
"");
2934 commodity = gnc_price_get_commodity(pr);
2935 currency = gnc_price_get_currency(pr);
2937 g_snprintf (buff, 2048,
"%s %s / %s on %s", val,
2946 #ifdef DUMP_FUNCTIONS 2949 price_list_dump(GList *price_list,
const char *tag)
2953 printf(
"Price list %s\n", tag);
2954 for (node = price_list; node !=
nullptr; node = node->next)
2956 printf(
"%s\n", price_printable(node->data));
2968 static QofObject price_object_def =
2971 DI(.e_type = ) GNC_ID_PRICE,
2972 DI(.type_label = ) "Price",
2973 DI(.create = ) price_create,
2974 DI(.book_begin = )
nullptr,
2975 DI(.book_end = )
nullptr,
2978 DI(.foreach = ) price_foreach,
2979 DI(.printable = ) price_printable,
2980 DI(.version_cmp = )
nullptr,
2983 static QofObject pricedb_object_def =
2986 DI(.e_type = ) GNC_ID_PRICEDB,
2987 DI(.type_label = ) "PriceDB",
2988 DI(.create = )
nullptr,
2989 DI(.book_begin = ) pricedb_book_begin,
2990 DI(.book_end = ) pricedb_book_end,
2993 DI(.foreach = )
nullptr,
2994 DI(.printable = )
nullptr,
2995 DI(.version_cmp = )
nullptr,
2999 gnc_pricedb_register (
void)
3001 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.