32 #include "gnc-datetime.hpp" 33 #include "gnc-pricedb-p.h" 34 #include <qofinstance-p.h> 37 static QofLogModule log_module = GNC_MOD_PRICE;
39 static gboolean add_price(GNCPriceDB *db, GNCPrice *p);
40 static gboolean remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup);
41 static GNCPrice *lookup_nearest_in_time(GNCPriceDB *db,
const gnc_commodity *c,
42 const gnc_commodity *currency,
43 time64 t, gboolean sameday);
45 pricedb_pricelist_traversal(GNCPriceDB *db,
46 gboolean (*f)(GList *p, gpointer user_data),
64 return a < b ? -1 : a > b ? 1 : 0;
67 using CommodityPtrPair = std::pair<const gnc_commodity*, gpointer>;
68 using CommodityPtrPairVec = std::vector<CommodityPtrPair>;
71 hash_entry_insert(
const gnc_commodity* key,
const gpointer val, CommodityPtrPairVec *result)
73 result->emplace_back (key, val);
76 static CommodityPtrPairVec
77 hash_table_to_vector (GHashTable *
table)
79 CommodityPtrPairVec result_vec;
80 result_vec.reserve (g_hash_table_size (
table));
81 g_hash_table_foreach(
table, (GHFunc)hash_entry_insert, &result_vec);
86 G_DEFINE_TYPE(GNCPrice, gnc_price, QOF_TYPE_INSTANCE)
89 gnc_price_init(GNCPrice* price)
92 price->value = gnc_numeric_zero();
93 price->type =
nullptr;
94 price->source = PRICE_SOURCE_INVALID;
104 static const char* source_names[(size_t)PRICE_SOURCE_INVALID + 1] =
112 "user:split-register",
115 "user:stock-transaction",
122 gnc_price_dispose(GObject *pricep)
124 G_OBJECT_CLASS(gnc_price_parent_class)->dispose(pricep);
128 gnc_price_finalize(GObject* pricep)
130 G_OBJECT_CLASS(gnc_price_parent_class)->finalize(pricep);
140 gnc_price_get_property(GObject*
object, guint prop_id, GValue* value, GParamSpec* pspec)
144 g_return_if_fail(GNC_IS_PRICE(
object));
146 price = GNC_PRICE(
object);
150 g_value_set_string(value, gnc_price_get_source_string(price));
153 g_value_set_string(value, price->type);
156 g_value_set_boxed(value, &price->value);
159 g_value_take_object(value, price->commodity);
162 g_value_take_object(value, price->currency);
165 g_value_set_boxed(value, &price->tmspec);
168 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
174 gnc_price_set_property(GObject*
object, guint prop_id,
const GValue* value, GParamSpec* pspec)
180 g_return_if_fail(GNC_IS_PRICE(
object));
182 price = GNC_PRICE(
object);
183 g_assert (qof_instance_get_editlevel(price));
188 gnc_price_set_source_string(price, g_value_get_string(value));
191 gnc_price_set_typestr(price, g_value_get_string(value));
194 number =
static_cast<gnc_numeric*
>(g_value_get_boxed(value));
195 gnc_price_set_value(price, *number);
198 gnc_price_set_commodity(price, static_cast<gnc_commodity*>(g_value_get_object(value)));
201 gnc_price_set_currency(price, static_cast<gnc_commodity*>(g_value_get_object(value)));
204 time =
static_cast<Time64*
>(g_value_get_boxed(value));
205 gnc_price_set_time64(price, time->t);
208 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
214 gnc_price_class_init(GNCPriceClass *klass)
216 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
218 gobject_class->dispose = gnc_price_dispose;
219 gobject_class->finalize = gnc_price_finalize;
220 gobject_class->set_property = gnc_price_set_property;
221 gobject_class->get_property = gnc_price_get_property;
223 g_object_class_install_property
226 g_param_spec_object (
"commodity",
228 "The commodity field denotes the base kind of " 229 "'stuff' for the units of this quote, whether " 230 "it is USD, gold, stock, etc.",
234 g_object_class_install_property
237 g_param_spec_object (
"currency",
239 "The currency field denotes the external kind " 240 "'stuff' for the units of this quote, whether " 241 "it is USD, gold, stock, etc.",
245 g_object_class_install_property
248 g_param_spec_string (
"source",
250 "The price source is PriceSource enum describing how" 251 " the price was created. This property works on the" 252 " string values in source_names for SQL database" 257 g_object_class_install_property
260 g_param_spec_string (
"type",
262 "The quote type is a string describing the " 263 "type of a price quote. Types possible now " 264 "are 'bid', 'ask', 'last', 'nav', 'transaction', " 269 g_object_class_install_property
272 g_param_spec_boxed(
"date",
274 "The date of the price quote.",
278 g_object_class_install_property
281 g_param_spec_boxed(
"value",
283 "The value of the price quote.",
298 g_return_val_if_fail (book,
nullptr);
301 p =
static_cast<GNCPrice*
>(g_object_new(GNC_TYPE_PRICE,
nullptr));
305 LEAVE (
"price created %p", p);
310 gnc_price_destroy (GNCPrice *p)
312 ENTER(
"destroy price %p", p);
315 if (p->type) CACHE_REMOVE(p->type);
333 if (p->refcount == 0)
340 if (p->refcount <= 0)
342 if (
nullptr != p->db)
344 PERR(
"last unref while price in database");
346 gnc_price_destroy (p);
358 g_return_val_if_fail (book,
nullptr);
364 LEAVE (
"return nullptr");
371 LEAVE (
"return nullptr");
375 qof_instance_copy_version(new_p, p);
377 gnc_price_begin_edit(new_p);
379 gnc_price_set_commodity(new_p, gnc_price_get_commodity(p));
380 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
381 gnc_price_set_source(new_p, gnc_price_get_source(p));
382 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
383 gnc_price_set_value(new_p, gnc_price_get_value(p));
384 gnc_price_set_currency(new_p, gnc_price_get_currency(p));
385 gnc_price_commit_edit(new_p);
386 LEAVE (
"return cloned price %p", new_p);
395 qof_instance_copy_version(new_p, p);
396 gnc_price_begin_edit(new_p);
397 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
398 gnc_price_set_source(new_p, PRICE_SOURCE_TEMP);
399 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
400 gnc_price_set_commodity(new_p, gnc_price_get_currency(p));
401 gnc_price_set_currency(new_p, gnc_price_get_commodity(p));
403 gnc_price_commit_edit(new_p);
410 gnc_price_begin_edit (GNCPrice *p)
417 PERR (
"Failed to commit: %d", errcode);
418 gnc_engine_signal_commit_error( errcode );
424 gnc_price_commit_edit (GNCPrice *p)
449 gnc_price_set_dirty (GNCPrice *p)
451 qof_instance_set_dirty(&p->inst);
456 gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c)
466 remove_price (p->db, p, TRUE);
467 gnc_price_begin_edit (p);
469 gnc_price_set_dirty(p);
470 gnc_price_commit_edit (p);
471 add_price (p->db, p);
478 gnc_price_set_currency(GNCPrice *p, gnc_commodity *c)
488 remove_price (p->db, p, TRUE);
489 gnc_price_begin_edit (p);
491 gnc_price_set_dirty(p);
492 gnc_price_commit_edit (p);
493 add_price (p->db, p);
499 gnc_price_set_time64(GNCPrice *p,
time64 t)
508 remove_price (p->db, p, FALSE);
509 gnc_price_begin_edit (p);
511 gnc_price_set_dirty(p);
512 gnc_price_commit_edit (p);
513 add_price (p->db, p);
522 gnc_price_begin_edit (p);
524 gnc_price_set_dirty(p);
525 gnc_price_commit_edit(p);
529 gnc_price_set_source_string(GNCPrice *p,
const char* str)
534 if (strcmp(source_names[s], str) == 0)
536 gnc_price_set_source(p, s);
543 gnc_price_set_typestr(GNCPrice *p,
const char* type)
546 if (g_strcmp0(p->type, type) != 0)
548 gnc_price_begin_edit (p);
549 CACHE_REPLACE(p->type, type);
550 gnc_price_set_dirty(p);
551 gnc_price_commit_edit (p);
556 gnc_price_set_value(GNCPrice *p, gnc_numeric value)
561 gnc_price_begin_edit (p);
563 gnc_price_set_dirty(p);
564 gnc_price_commit_edit (p);
572 gnc_price_lookup (
const GncGUID *guid, QofBook *book)
576 if (!guid || !book)
return nullptr;
582 gnc_price_get_commodity(
const GNCPrice *p)
584 if (!p)
return nullptr;
589 gnc_price_get_time64(
const GNCPrice *p)
591 return p ? p->tmspec : 0;
595 gnc_price_get_source(
const GNCPrice *p)
597 if (!p)
return PRICE_SOURCE_INVALID;
602 gnc_price_get_source_string(
const GNCPrice *p)
604 if (!p)
return nullptr;
605 return source_names[p->source];
609 gnc_price_get_typestr(
const GNCPrice *p)
611 if (!p)
return nullptr;
616 gnc_price_get_value(
const GNCPrice *p)
620 PERR(
"price nullptr.\n");
621 return gnc_numeric_zero();
627 gnc_price_get_currency(
const GNCPrice *p)
629 if (!p)
return nullptr;
634 gnc_price_equal (
const GNCPrice *p1,
const GNCPrice *p2)
638 if (p1 == p2)
return TRUE;
639 if (!p1 || !p2)
return FALSE;
642 gnc_price_get_commodity (p2)))
646 gnc_price_get_currency (p2)))
649 time1 = gnc_price_get_time64 (p1);
650 time2 = gnc_price_get_time64 (p2);
655 if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
658 if (g_strcmp0 (gnc_price_get_typestr (p1),
659 gnc_price_get_typestr (p2)) != 0)
663 gnc_price_get_value (p2)))
673 compare_prices_by_date(gconstpointer a, gconstpointer b)
678 if (!a && !b)
return 0;
682 time_a = gnc_price_get_time64((GNCPrice *) a);
683 time_b = gnc_price_get_time64((GNCPrice *) b);
686 result = time64_cmp(time_b, time_a);
687 if (result)
return result;
690 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
691 gnc_price_get_guid((GNCPrice *) b));
695 price_is_duplicate (
const GNCPrice *p_price,
const GNCPrice *c_price)
707 if (!prices || !p)
return FALSE;
710 if (check_dupl && g_list_find_custom (*prices, p, (GCompareFunc)price_is_duplicate))
713 auto result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
717 *prices = result_list;
725 GList *found_element;
727 if (!prices || !p)
return FALSE;
729 found_element = g_list_find(*prices, p);
730 if (!found_element)
return TRUE;
732 result_list = g_list_remove_link(*prices, found_element);
734 g_list_free(found_element);
736 *prices = result_list;
747 gnc_price_list_equal(PriceList *prices1, PriceList *prices2)
749 if (prices1 == prices2)
return TRUE;
751 for (
auto n1 = prices1, n2 = prices2; n1 || n2; n1 = g_list_next (n1), n2 = g_list_next (n2))
755 PINFO (
"prices2 has extra prices");
760 PINFO (
"prices1 has extra prices");
763 if (!gnc_price_equal (static_cast<GNCPrice*>(n1->data), static_cast<GNCPrice*>(n2->data)))
782 QOF_GOBJECT_IMPL(gnc_pricedb, GNCPriceDB, QOF_TYPE_INSTANCE)
785 gnc_pricedb_init(GNCPriceDB* pdb)
787 pdb->reset_nth_price_cache = FALSE;
791 gnc_pricedb_dispose_real (GObject *pdbp)
796 gnc_pricedb_finalize_real(GObject* pdbp)
801 gnc_pricedb_create(QofBook * book)
806 g_return_val_if_fail (book,
nullptr);
815 PWARN (
"A price database already exists for this book!");
819 result =
static_cast<GNCPriceDB*
>(g_object_new(GNC_TYPE_PRICEDB,
nullptr));
826 qof_collection_set_data (col, result);
828 result->commodity_hash = g_hash_table_new(
nullptr,
nullptr);
829 g_return_val_if_fail (result->commodity_hash,
nullptr);
834 destroy_pricedb_currency_hash_data(gpointer key,
838 GList *price_list = (GList *) data;
842 for (node = price_list; node; node = node->next)
844 p =
static_cast<GNCPrice*
>(node->data);
853 destroy_pricedb_commodity_hash_data(gpointer key,
857 GHashTable *currency_hash = (GHashTable *) data;
858 if (!currency_hash)
return;
859 g_hash_table_foreach (currency_hash,
860 destroy_pricedb_currency_hash_data,
862 g_hash_table_destroy(currency_hash);
869 if (db->commodity_hash)
871 g_hash_table_foreach (db->commodity_hash,
872 destroy_pricedb_commodity_hash_data,
875 g_hash_table_destroy (db->commodity_hash);
876 db->commodity_hash =
nullptr;
884 db->bulk_update = bulk_update;
905 if (!col)
return nullptr;
914 if (!book)
return nullptr;
922 num_prices_helper (GNCPrice *p, gpointer user_data)
924 auto count =
static_cast<guint*
>(user_data);
951 gnc_commodity *commodity;
955 pricedb_equal_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
958 auto currency =
static_cast<gnc_commodity*
>(key);
959 auto price_list1 =
static_cast<GList*
>(val);
961 equal_data->commodity,
964 if (!gnc_price_list_equal (price_list1, price_list2))
965 equal_data->equal = FALSE;
971 pricedb_equal_foreach_currencies_hash (gpointer key, gpointer val,
974 auto currencies_hash =
static_cast<GHashTable*
>(val);
977 equal_data->commodity =
static_cast<gnc_commodity*
>(key);
979 g_hash_table_foreach (currencies_hash,
980 pricedb_equal_foreach_pricelist,
989 if (db1 == db2)
return TRUE;
993 PWARN (
"one is nullptr");
997 equal_data.equal = TRUE;
998 equal_data.db2 = db2;
1000 g_hash_table_foreach (db1->commodity_hash,
1001 pricedb_equal_foreach_currencies_hash,
1004 return equal_data.equal;
1012 add_price(GNCPriceDB *db, GNCPrice *p)
1017 gnc_commodity *commodity;
1018 gnc_commodity *currency;
1019 GHashTable *currency_hash;
1021 if (!db || !p)
return FALSE;
1022 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1028 PERR (
"attempted to mix up prices across different books");
1033 commodity = gnc_price_get_commodity(p);
1036 PWARN(
"no commodity");
1040 currency = gnc_price_get_currency(p);
1043 PWARN(
"no currency");
1047 if (!db->commodity_hash)
1049 LEAVE (
"no commodity hash found ");
1056 if (!db->bulk_update)
1059 p->currency, p->tmspec);
1060 if (old_price !=
nullptr)
1062 if (p->source > old_price->source)
1065 LEAVE (
"Better price already in DB.");
1072 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1075 currency_hash = g_hash_table_new(
nullptr,
nullptr);
1076 g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
1079 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1082 LEAVE (
"gnc_price_list_insert failed");
1088 LEAVE (
" no price list");
1092 g_hash_table_insert(currency_hash, currency, price_list);
1097 LEAVE (
"db=%p, pr=%p dirty=%d dextroying=%d commodity=%s/%s currency_hash=%p",
1113 if (!db || !p)
return FALSE;
1115 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1119 if (FALSE == add_price(db, p))
1121 LEAVE (
" failed to add price");
1126 qof_instance_set_dirty(&db->inst);
1129 LEAVE (
"db=%p, pr=%p dirty=%d destroying=%d",
1141 remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup)
1144 gnc_commodity *commodity;
1145 gnc_commodity *currency;
1146 GHashTable *currency_hash;
1148 if (!db || !p)
return FALSE;
1149 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1153 commodity = gnc_price_get_commodity(p);
1156 LEAVE (
" no commodity");
1159 currency = gnc_price_get_currency(p);
1162 LEAVE (
" no currency");
1165 if (!db->commodity_hash)
1167 LEAVE (
" no commodity hash");
1171 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1174 LEAVE (
" no currency hash");
1179 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
1184 LEAVE (
" cannot remove price list");
1192 g_hash_table_insert(currency_hash, currency, price_list);
1196 g_hash_table_remove(currency_hash, currency);
1203 guint num_currencies = g_hash_table_size (currency_hash);
1204 if (0 == num_currencies)
1206 g_hash_table_remove (db->commodity_hash, commodity);
1207 g_hash_table_destroy (currency_hash);
1213 LEAVE (
"db=%p, pr=%p", db, p);
1222 memset(datebuff, 0,
sizeof(datebuff));
1223 if (!db || !p)
return FALSE;
1224 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1230 DEBUG(
"Remove Date is %s, Commodity is %s, Source is %s", datebuff,
1232 gnc_price_get_source_string (p));
1234 rc = remove_price (db, p, TRUE);
1236 qof_instance_set_dirty(&db->inst);
1240 gnc_price_begin_edit (p);
1241 qof_instance_set_destroying(p, TRUE);
1242 gnc_price_commit_edit (p);
1245 LEAVE (
"db=%p, pr=%p", db, p);
1254 gboolean delete_user;
1255 gboolean delete_app;
1260 check_one_price_date (GNCPrice *price, gpointer user_data)
1266 ENTER(
"price %p (%s), data %p", price,
1270 source = gnc_price_get_source (price);
1272 if ((source == PRICE_SOURCE_FQ) && data->delete_fq)
1273 PINFO (
"Delete Quote Source");
1274 else if ((source == PRICE_SOURCE_USER_PRICE) && data->delete_user)
1275 PINFO (
"Delete User Source");
1276 else if ((source != PRICE_SOURCE_FQ) && (source != PRICE_SOURCE_USER_PRICE) && data->delete_app)
1277 PINFO (
"Delete App Source");
1280 LEAVE(
"Not a matching source");
1284 time = gnc_price_get_time64 (price);
1288 DEBUG(
"checking date %s", buf);
1290 if (time < data->cutoff)
1292 data->list = g_slist_prepend(data->list, price);
1293 DEBUG(
"will delete");
1300 pricedb_remove_foreach_pricelist (gpointer key,
1304 GList *price_list = (GList *) val;
1305 GList *node = price_list;
1308 ENTER(
"key %p, value %p, data %p", key, val, user_data);
1311 g_list_foreach(node, (GFunc)check_one_price_date, data);
1317 compare_prices_by_commodity_date (gconstpointer a, gconstpointer b)
1320 gnc_commodity *comma;
1321 gnc_commodity *commb;
1322 gnc_commodity *curra;
1323 gnc_commodity *currb;
1326 if (!a && !b)
return 0;
1331 comma = gnc_price_get_commodity ((GNCPrice *) a);
1332 commb = gnc_price_get_commodity ((GNCPrice *) b);
1337 curra = gnc_price_get_currency ((GNCPrice *) a);
1338 currb = gnc_price_get_currency ((GNCPrice *) b);
1343 time_a = gnc_price_get_time64((GNCPrice *) a);
1344 time_b = gnc_price_get_time64((GNCPrice *) b);
1347 result = time64_cmp(time_b, time_a);
1348 if (result)
return result;
1351 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
1352 gnc_price_get_guid((GNCPrice *) b));
1356 price_commodity_and_currency_equal (GNCPrice *a, GNCPrice *b)
1358 gboolean ret_comm = FALSE;
1359 gboolean ret_curr = FALSE;
1367 return (ret_comm && ret_curr);
1371 gnc_pricedb_remove_old_prices_pinfo (GNCPrice *price, gboolean keep_message)
1376 if (g_date_valid (&price_date))
1382 PINFO(
"#### Keep price with date %s, commodity is %s, currency is %s", date_buf,
1387 PINFO(
"## Remove price with date %s", date_buf);
1390 PINFO(
"Keep price date is invalid");
1394 clone_price (GNCPrice **price, GNCPrice *source_price)
1398 if (!source_price)
return;
1399 if (price ==
nullptr)
return;
1408 gnc_pricedb_remove_old_prices_pinfo (source_price, TRUE);
1412 roundUp (gint numToRound, gint multiple)
1419 remainder = numToRound % multiple;
1423 return numToRound + multiple - remainder;
1427 get_fiscal_quarter (GDate *date, GDateMonth fiscal_start)
1429 GDateMonth month = g_date_get_month (date);
1431 gint q = ((roundUp (22 - fiscal_start + month, 3)/3) % 4) + 1;
1433 PINFO(
"Return fiscal quarter is %d", q);
1438 gnc_pricedb_process_removal_list (GNCPriceDB *db, GDate *fiscal_end_date,
1442 gboolean save_first_price = FALSE;
1443 gint saved_test_value = 0, next_test_value = 0;
1444 GNCPrice *cloned_price =
nullptr;
1445 GDateMonth fiscal_month_start;
1446 GDate *tmp_date = g_date_new_dmy (g_date_get_day (fiscal_end_date),
1447 g_date_get_month (fiscal_end_date),
1448 g_date_get_year (fiscal_end_date));
1451 g_date_subtract_months (tmp_date, 12);
1452 fiscal_month_start =
static_cast<GDateMonth
>(g_date_get_month (tmp_date) + 1);
1453 g_date_free (tmp_date);
1456 data.list = g_slist_sort (data.list, compare_prices_by_commodity_date);
1459 for (item = data.list; item; item = g_slist_next(item))
1461 GDate saved_price_date;
1462 GDate next_price_date;
1463 auto price =
static_cast<GNCPrice*
>(item->data);
1466 if (keep == PRICE_REMOVE_KEEP_NONE)
1468 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1473 save_first_price = !price_commodity_and_currency_equal (price, cloned_price);
1474 if (save_first_price == TRUE)
1476 clone_price (&cloned_price, price);
1481 saved_price_date =
time64_to_gdate (gnc_price_get_time64 (cloned_price));
1485 if (keep == PRICE_REMOVE_KEEP_LAST_PERIOD && save_first_price == FALSE)
1487 GDate *saved_fiscal_end = g_date_new_dmy (g_date_get_day (&saved_price_date),
1488 g_date_get_month (&saved_price_date),
1489 g_date_get_year (&saved_price_date));
1491 GDate *next_fiscal_end = g_date_new_dmy (g_date_get_day (&next_price_date),
1492 g_date_get_month (&next_price_date),
1493 g_date_get_year (&next_price_date));
1498 saved_test_value = g_date_get_year (saved_fiscal_end);
1499 next_test_value = g_date_get_year (next_fiscal_end);
1501 PINFO(
"Keep last price in fiscal year");
1503 g_date_free (saved_fiscal_end);
1504 g_date_free (next_fiscal_end);
1508 if (keep == PRICE_REMOVE_KEEP_LAST_QUARTERLY && save_first_price == FALSE)
1510 saved_test_value = get_fiscal_quarter (&saved_price_date, fiscal_month_start);
1511 next_test_value = get_fiscal_quarter (&next_price_date, fiscal_month_start);
1513 PINFO(
"Keep last price in fiscal quarter");
1517 if (keep == PRICE_REMOVE_KEEP_LAST_MONTHLY && save_first_price == FALSE)
1519 saved_test_value = g_date_get_month (&saved_price_date);
1520 next_test_value = g_date_get_month (&next_price_date);
1522 PINFO(
"Keep last price of every month");
1526 if (keep == PRICE_REMOVE_KEEP_LAST_WEEKLY && save_first_price == FALSE)
1528 saved_test_value = g_date_get_iso8601_week_of_year (&saved_price_date);
1529 next_test_value = g_date_get_iso8601_week_of_year (&next_price_date);
1531 PINFO(
"Keep last price of every week");
1535 if (saved_test_value == next_test_value)
1537 gnc_pricedb_remove_old_prices_pinfo (price, FALSE);
1541 clone_price (&cloned_price, price);
1549 GDate *fiscal_end_date,
time64 cutoff,
1550 PriceRemoveSourceFlags source,
1551 PriceRemoveKeepOptions keep)
1556 memset (datebuff, 0,
sizeof(datebuff));
1559 data.cutoff = cutoff;
1560 data.list =
nullptr;
1561 data.delete_fq = FALSE;
1562 data.delete_user = FALSE;
1563 data.delete_app = FALSE;
1565 ENTER(
"Remove Prices for Source %d, keeping %d", source, keep);
1568 if (source & PRICE_REMOVE_SOURCE_APP)
1569 data.delete_app = TRUE;
1571 if (source & PRICE_REMOVE_SOURCE_FQ)
1572 data.delete_fq = TRUE;
1574 if (source & PRICE_REMOVE_SOURCE_USER)
1575 data.delete_user = TRUE;
1578 for (node = g_list_first (comm_list); node; node = g_list_next (node))
1580 auto currencies_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, node->data));
1581 g_hash_table_foreach (currencies_hash, pricedb_remove_foreach_pricelist, &data);
1584 if (data.list ==
nullptr)
1586 LEAVE(
"Empty price list");
1590 DEBUG(
"Number of Prices in list is %d, Cutoff date is %s",
1591 g_slist_length (data.list), datebuff);
1594 if (fiscal_end_date ==
nullptr || !g_date_valid (fiscal_end_date))
1597 GDate end_this_year;
1598 g_date_set_dmy (&end_this_year, 31, GDateMonth(12), ymd.year);
1599 gnc_pricedb_process_removal_list (db, &end_this_year, data, keep);
1602 gnc_pricedb_process_removal_list (db, fiscal_end_date, data, keep);
1604 g_slist_free (data.list);
1612 static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b);
1615 hash_values_helper(gpointer key, gpointer value, gpointer data)
1617 auto l =
static_cast<GList**
>(data);
1621 new_l = pricedb_price_list_merge(*l, static_cast<PriceList*>(value));
1626 *l = g_list_copy (static_cast<GList*>(value));
1630 price_list_from_hashtable (GHashTable *hash,
const gnc_commodity *currency)
1632 GList *price_list =
nullptr, *result = nullptr ;
1635 price_list =
static_cast<GList*
>(g_hash_table_lookup(hash, currency));
1638 LEAVE (
" no price list");
1641 result = g_list_copy (price_list);
1645 g_hash_table_foreach(hash, hash_values_helper, (gpointer)&result);
1651 pricedb_price_list_merge (PriceList *a, PriceList *b)
1653 PriceList *merged_list =
nullptr;
1657 while (next_a || next_b)
1659 if (next_a ==
nullptr)
1661 merged_list = g_list_prepend (merged_list, next_b->data);
1662 next_b = next_b->next;
1664 else if (next_b ==
nullptr)
1666 merged_list = g_list_prepend (merged_list, next_a->data);
1667 next_a = next_a->next;
1670 else if (compare_prices_by_date (next_a->data, next_b->data) < 0)
1672 merged_list = g_list_prepend (merged_list, next_a->data);
1673 next_a = next_a->next;
1677 merged_list = g_list_prepend (merged_list, next_b->data);
1678 next_b = next_b->next;
1681 return g_list_reverse (merged_list);
1685 pricedb_get_prices_internal(GNCPriceDB *db,
const gnc_commodity *commodity,
1686 const gnc_commodity *currency, gboolean bidi)
1688 GHashTable *forward_hash =
nullptr, *reverse_hash =
nullptr;
1689 PriceList *forward_list =
nullptr, *reverse_list =
nullptr;
1690 g_return_val_if_fail (db !=
nullptr,
nullptr);
1691 g_return_val_if_fail (commodity !=
nullptr,
nullptr);
1692 forward_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
1693 if (currency && bidi)
1694 reverse_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, currency));
1695 if (!forward_hash && !reverse_hash)
1697 LEAVE (
" no currency hash");
1701 forward_list = price_list_from_hashtable (forward_hash, currency);
1702 if (currency && reverse_hash)
1704 reverse_list = price_list_from_hashtable (reverse_hash, commodity);
1714 PriceList *merged_list;
1715 merged_list = pricedb_price_list_merge (forward_list, reverse_list);
1716 g_list_free (forward_list);
1717 g_list_free (reverse_list);
1718 forward_list = merged_list;
1722 forward_list = reverse_list;
1727 return forward_list;
1731 const gnc_commodity *commodity,
1732 const gnc_commodity *currency)
1737 if (!db || !commodity || !currency)
return nullptr;
1738 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
1740 price_list = pricedb_get_prices_internal(db, commodity, currency, TRUE);
1741 if (!price_list)
return nullptr;
1745 result =
static_cast<GNCPrice*
>(price_list->data);
1747 g_list_free (price_list);
1748 LEAVE(
"price is %p", result);
1755 const gnc_commodity *com;
1770 price_list_scan_any_currency(GList *price_list, gpointer data)
1779 auto price =
static_cast<GNCPrice*
>(price_list->data);
1780 com = gnc_price_get_commodity(price);
1781 cur = gnc_price_get_currency(price);
1785 if (com != helper->com && cur != helper->com)
1791 for (
auto node = price_list; node; node = g_list_next (node))
1793 price =
static_cast<GNCPrice*
>(node->data);
1794 time64 price_t = gnc_price_get_time64(price);
1795 if (price_t < helper->t)
1800 auto prev_price =
static_cast<GNCPrice*
>(node->prev->data);
1802 *helper->list = g_list_prepend(*helper->list, prev_price);
1806 *helper->list = g_list_prepend(*helper->list, price);
1810 else if (node->next ==
nullptr)
1814 *helper->list = g_list_prepend(*helper->list, price);
1825 latest_before (PriceList *prices,
const gnc_commodity* target,
time64 t)
1827 GList *node, *found_coms =
nullptr, *retval =
nullptr;
1828 for (node = prices; node !=
nullptr; node = g_list_next(node))
1830 GNCPrice *price = (GNCPrice*)node->data;
1831 gnc_commodity *com = gnc_price_get_commodity(price);
1832 gnc_commodity *cur = gnc_price_get_currency(price);
1833 time64 price_t = gnc_price_get_time64(price);
1836 (com == target && g_list_find (found_coms, cur)) ||
1837 (cur == target && g_list_find (found_coms, com)))
1841 retval = g_list_prepend (retval, price);
1842 found_coms = g_list_prepend (found_coms, com == target ? cur : com);
1844 g_list_free (found_coms);
1845 return g_list_reverse(retval);
1849 find_comtime(GPtrArray* array, gnc_commodity *com)
1851 unsigned int index = 0;
1852 GNCPrice** retval =
nullptr;
1853 for (index = 0; index < array->len; ++index)
1855 auto price_p =
static_cast<GNCPrice**
>(g_ptr_array_index(array, index));
1856 if (gnc_price_get_commodity(*price_p) == com ||
1857 gnc_price_get_currency(*price_p) == com)
1864 add_nearest_price(GList *target_list, GPtrArray *price_array, GNCPrice *price,
1865 const gnc_commodity *target,
time64 t)
1867 gnc_commodity *com = gnc_price_get_commodity(price);
1868 gnc_commodity *cur = gnc_price_get_currency(price);
1869 time64 price_t = gnc_price_get_time64(price);
1870 gnc_commodity *other = com == target ? cur : com;
1871 GNCPrice **com_price = find_comtime(price_array, other);
1873 if (com_price ==
nullptr)
1875 com_price = (GNCPrice**)g_slice_new(gpointer);
1877 g_ptr_array_add(price_array, com_price);
1883 target_list = g_list_prepend(target_list, price);
1887 com_t = gnc_price_get_time64(*com_price);
1900 time64 com_diff = com_t - t;
1901 time64 price_diff = t - price_t;
1902 if (com_diff < price_diff)
1905 target_list = g_list_prepend(target_list, *com_price);
1910 target_list = g_list_prepend(target_list, price);
1918 nearest_to (PriceList *prices,
const gnc_commodity* target,
time64 t)
1920 GList *node, *retval =
nullptr;
1921 const guint prealloc_size = 5;
1926 GPtrArray *price_array = g_ptr_array_sized_new(prealloc_size);
1928 for (node = prices; node !=
nullptr; node = g_list_next(node))
1930 GNCPrice *price = (GNCPrice*)node->data;
1931 retval = add_nearest_price(retval, price_array, price, target, t);
1937 for (index = 0; index < price_array->len; ++index)
1939 auto com_price =
static_cast<GNCPrice**
>(g_ptr_array_index(price_array, index));
1940 time64 price_t = gnc_price_get_time64(*com_price);
1944 retval = g_list_prepend(retval, *com_price);
1947 g_ptr_array_free(price_array, TRUE);
1948 return g_list_sort(retval, compare_prices_by_date);
1955 const gnc_commodity *commodity)
1963 const gnc_commodity *commodity,
1966 GList *prices =
nullptr, *result;
1970 if (!db || !commodity)
return nullptr;
1971 ENTER (
"db=%p commodity=%p", db, commodity);
1973 pricedb_pricelist_traversal(db, price_list_scan_any_currency, &helper);
1974 prices = g_list_sort(prices, compare_prices_by_date);
1975 result = nearest_to(prices, commodity, t);
1983 const gnc_commodity *commodity,
1986 GList *prices =
nullptr, *result;
1990 if (!db || !commodity)
return nullptr;
1991 ENTER (
"db=%p commodity=%p", db, commodity);
1993 pricedb_pricelist_traversal(db, price_list_scan_any_currency,
1995 prices = g_list_sort(prices, compare_prices_by_date);
1996 result = latest_before(prices, commodity, t);
2008 const gnc_commodity *commodity,
2009 const gnc_commodity *currency)
2012 GHashTable *currency_hash;
2015 if (!db || !commodity)
return FALSE;
2016 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2017 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, commodity));
2020 LEAVE(
"no, no currency_hash table");
2026 price_list =
static_cast<GList*
>(g_hash_table_lookup(currency_hash, currency));
2032 LEAVE(
"no, no price list");
2036 size = g_hash_table_size (currency_hash);
2037 LEAVE(
"%s", size > 0 ?
"yes" :
"no");
2047 const gnc_commodity *commodity,
2048 const gnc_commodity *currency)
2050 if (!db || !commodity)
return nullptr;
2051 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2052 auto result = pricedb_get_prices_internal (db, commodity, currency, FALSE);
2053 if (!result)
return nullptr;
2062 price_count_helper(gpointer key, gpointer value, gpointer data)
2064 auto result =
static_cast<int*
>(data);
2065 auto price_list =
static_cast<GList*
>(value);
2067 *result += g_list_length(price_list);
2072 const gnc_commodity *c)
2075 GHashTable *currency_hash;
2077 if (!db || !c)
return 0;
2078 ENTER (
"db=%p commodity=%p", db, c);
2080 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup(db->commodity_hash, c));
2083 g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result);
2086 LEAVE (
"count=%d", result);
2092 list_combine (gpointer element, gpointer data)
2094 GList *list = *(GList**)data;
2095 auto lst =
static_cast<GList*
>(element);
2096 if (list ==
nullptr)
2097 *(GList**)data = g_list_copy (lst);
2100 GList *new_list = g_list_concat (list, g_list_copy (lst));
2101 *(GList**)data = new_list;
2119 const gnc_commodity *c,
2122 static const gnc_commodity *last_c =
nullptr;
2123 static GList *prices =
nullptr;
2125 GNCPrice *result =
nullptr;
2126 GHashTable *currency_hash;
2127 g_return_val_if_fail (GNC_IS_COMMODITY (c),
nullptr);
2129 if (!db || !c || n < 0)
return nullptr;
2132 if (last_c && prices && last_c == c && db->reset_nth_price_cache == FALSE)
2134 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2135 LEAVE (
"price=%p", result);
2143 g_list_free (prices);
2147 db->reset_nth_price_cache = FALSE;
2149 currency_hash =
static_cast<GHashTable*
>(g_hash_table_lookup (db->commodity_hash, c));
2152 GList *currencies = g_hash_table_get_values (currency_hash);
2153 g_list_foreach (currencies, list_combine, &prices);
2154 result =
static_cast<GNCPrice*
>(g_list_nth_data (prices, n));
2155 g_list_free (currencies);
2158 LEAVE (
"price=%p", result);
2163 gnc_pricedb_nth_price_reset_cache (GNCPriceDB *db)
2166 db->reset_nth_price_cache = TRUE;
2171 const gnc_commodity *c,
2172 const gnc_commodity *currency,
2175 return lookup_nearest_in_time(db, c, currency, t, TRUE);
2179 lookup_nearest_in_time(GNCPriceDB *db,
2180 const gnc_commodity *c,
2181 const gnc_commodity *currency,
2186 GNCPrice *current_price =
nullptr;
2187 GNCPrice *next_price =
nullptr;
2188 GNCPrice *result =
nullptr;
2190 if (!db || !c || !currency)
return nullptr;
2191 if (t == INT64_MAX)
return nullptr;
2192 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2193 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2194 if (!price_list)
return nullptr;
2197 current_price =
static_cast<GNCPrice*
>(price_list->data);
2201 for (
auto item = price_list; item; item = g_list_next (item))
2203 auto p =
static_cast<GNCPrice*
>(item->data);
2204 time64 price_time = gnc_price_get_time64(p);
2205 if (price_time <= t)
2207 next_price =
static_cast<GNCPrice*
>(item->data);
2210 current_price =
static_cast<GNCPrice*
>(item->data);
2218 result = current_price;
2226 if (price_day != t_day)
2234 time64 current_t = gnc_price_get_time64(current_price);
2235 time64 next_t = gnc_price_get_time64(next_price);
2236 time64 diff_current = current_t - t;
2237 time64 diff_next = next_t - t;
2238 time64 abs_current = llabs(diff_current);
2239 time64 abs_next = llabs(diff_next);
2247 if (current_day == t_day)
2249 if (next_day == t_day)
2252 if (abs_current < abs_next)
2253 result = current_price;
2255 result = next_price;
2259 result = current_price;
2261 else if (next_day == t_day)
2263 result = next_price;
2270 if (abs_current < abs_next)
2272 result = current_price;
2276 result = next_price;
2283 g_list_free (price_list);
2290 const gnc_commodity *c,
2291 const gnc_commodity *currency,
2294 return lookup_nearest_in_time(db, c, currency, t, FALSE);
2298 static int price_time64_less_or_equal (GNCPrice *p,
time64 *time)
2300 return !(gnc_price_get_time64 (p) <= *time);
2305 const gnc_commodity *c,
2306 const gnc_commodity *currency,
2309 GNCPrice *current_price =
nullptr;
2310 if (!db || !c || !currency)
return nullptr;
2311 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2312 auto price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2313 if (!price_list)
return nullptr;
2314 auto p = g_list_find_custom (price_list, &t, (GCompareFunc)price_time64_less_or_equal);
2317 current_price = GNC_PRICE (p->data);
2320 g_list_free (price_list);
2322 return current_price;
2333 extract_common_prices (PriceList *from_prices, PriceList *to_prices,
2334 const gnc_commodity *from,
const gnc_commodity *to)
2337 GList *from_node =
nullptr, *to_node =
nullptr;
2338 GNCPrice *from_price =
nullptr, *to_price =
nullptr;
2340 for (from_node = from_prices; from_node !=
nullptr;
2341 from_node = g_list_next(from_node))
2343 for (to_node = to_prices; to_node !=
nullptr;
2344 to_node = g_list_next(to_node))
2346 gnc_commodity *to_com, *to_cur;
2347 gnc_commodity *from_com, *from_cur;
2348 to_price = GNC_PRICE(to_node->data);
2349 from_price = GNC_PRICE(from_node->data);
2350 to_com = gnc_price_get_commodity (to_price);
2351 to_cur = gnc_price_get_currency (to_price);
2352 from_com = gnc_price_get_commodity (from_price);
2353 from_cur = gnc_price_get_currency (from_price);
2354 if (((to_com == from_com || to_com == from_cur) &&
2355 (to_com != from && to_com != to)) ||
2356 ((to_cur == from_com || to_cur == from_cur) &&
2357 (to_cur != from && to_cur != to)))
2360 from_price =
nullptr;
2362 if (to_price !=
nullptr && from_price !=
nullptr)
2365 if (from_price ==
nullptr || to_price ==
nullptr)
2369 retval.from = from_price;
2370 retval.to = to_price;
2376 convert_price (
const gnc_commodity *from,
const gnc_commodity *to,
PriceTuple tuple)
2378 gnc_commodity *from_com = gnc_price_get_commodity (tuple.from);
2379 gnc_commodity *from_cur = gnc_price_get_currency (tuple.from);
2380 gnc_commodity *to_com = gnc_price_get_commodity (tuple.to);
2381 gnc_commodity *to_cur = gnc_price_get_currency (tuple.to);
2382 gnc_numeric from_val = gnc_price_get_value (tuple.from);
2383 gnc_numeric to_val = gnc_price_get_value (tuple.to);
2392 if (from_cur == from && to_cur == to)
2395 if (from_com == from && to_com == to)
2400 if (from_cur == from)
2407 indirect_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2408 const gnc_commodity *to,
time64 t, gboolean before_date)
2410 GList *from_prices =
nullptr, *to_prices =
nullptr;
2412 gnc_numeric zero = gnc_numeric_zero();
2423 else if (before_date)
2435 if (!from_prices || !to_prices)
2441 tuple = extract_common_prices (from_prices, to_prices, from, to);
2445 return convert_price (from, to, tuple);
2451 direct_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2452 const gnc_commodity *to,
time64 t, gboolean before_date)
2455 gnc_numeric retval = gnc_numeric_zero();
2457 if (!from || !to)
return retval;
2461 else if (before_date)
2466 if (!price)
return retval;
2468 retval = gnc_price_get_value (price);
2470 if (gnc_price_get_commodity (price) != from)
2478 get_nearest_price (GNCPriceDB *pdb,
2479 const gnc_commodity *orig_curr,
2480 const gnc_commodity *new_curr,
2487 return gnc_numeric_create (1, 1);
2490 price = direct_price_conversion (pdb, orig_curr, new_curr, t, before);
2496 price = indirect_price_conversion (pdb, orig_curr, new_curr, t, before);
2503 const gnc_commodity *orig_currency,
2504 const gnc_commodity *new_currency,
2507 return get_nearest_price (pdb, orig_currency, new_currency, t, TRUE);
2512 const gnc_commodity *orig_currency,
2513 const gnc_commodity *new_currency,
2516 return get_nearest_price (pdb, orig_currency, new_currency, t, FALSE);
2521 const gnc_commodity *orig_currency,
2522 const gnc_commodity *new_currency)
2524 return get_nearest_price (pdb, orig_currency, new_currency, INT64_MAX, FALSE);
2528 convert_amount_at_date (GNCPriceDB *pdb,
2530 const gnc_commodity *orig_currency,
2531 const gnc_commodity *new_currency,
2533 gboolean before_date)
2540 price = get_nearest_price (pdb, orig_currency, new_currency, t, before_date);
2544 return gnc_numeric_zero ();
2556 gnc_numeric balance,
2557 const gnc_commodity *balance_currency,
2558 const gnc_commodity *new_currency)
2560 return convert_amount_at_date
2561 (pdb, balance, balance_currency, new_currency, INT64_MAX, FALSE);
2566 gnc_numeric balance,
2567 const gnc_commodity *balance_currency,
2568 const gnc_commodity *new_currency,
2571 return convert_amount_at_date
2572 (pdb, balance, balance_currency, new_currency, t, FALSE);
2577 gnc_numeric balance,
2578 const gnc_commodity *balance_currency,
2579 const gnc_commodity *new_currency,
2582 return convert_amount_at_date
2583 (pdb, balance, balance_currency, new_currency, t, TRUE);
2593 gboolean (*func)(GNCPrice *p, gpointer user_data);
2598 pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2600 GList *price_list = (GList *) val;
2604 foreach_data->ok = g_list_find_custom (price_list, foreach_data->user_data, (GCompareFunc)foreach_data->func)
2609 pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2611 GHashTable *currencies_hash = (GHashTable *) val;
2612 g_hash_table_foreach(currencies_hash, pricedb_foreach_pricelist, user_data);
2616 unstable_price_traversal(GNCPriceDB *db,
2617 gboolean (*f)(GNCPrice *p, gpointer user_data),
2622 if (!db || !f)
return FALSE;
2623 foreach_data.ok = TRUE;
2624 foreach_data.func = f;
2625 foreach_data.user_data = user_data;
2626 if (db->commodity_hash ==
nullptr)
2630 g_hash_table_foreach(db->commodity_hash,
2631 pricedb_foreach_currencies_hash,
2634 return foreach_data.ok;
2641 gboolean (*func)(GList *p, gpointer user_data);
2646 pricedb_pricelist_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2648 GList *price_list = (GList *) val;
2650 if (foreach_data->ok)
2652 foreach_data->ok = foreach_data->func(price_list, foreach_data->user_data);
2657 pricedb_pricelist_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2659 GHashTable *currencies_hash = (GHashTable *) val;
2660 g_hash_table_foreach(currencies_hash, pricedb_pricelist_foreach_pricelist, user_data);
2664 pricedb_pricelist_traversal(GNCPriceDB *db,
2665 gboolean (*f)(GList *p, gpointer user_data),
2670 if (!db || !f)
return FALSE;
2671 foreach_data.ok = TRUE;
2672 foreach_data.func = f;
2673 foreach_data.user_data = user_data;
2674 if (db->commodity_hash ==
nullptr)
2678 g_hash_table_foreach(db->commodity_hash,
2679 pricedb_pricelist_foreach_currencies_hash,
2682 return foreach_data.ok;
2686 compare_hash_entries_by_commodity_key (
const CommodityPtrPair& he_a,
const CommodityPtrPair& he_b)
2688 auto ca = he_a.first;
2689 auto cb = he_b.first;
2691 if (ca == cb || !cb)
2700 return (cmp_result < 0);
2706 stable_price_traversal(GNCPriceDB *db,
2707 gboolean (*f)(GNCPrice *p, gpointer user_data),
2710 g_return_val_if_fail (db && f,
false);
2712 auto currency_hashes = hash_table_to_vector (db->commodity_hash);
2713 std::sort (currency_hashes.begin(), currency_hashes.end(), compare_hash_entries_by_commodity_key);
2715 for (
const auto& entry : currency_hashes)
2717 auto price_lists = hash_table_to_vector (static_cast<GHashTable*>(entry.second));
2718 std::sort (price_lists.begin(), price_lists.end(), compare_hash_entries_by_commodity_key);
2720 for (
const auto& pricelist_entry : price_lists)
2721 if (g_list_find_custom (static_cast<GList*>(pricelist_entry.second), user_data, (GCompareFunc)f))
2730 GncPriceForeachFunc f,
2732 gboolean stable_order)
2734 ENTER (
"db=%p f=%p", db, f);
2737 LEAVE (
" stable order found");
2738 return stable_price_traversal(db, f, user_data);
2740 LEAVE (
" use unstable order");
2741 return unstable_price_traversal(db, f, user_data);
2751 gnc_commodity *commodity;
2752 gnc_commodity *currency;
2753 gchar *istr =
nullptr;
2759 commodity = gnc_price_get_commodity(p);
2760 currency = gnc_price_get_currency(p);
2762 if (!commodity)
return;
2763 if (!currency)
return;
2765 istr = g_strnfill(indent,
' ');
2767 fprintf(f,
"%s<pdb:price>\n", istr);
2768 fprintf(f,
"%s <pdb:commodity pointer=%p>\n", istr, commodity);
2770 str = str ? str :
"(null)";
2771 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2773 str = str ? str :
"(null)";
2774 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2775 fprintf(f,
"%s </pdb:commodity>\n", istr);
2776 fprintf(f,
"%s <pdb:currency pointer=%p>\n", istr, currency);
2778 str = str ? str :
"(null)";
2779 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2781 str = str ? str :
"(null)";
2782 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2783 fprintf(f,
"%s </pdb:currency>\n", istr);
2784 str = source_names[gnc_price_get_source(p)];
2785 str = str ? str :
"invalid";
2786 fprintf(f,
"%s %s\n", istr, str);
2787 str = gnc_price_get_typestr(p);
2788 str = str ? str :
"(null)";
2789 fprintf(f,
"%s %s\n", istr, str);
2791 fprintf(f,
"%s</pdb:price>\n", istr);
2797 print_pricedb_adapter(GNCPrice *p, gpointer user_data)
2799 FILE *f = (FILE *) user_data;
2809 PERR(
"nullptr PriceDB\n");
2814 PERR(
"nullptr FILE*\n");
2818 fprintf(f,
"<gnc:pricedb>\n");
2820 fprintf(f,
"</gnc:pricedb>\n");
2827 pricedb_book_begin (QofBook *book)
2829 gnc_pricedb_create(book);
2833 pricedb_book_end (QofBook *book)
2841 qof_collection_set_data(col,
nullptr);
2846 price_create (QofBook *book)
2856 void (*func)(GNCPrice *p, gpointer user_data);
2862 void_pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2864 GList *price_list = (GList *) val;
2867 g_list_foreach (price_list, (GFunc)foreach_data->func, foreach_data->user_data);
2871 void_pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2873 GHashTable *currencies_hash = (GHashTable *) val;
2874 g_hash_table_foreach(currencies_hash, void_pricedb_foreach_pricelist, user_data);
2878 void_unstable_price_traversal(GNCPriceDB *db,
2879 void (*f)(GNCPrice *p, gpointer user_data),
2884 if (!db || !f)
return;
2885 foreach_data.func = f;
2886 foreach_data.user_data = user_data;
2888 g_hash_table_foreach(db->commodity_hash,
2889 void_pricedb_foreach_currencies_hash,
2899 void_unstable_price_traversal(db,
2900 (
void (*)(GNCPrice *, gpointer)) cb,
2906 #ifdef DUMP_FUNCTIONS 2908 static void price_list_dump(GList *price_list,
const char *tag);
2912 price_printable(gpointer obj)
2914 auto pr =
static_cast<GNCPrice*
>(obj);
2915 gnc_commodity *commodity;
2916 gnc_commodity *currency;
2917 static char buff[2048];
2922 #ifdef DUMP_FUNCTIONS 2926 price_list_dump(
nullptr,
"");
2932 commodity = gnc_price_get_commodity(pr);
2933 currency = gnc_price_get_currency(pr);
2935 g_snprintf (buff, 2048,
"%s %s / %s on %s", val,
2944 #ifdef DUMP_FUNCTIONS 2947 price_list_dump(GList *price_list,
const char *tag)
2951 printf(
"Price list %s\n", tag);
2952 for (node = price_list; node !=
nullptr; node = node->next)
2954 printf(
"%s\n", price_printable(node->data));
2966 static QofObject price_object_def =
2969 DI(.e_type = ) GNC_ID_PRICE,
2970 DI(.type_label = ) "Price",
2971 DI(.create = ) price_create,
2972 DI(.book_begin = )
nullptr,
2973 DI(.book_end = )
nullptr,
2976 DI(.foreach = ) price_foreach,
2977 DI(.printable = ) price_printable,
2978 DI(.version_cmp = )
nullptr,
2981 static QofObject pricedb_object_def =
2984 DI(.e_type = ) GNC_ID_PRICEDB,
2985 DI(.type_label = ) "PriceDB",
2986 DI(.create = )
nullptr,
2987 DI(.book_begin = ) pricedb_book_begin,
2988 DI(.book_end = ) pricedb_book_end,
2991 DI(.foreach = )
nullptr,
2992 DI(.printable = )
nullptr,
2993 DI(.version_cmp = )
nullptr,
2997 gnc_pricedb_register (
void)
2999 static QofParam params[] =
void gnc_price_list_destroy(PriceList *prices)
gnc_price_list_destroy - destroy the given price list, calling gnc_price_unref on all the prices incl...
GNCPrice * gnc_pricedb_lookup_day_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commodities on the indicated day.
Never round at all, and signal an error if there is a fractional result in a computation.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
GNCPrice * gnc_pricedb_nth_price(GNCPriceDB *db, const gnc_commodity *c, const int n)
Get the nth price for the given commodity in reverse date order.
Date and Time handling routines.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
void gnc_gdate_set_fiscal_year_end(GDate *date, const GDate *year_end)
This function modifies a GDate to set it to the last day of the fiscal year in which it falls...
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
#define PINFO(format, args...)
Print an informational note.
GNCPrice * gnc_price_invert(GNCPrice *p)
Return a newly-allocated price that's the inverse of the given price, p.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
int gnc_pricedb_num_prices(GNCPriceDB *db, const gnc_commodity *c)
Get the number of prices, in any currency, for a given commodity.
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
#define DEBUG(format, args...)
Print a debugging message.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
void(* QofInstanceForeachCB)(QofInstance *, gpointer user_data)
Callback type for qof_collection_foreach.
GNCPriceDB * gnc_collection_get_pricedb(QofCollection *col)
Return the pricedb via the Book's collection.
gboolean gnc_pricedb_equal(GNCPriceDB *db1, GNCPriceDB *db2)
Test equality of two pricedbs.
gnc_numeric gnc_pricedb_get_nearest_price(GNCPriceDB *pdb, const gnc_commodity *orig_currency, const gnc_commodity *new_currency, const time64 t)
Retrieve the price one currency to another using the price nearest to the given time.
gboolean gnc_pricedb_remove_old_prices(GNCPriceDB *db, GList *comm_list, GDate *fiscal_end_date, time64 cutoff, PriceRemoveSourceFlags source, PriceRemoveKeepOptions keep)
Remove and unref prices older than a certain time.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
GDate time64_to_gdate(time64 t)
Returns the GDate in which the time64 occurs.
gnc_numeric gnc_pricedb_get_latest_price(GNCPriceDB *pdb, const gnc_commodity *orig_currency, const gnc_commodity *new_currency)
Retrieve the price one currency to another using the latest price.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
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.
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.