32 #include "gnc-pricedb-p.h" 33 #include <qofinstance-p.h> 36 static QofLogModule log_module = GNC_MOD_PRICE;
38 static gboolean add_price(GNCPriceDB *db, GNCPrice *p);
39 static gboolean remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup);
40 static GNCPrice *lookup_nearest_in_time(GNCPriceDB *db,
const gnc_commodity *c,
41 const gnc_commodity *currency,
42 time64 t, gboolean sameday);
44 pricedb_pricelist_traversal(GNCPriceDB *db,
45 gboolean (*f)(GList *p, gpointer user_data),
69 return a < b ? -1 : a > b ? 1 : 0;
73 hash_entry_insert(gpointer key, gpointer val, gpointer user_data)
75 GSList **result = (GSList **) user_data;
80 *result = g_slist_prepend(*result, entry);
84 hash_table_to_list(GHashTable *
table)
86 GSList *result_list = NULL;
87 g_hash_table_foreach(
table, hash_entry_insert, &result_list);
92 hash_entry_free_gfunc(gpointer data, G_GNUC_UNUSED gpointer user_data)
99 G_DEFINE_TYPE(GNCPrice, gnc_price, QOF_TYPE_INSTANCE);
102 gnc_price_init(GNCPrice* price)
105 price->value = gnc_numeric_zero();
107 price->source = PRICE_SOURCE_INVALID;
117 static const char* source_names[(size_t)PRICE_SOURCE_INVALID + 1] =
126 "user:split-register",
129 "user:stock-transaction",
136 gnc_price_dispose(GObject *pricep)
138 G_OBJECT_CLASS(gnc_price_parent_class)->dispose(pricep);
142 gnc_price_finalize(GObject* pricep)
144 G_OBJECT_CLASS(gnc_price_parent_class)->finalize(pricep);
154 gnc_price_get_property(GObject*
object, guint prop_id, GValue* value, GParamSpec* pspec)
158 g_return_if_fail(GNC_IS_PRICE(
object));
160 price = GNC_PRICE(
object);
164 g_value_set_string(value, gnc_price_get_source_string(price));
167 g_value_set_string(value, price->type);
170 g_value_set_boxed(value, &price->value);
173 g_value_take_object(value, price->commodity);
176 g_value_take_object(value, price->currency);
179 g_value_set_boxed(value, &price->tmspec);
182 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
188 gnc_price_set_property(GObject*
object, guint prop_id,
const GValue* value, GParamSpec* pspec)
194 g_return_if_fail(GNC_IS_PRICE(
object));
196 price = GNC_PRICE(
object);
197 g_assert (qof_instance_get_editlevel(price));
202 gnc_price_set_source_string(price, g_value_get_string(value));
205 gnc_price_set_typestr(price, g_value_get_string(value));
208 number = g_value_get_boxed(value);
209 gnc_price_set_value(price, *number);
212 gnc_price_set_commodity(price, g_value_get_object(value));
215 gnc_price_set_currency(price, g_value_get_object(value));
218 time = g_value_get_boxed(value);
219 gnc_price_set_time64(price, time->t);
222 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
228 gnc_price_class_init(GNCPriceClass *klass)
230 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
232 gobject_class->dispose = gnc_price_dispose;
233 gobject_class->finalize = gnc_price_finalize;
234 gobject_class->set_property = gnc_price_set_property;
235 gobject_class->get_property = gnc_price_get_property;
237 g_object_class_install_property
240 g_param_spec_object (
"commodity",
242 "The commodity field denotes the base kind of " 243 "'stuff' for the units of this quote, whether " 244 "it is USD, gold, stock, etc.",
248 g_object_class_install_property
251 g_param_spec_object (
"currency",
253 "The currency field denotes the external kind " 254 "'stuff' for the units of this quote, whether " 255 "it is USD, gold, stock, etc.",
259 g_object_class_install_property
262 g_param_spec_string (
"source",
264 "The price source is PriceSource enum describing how" 265 " the price was created. This property works on the" 266 " string values in source_names for SQL database" 271 g_object_class_install_property
274 g_param_spec_string (
"type",
276 "The quote type is a string describing the " 277 "type of a price quote. Types possible now " 278 "are 'bid', 'ask', 'last', 'nav', 'transaction', " 283 g_object_class_install_property
286 g_param_spec_boxed(
"date",
288 "The date of the price quote.",
292 g_object_class_install_property
295 g_param_spec_boxed(
"value",
297 "The value of the price quote.",
312 g_return_val_if_fail (book, NULL);
315 p = g_object_new(GNC_TYPE_PRICE, NULL);
319 LEAVE (
"price created %p", p);
324 gnc_price_destroy (GNCPrice *p)
326 ENTER(
"destroy price %p", p);
329 if (p->type) CACHE_REMOVE(p->type);
347 if (p->refcount == 0)
354 if (p->refcount <= 0)
358 PERR(
"last unref while price in database");
360 gnc_price_destroy (p);
372 g_return_val_if_fail (book, NULL);
378 LEAVE (
"return NULL");
385 LEAVE (
"return NULL");
389 qof_instance_copy_version(new_p, p);
391 gnc_price_begin_edit(new_p);
393 gnc_price_set_commodity(new_p, gnc_price_get_commodity(p));
394 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
395 gnc_price_set_source(new_p, gnc_price_get_source(p));
396 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
397 gnc_price_set_value(new_p, gnc_price_get_value(p));
398 gnc_price_set_currency(new_p, gnc_price_get_currency(p));
399 gnc_price_commit_edit(new_p);
400 LEAVE (
"return cloned price %p", new_p);
409 qof_instance_copy_version(new_p, p);
410 gnc_price_begin_edit(new_p);
411 gnc_price_set_time64(new_p, gnc_price_get_time64(p));
412 gnc_price_set_source(new_p, PRICE_SOURCE_TEMP);
413 gnc_price_set_typestr(new_p, gnc_price_get_typestr(p));
414 gnc_price_set_commodity(new_p, gnc_price_get_currency(p));
415 gnc_price_set_currency(new_p, gnc_price_get_commodity(p));
417 gnc_price_commit_edit(new_p);
424 gnc_price_begin_edit (GNCPrice *p)
431 PERR (
"Failed to commit: %d", errcode);
432 gnc_engine_signal_commit_error( errcode );
435 static void noop (QofInstance *inst) {}
438 gnc_price_commit_edit (GNCPrice *p)
463 gnc_price_set_dirty (GNCPrice *p)
465 qof_instance_set_dirty(&p->inst);
470 gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c)
480 remove_price (p->db, p, TRUE);
481 gnc_price_begin_edit (p);
483 gnc_price_set_dirty(p);
484 gnc_price_commit_edit (p);
485 add_price (p->db, p);
492 gnc_price_set_currency(GNCPrice *p, gnc_commodity *c)
502 remove_price (p->db, p, TRUE);
503 gnc_price_begin_edit (p);
505 gnc_price_set_dirty(p);
506 gnc_price_commit_edit (p);
507 add_price (p->db, p);
513 gnc_price_set_time64(GNCPrice *p,
time64 t)
522 remove_price (p->db, p, FALSE);
523 gnc_price_begin_edit (p);
525 gnc_price_set_dirty(p);
526 gnc_price_commit_edit (p);
527 add_price (p->db, p);
536 gnc_price_begin_edit (p);
538 gnc_price_set_dirty(p);
539 gnc_price_commit_edit(p);
543 gnc_price_set_source_string(GNCPrice *p,
const char* str)
547 s < PRICE_SOURCE_INVALID; ++s)
548 if (strcmp(source_names[s], str) == 0)
550 gnc_price_set_source(p, s);
557 gnc_price_set_typestr(GNCPrice *p,
const char* type)
560 if (g_strcmp0(p->type, type) != 0)
562 gnc_price_begin_edit (p);
563 CACHE_REPLACE(p->type, type);
564 gnc_price_set_dirty(p);
565 gnc_price_commit_edit (p);
570 gnc_price_set_value(GNCPrice *p, gnc_numeric value)
575 gnc_price_begin_edit (p);
577 gnc_price_set_dirty(p);
578 gnc_price_commit_edit (p);
586 gnc_price_lookup (
const GncGUID *guid, QofBook *book)
590 if (!guid || !book)
return NULL;
596 gnc_price_get_commodity(
const GNCPrice *p)
603 gnc_price_get_time64(
const GNCPrice *p)
605 return p ? p->tmspec : 0;
609 gnc_price_get_source(
const GNCPrice *p)
611 if (!p)
return PRICE_SOURCE_INVALID;
616 gnc_price_get_source_string(
const GNCPrice *p)
619 return source_names[p->source];
623 gnc_price_get_typestr(
const GNCPrice *p)
630 gnc_price_get_value(
const GNCPrice *p)
634 PERR(
"price NULL.\n");
635 return gnc_numeric_zero();
641 gnc_price_get_currency(
const GNCPrice *p)
648 gnc_price_equal (
const GNCPrice *p1,
const GNCPrice *p2)
652 if (p1 == p2)
return TRUE;
653 if (!p1 || !p2)
return FALSE;
656 gnc_price_get_commodity (p2)))
660 gnc_price_get_currency (p2)))
663 time1 = gnc_price_get_time64 (p1);
664 time2 = gnc_price_get_time64 (p2);
669 if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
672 if (g_strcmp0 (gnc_price_get_typestr (p1),
673 gnc_price_get_typestr (p2)) != 0)
677 gnc_price_get_value (p2)))
687 compare_prices_by_date(gconstpointer a, gconstpointer b)
692 if (!a && !b)
return 0;
696 time_a = gnc_price_get_time64((GNCPrice *) a);
697 time_b = gnc_price_get_time64((GNCPrice *) b);
700 result = time64_cmp(time_b, time_a);
701 if (result)
return result;
704 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
705 gnc_price_get_guid((GNCPrice *) b));
715 price_list_is_duplicate( gpointer data, gpointer user_data )
717 GNCPrice* pPrice = (GNCPrice*)data;
725 if ( !
gnc_numeric_equal( gnc_price_get_value( pPrice ), gnc_price_get_value( pStruct->pPrice ) ) )
return;
726 if ( gnc_price_get_commodity( pPrice ) != gnc_price_get_commodity( pStruct->pPrice ) )
return;
727 if ( gnc_price_get_currency( pPrice ) != gnc_price_get_currency( pStruct->pPrice ) )
return;
729 if (time_a != time_b)
return;
731 pStruct->isDupl = TRUE;
741 if (!prices || !p)
return FALSE;
748 pStruct->isDupl = FALSE;
749 g_list_foreach( *prices, price_list_is_duplicate, pStruct );
750 isDupl = pStruct->isDupl;
759 result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
760 if (!result_list)
return FALSE;
761 *prices = result_list;
769 GList *found_element;
771 if (!prices || !p)
return FALSE;
773 found_element = g_list_find(*prices, p);
774 if (!found_element)
return TRUE;
776 result_list = g_list_remove_link(*prices, found_element);
778 g_list_free(found_element);
780 *prices = result_list;
785 price_list_destroy_helper(gpointer data, gpointer user_data)
793 g_list_foreach(prices, price_list_destroy_helper, NULL);
798 gnc_price_list_equal(PriceList *prices1, PriceList *prices2)
803 if (prices1 == prices2)
return TRUE;
809 PINFO (
"prices2 has extra prices");
814 PINFO (
"prices1 has extra prices");
817 if (!gnc_price_equal (n1->data, n2->data))
839 QOF_GOBJECT_IMPL(gnc_pricedb, GNCPriceDB, QOF_TYPE_INSTANCE);
842 gnc_pricedb_init(GNCPriceDB* pdb)
844 pdb->reset_nth_price_cache = FALSE;
848 gnc_pricedb_dispose_real (GObject *pdbp)
853 gnc_pricedb_finalize_real(GObject* pdbp)
858 gnc_pricedb_create(QofBook * book)
863 g_return_val_if_fail (book, NULL);
872 PWARN (
"A price database already exists for this book!");
876 result = g_object_new(GNC_TYPE_PRICEDB, NULL);
883 qof_collection_set_data (col, result);
885 result->commodity_hash = g_hash_table_new(NULL, NULL);
886 g_return_val_if_fail (result->commodity_hash, NULL);
891 destroy_pricedb_currency_hash_data(gpointer key,
895 GList *price_list = (GList *) data;
899 for (node = price_list; node; node = node->next)
910 destroy_pricedb_commodity_hash_data(gpointer key,
914 GHashTable *currency_hash = (GHashTable *) data;
915 if (!currency_hash)
return;
916 g_hash_table_foreach (currency_hash,
917 destroy_pricedb_currency_hash_data,
919 g_hash_table_destroy(currency_hash);
926 if (db->commodity_hash)
928 g_hash_table_foreach (db->commodity_hash,
929 destroy_pricedb_commodity_hash_data,
932 g_hash_table_destroy (db->commodity_hash);
933 db->commodity_hash = NULL;
941 db->bulk_update = bulk_update;
962 if (!col)
return NULL;
971 if (!book)
return NULL;
979 num_prices_helper (GNCPrice *p, gpointer user_data)
981 guint *count = user_data;
1008 gnc_commodity *commodity;
1012 pricedb_equal_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
1015 gnc_commodity *currency = key;
1016 GList *price_list1 = val;
1020 equal_data->commodity,
1023 if (!gnc_price_list_equal (price_list1, price_list2))
1024 equal_data->equal = FALSE;
1030 pricedb_equal_foreach_currencies_hash (gpointer key, gpointer val,
1033 GHashTable *currencies_hash = val;
1036 equal_data->commodity = key;
1038 g_hash_table_foreach (currencies_hash,
1039 pricedb_equal_foreach_pricelist,
1048 if (db1 == db2)
return TRUE;
1052 PWARN (
"one is NULL");
1056 equal_data.equal = TRUE;
1057 equal_data.db2 = db2;
1059 g_hash_table_foreach (db1->commodity_hash,
1060 pricedb_equal_foreach_currencies_hash,
1063 return equal_data.equal;
1071 add_price(GNCPriceDB *db, GNCPrice *p)
1076 gnc_commodity *commodity;
1077 gnc_commodity *currency;
1078 GHashTable *currency_hash;
1080 if (!db || !p)
return FALSE;
1081 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1087 PERR (
"attempted to mix up prices across different books");
1092 commodity = gnc_price_get_commodity(p);
1095 PWARN(
"no commodity");
1099 currency = gnc_price_get_currency(p);
1102 PWARN(
"no currency");
1106 if (!db->commodity_hash)
1108 LEAVE (
"no commodity hash found ");
1115 if (!db->bulk_update)
1118 p->currency, p->tmspec);
1119 if (old_price != NULL)
1121 if (p->source > old_price->source)
1124 LEAVE (
"Better price already in DB.");
1131 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
1134 currency_hash = g_hash_table_new(NULL, NULL);
1135 g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
1138 price_list = g_hash_table_lookup(currency_hash, currency);
1141 LEAVE (
"gnc_price_list_insert failed");
1147 LEAVE (
" no price list");
1151 g_hash_table_insert(currency_hash, currency, price_list);
1156 LEAVE (
"db=%p, pr=%p dirty=%d dextroying=%d commodity=%s/%s currency_hash=%p",
1172 if (!db || !p)
return FALSE;
1174 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1178 if (FALSE == add_price(db, p))
1180 LEAVE (
" failed to add price");
1185 qof_instance_set_dirty(&db->inst);
1188 LEAVE (
"db=%p, pr=%p dirty=%d destroying=%d",
1200 remove_price(GNCPriceDB *db, GNCPrice *p, gboolean cleanup)
1203 gnc_commodity *commodity;
1204 gnc_commodity *currency;
1205 GHashTable *currency_hash;
1207 if (!db || !p)
return FALSE;
1208 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1212 commodity = gnc_price_get_commodity(p);
1215 LEAVE (
" no commodity");
1218 currency = gnc_price_get_currency(p);
1221 LEAVE (
" no currency");
1224 if (!db->commodity_hash)
1226 LEAVE (
" no commodity hash");
1230 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
1233 LEAVE (
" no currency hash");
1238 price_list = g_hash_table_lookup(currency_hash, currency);
1243 LEAVE (
" cannot remove price list");
1251 g_hash_table_insert(currency_hash, currency, price_list);
1255 g_hash_table_remove(currency_hash, currency);
1262 guint num_currencies = g_hash_table_size (currency_hash);
1263 if (0 == num_currencies)
1265 g_hash_table_remove (db->commodity_hash, commodity);
1266 g_hash_table_destroy (currency_hash);
1272 LEAVE (
"db=%p, pr=%p", db, p);
1281 memset(datebuff, 0,
sizeof(datebuff));
1282 if (!db || !p)
return FALSE;
1283 ENTER (
"db=%p, pr=%p dirty=%d destroying=%d",
1289 DEBUG(
"Remove Date is %s, Commodity is %s, Source is %s", datebuff,
1291 gnc_price_get_source_string (p));
1293 rc = remove_price (db, p, TRUE);
1295 qof_instance_set_dirty(&db->inst);
1299 gnc_price_begin_edit (p);
1300 qof_instance_set_destroying(p, TRUE);
1301 gnc_price_commit_edit (p);
1304 LEAVE (
"db=%p, pr=%p", db, p);
1313 gboolean delete_user;
1314 gboolean delete_app;
1319 check_one_price_date (GNCPrice *price, gpointer user_data)
1325 ENTER(
"price %p (%s), data %p", price,
1329 source = gnc_price_get_source (price);
1331 if ((source == PRICE_SOURCE_FQ) && data->delete_fq)
1332 PINFO (
"Delete Quote Source");
1333 else if ((source == PRICE_SOURCE_USER_PRICE) && data->delete_user)
1334 PINFO (
"Delete User Source");
1335 else if ((source != PRICE_SOURCE_FQ) && (source != PRICE_SOURCE_USER_PRICE) && data->delete_app)
1336 PINFO (
"Delete App Source");
1339 LEAVE(
"Not a matching source");
1343 time = gnc_price_get_time64 (price);
1347 DEBUG(
"checking date %s", buf);
1349 if (time < data->cutoff)
1351 data->list = g_slist_prepend(data->list, price);
1352 DEBUG(
"will delete");
1359 pricedb_remove_foreach_pricelist (gpointer key,
1363 GList *price_list = (GList *) val;
1364 GList *node = price_list;
1367 ENTER(
"key %p, value %p, data %p", key, val, user_data);
1370 g_list_foreach(node, (GFunc)check_one_price_date, data);
1376 compare_prices_by_commodity_date (gconstpointer a, gconstpointer b)
1379 gnc_commodity *comma;
1380 gnc_commodity *commb;
1381 gnc_commodity *curra;
1382 gnc_commodity *currb;
1385 if (!a && !b)
return 0;
1390 comma = gnc_price_get_commodity ((GNCPrice *) a);
1391 commb = gnc_price_get_commodity ((GNCPrice *) b);
1396 curra = gnc_price_get_currency ((GNCPrice *) a);
1397 currb = gnc_price_get_currency ((GNCPrice *) b);
1402 time_a = gnc_price_get_time64((GNCPrice *) a);
1403 time_b = gnc_price_get_time64((GNCPrice *) b);
1406 result = time64_cmp(time_b, time_a);
1407 if (result)
return result;
1410 return guid_compare (gnc_price_get_guid((GNCPrice *) a),
1411 gnc_price_get_guid((GNCPrice *) b));
1415 price_commodity_and_currency_equal (GNCPrice *a, GNCPrice *b)
1417 gboolean ret_comm = FALSE;
1418 gboolean ret_curr = FALSE;
1426 return (ret_comm && ret_curr);
1430 gnc_pricedb_remove_old_prices_pinfo (GNCPrice *price, gboolean keep_message)
1435 if (g_date_valid (&price_date))
1441 PINFO(
"#### Keep price with date %s, commodity is %s, currency is %s", date_buf,
1446 PINFO(
"## Remove price with date %s", date_buf);
1449 PINFO(
"Keep price date is invalid");
1453 clone_price (GNCPrice **price, GNCPrice *source_price)
1457 if (!source_price)
return;
1458 if (price == NULL)
return;
1467 gnc_pricedb_remove_old_prices_pinfo (source_price, TRUE);
1471 roundUp (gint numToRound, gint multiple)
1478 remainder = numToRound % multiple;
1482 return numToRound + multiple - remainder;
1486 get_fiscal_quarter (GDate *date, GDateMonth fiscal_start)
1488 GDateMonth month = g_date_get_month (date);
1490 gint q = ((roundUp (22 - fiscal_start + month, 3)/3) % 4) + 1;
1492 PINFO(
"Return fiscal quarter is %d", q);
1497 gnc_pricedb_process_removal_list (GNCPriceDB *db, GDate *fiscal_end_date,
1501 gboolean save_first_price = FALSE;
1502 gint saved_test_value = 0, next_test_value = 0;
1503 GNCPrice *cloned_price = NULL;
1504 GDateMonth fiscal_month_start;
1505 GDate *tmp_date = g_date_new_dmy (g_date_get_day (fiscal_end_date),
1506 g_date_get_month (fiscal_end_date),
1507 g_date_get_year (fiscal_end_date));
1510 g_date_subtract_months (tmp_date, 12);
1511 fiscal_month_start = g_date_get_month (tmp_date) + 1;
1512 g_date_free (tmp_date);
1515 data.list = g_slist_sort (data.list, compare_prices_by_commodity_date);
1518 for (item = data.list; item; item = g_slist_next(item))
1520 GDate saved_price_date;
1521 GDate next_price_date;
1524 if (keep == PRICE_REMOVE_KEEP_NONE)
1526 gnc_pricedb_remove_old_prices_pinfo (item->data, FALSE);
1531 save_first_price = !price_commodity_and_currency_equal (item->data, cloned_price);
1532 if (save_first_price == TRUE)
1534 clone_price (&cloned_price, item->data);
1539 saved_price_date =
time64_to_gdate (gnc_price_get_time64 (cloned_price));
1540 next_price_date =
time64_to_gdate (gnc_price_get_time64 (item->data));
1543 if (keep == PRICE_REMOVE_KEEP_LAST_PERIOD && save_first_price == FALSE)
1545 GDate *saved_fiscal_end = g_date_new_dmy (g_date_get_day (&saved_price_date),
1546 g_date_get_month (&saved_price_date),
1547 g_date_get_year (&saved_price_date));
1549 GDate *next_fiscal_end = g_date_new_dmy (g_date_get_day (&next_price_date),
1550 g_date_get_month (&next_price_date),
1551 g_date_get_year (&next_price_date));
1556 saved_test_value = g_date_get_year (saved_fiscal_end);
1557 next_test_value = g_date_get_year (next_fiscal_end);
1559 PINFO(
"Keep last price in fiscal year");
1561 g_date_free (saved_fiscal_end);
1562 g_date_free (next_fiscal_end);
1566 if (keep == PRICE_REMOVE_KEEP_LAST_QUARTERLY && save_first_price == FALSE)
1568 saved_test_value = get_fiscal_quarter (&saved_price_date, fiscal_month_start);
1569 next_test_value = get_fiscal_quarter (&next_price_date, fiscal_month_start);
1571 PINFO(
"Keep last price in fiscal quarter");
1575 if (keep == PRICE_REMOVE_KEEP_LAST_MONTHLY && save_first_price == FALSE)
1577 saved_test_value = g_date_get_month (&saved_price_date);
1578 next_test_value = g_date_get_month (&next_price_date);
1580 PINFO(
"Keep last price of every month");
1584 if (keep == PRICE_REMOVE_KEEP_LAST_WEEKLY && save_first_price == FALSE)
1586 saved_test_value = g_date_get_iso8601_week_of_year (&saved_price_date);
1587 next_test_value = g_date_get_iso8601_week_of_year (&next_price_date);
1589 PINFO(
"Keep last price of every week");
1593 if (saved_test_value == next_test_value)
1595 gnc_pricedb_remove_old_prices_pinfo (item->data, FALSE);
1599 clone_price (&cloned_price, item->data);
1607 GDate *fiscal_end_date,
time64 cutoff,
1608 PriceRemoveSourceFlags source,
1609 PriceRemoveKeepOptions keep)
1614 memset (datebuff, 0,
sizeof(datebuff));
1617 data.cutoff = cutoff;
1619 data.delete_fq = FALSE;
1620 data.delete_user = FALSE;
1621 data.delete_app = FALSE;
1623 ENTER(
"Remove Prices for Source %d, keeping %d", source, keep);
1626 if (source & PRICE_REMOVE_SOURCE_APP)
1627 data.delete_app = TRUE;
1629 if (source & PRICE_REMOVE_SOURCE_FQ)
1630 data.delete_fq = TRUE;
1632 if (source & PRICE_REMOVE_SOURCE_USER)
1633 data.delete_user = TRUE;
1636 for (node = g_list_first (comm_list); node; node = g_list_next (node))
1638 GHashTable *currencies_hash = g_hash_table_lookup (db->commodity_hash, node->data);
1639 g_hash_table_foreach (currencies_hash, pricedb_remove_foreach_pricelist, &data);
1642 if (data.list == NULL)
1644 LEAVE(
"Empty price list");
1648 DEBUG(
"Number of Prices in list is %d, Cutoff date is %s",
1649 g_slist_length (data.list), datebuff);
1652 if (fiscal_end_date == NULL)
1655 fiscal_end_date = g_date_new ();
1656 g_date_set_dmy (fiscal_end_date, 31, 12, year_now);
1658 else if (g_date_valid (fiscal_end_date) == FALSE)
1661 g_date_clear (fiscal_end_date, 1);
1662 g_date_set_dmy (fiscal_end_date, 31, 12, year_now);
1664 gnc_pricedb_process_removal_list (db, fiscal_end_date, data, keep);
1666 g_slist_free (data.list);
1674 static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b);
1677 hash_values_helper(gpointer key, gpointer value, gpointer data)
1683 new_l = pricedb_price_list_merge(*l, value);
1688 *l = g_list_copy (value);
1692 price_list_from_hashtable (GHashTable *hash,
const gnc_commodity *currency)
1694 GList *price_list = NULL, *result = NULL ;
1697 price_list = g_hash_table_lookup(hash, currency);
1700 LEAVE (
" no price list");
1703 result = g_list_copy (price_list);
1707 g_hash_table_foreach(hash, hash_values_helper, (gpointer)&result);
1713 pricedb_price_list_merge (PriceList *a, PriceList *b)
1715 PriceList *merged_list = NULL;
1719 while (next_a || next_b)
1723 merged_list = g_list_prepend (merged_list, next_b->data);
1724 next_b = next_b->next;
1726 else if (next_b == NULL)
1728 merged_list = g_list_prepend (merged_list, next_a->data);
1729 next_a = next_a->next;
1732 else if (compare_prices_by_date (next_a->data, next_b->data) < 0)
1734 merged_list = g_list_prepend (merged_list, next_a->data);
1735 next_a = next_a->next;
1739 merged_list = g_list_prepend (merged_list, next_b->data);
1740 next_b = next_b->next;
1743 return g_list_reverse (merged_list);
1747 pricedb_get_prices_internal(GNCPriceDB *db,
const gnc_commodity *commodity,
1748 const gnc_commodity *currency, gboolean bidi)
1750 GHashTable *forward_hash = NULL, *reverse_hash = NULL;
1751 PriceList *forward_list = NULL, *reverse_list = NULL;
1752 g_return_val_if_fail (db != NULL, NULL);
1753 g_return_val_if_fail (commodity != NULL, NULL);
1754 forward_hash = g_hash_table_lookup(db->commodity_hash, commodity);
1755 if (currency && bidi)
1756 reverse_hash = g_hash_table_lookup(db->commodity_hash, currency);
1757 if (!forward_hash && !reverse_hash)
1759 LEAVE (
" no currency hash");
1763 forward_list = price_list_from_hashtable (forward_hash, currency);
1764 if (currency && reverse_hash)
1766 reverse_list = price_list_from_hashtable (reverse_hash, commodity);
1776 PriceList *merged_list;
1777 merged_list = pricedb_price_list_merge (forward_list, reverse_list);
1778 g_list_free (forward_list);
1779 g_list_free (reverse_list);
1780 forward_list = merged_list;
1784 forward_list = reverse_list;
1789 return forward_list;
1793 const gnc_commodity *commodity,
1794 const gnc_commodity *currency)
1799 if (!db || !commodity || !currency)
return NULL;
1800 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
1802 price_list = pricedb_get_prices_internal(db, commodity, currency, TRUE);
1803 if (!price_list)
return NULL;
1807 result = price_list->data;
1809 g_list_free (price_list);
1810 LEAVE(
"price is %p", result);
1817 const gnc_commodity *com;
1832 price_list_scan_any_currency(GList *price_list, gpointer data)
1835 GList *node = price_list;
1842 com = gnc_price_get_commodity(node->data);
1843 cur = gnc_price_get_currency(node->data);
1847 if (com != helper->com && cur != helper->com)
1853 while (node != NULL)
1855 GNCPrice *price = node->data;
1856 time64 price_t = gnc_price_get_time64(price);
1857 if (price_t < helper->t)
1862 GNCPrice *prev_price = node->prev->data;
1864 *helper->list = g_list_prepend(*helper->list, prev_price);
1868 *helper->list = g_list_prepend(*helper->list, price);
1872 else if (node->next == NULL)
1876 *helper->list = g_list_prepend(*helper->list, price);
1888 latest_before (PriceList *prices,
const gnc_commodity* target,
time64 t)
1890 GList *node, *found_coms = NULL, *retval = NULL;
1891 for (node = prices; node != NULL; node = g_list_next(node))
1893 GNCPrice *price = (GNCPrice*)node->data;
1894 gnc_commodity *com = gnc_price_get_commodity(price);
1895 gnc_commodity *cur = gnc_price_get_currency(price);
1896 time64 price_t = gnc_price_get_time64(price);
1899 (com == target && g_list_find (found_coms, cur)) ||
1900 (cur == target && g_list_find (found_coms, com)))
1904 retval = g_list_prepend (retval, price);
1905 found_coms = g_list_prepend (found_coms, com == target ? cur : com);
1907 g_list_free (found_coms);
1908 return g_list_reverse(retval);
1912 find_comtime(GPtrArray* array, gnc_commodity *com)
1914 unsigned int index = 0;
1915 GNCPrice** retval = NULL;
1916 for (index = 0; index < array->len; ++index)
1918 GNCPrice **price_p = g_ptr_array_index(array, index);
1919 if (gnc_price_get_commodity(*price_p) == com ||
1920 gnc_price_get_currency(*price_p) == com)
1927 add_nearest_price(GList *target_list, GPtrArray *price_array, GNCPrice *price,
1928 const gnc_commodity *target,
time64 t)
1930 gnc_commodity *com = gnc_price_get_commodity(price);
1931 gnc_commodity *cur = gnc_price_get_currency(price);
1932 time64 price_t = gnc_price_get_time64(price);
1933 gnc_commodity *other = com == target ? cur : com;
1934 GNCPrice **com_price = find_comtime(price_array, other);
1936 if (com_price == NULL)
1938 com_price = (GNCPrice**)g_slice_new(gpointer);
1940 g_ptr_array_add(price_array, com_price);
1946 target_list = g_list_prepend(target_list, price);
1950 com_t = gnc_price_get_time64(*com_price);
1963 time64 com_diff = com_t - t;
1964 time64 price_diff = t - price_t;
1965 if (com_diff < price_diff)
1968 target_list = g_list_prepend(target_list, *com_price);
1973 target_list = g_list_prepend(target_list, price);
1981 nearest_to (PriceList *prices,
const gnc_commodity* target,
time64 t)
1983 GList *node, *retval = NULL;
1984 const guint prealloc_size = 5;
1989 GPtrArray *price_array = g_ptr_array_sized_new(prealloc_size);
1991 for (node = prices; node != NULL; node = g_list_next(node))
1993 GNCPrice *price = (GNCPrice*)node->data;
1994 retval = add_nearest_price(retval, price_array, price, target, t);
2000 for (index = 0; index < price_array->len; ++index)
2002 GNCPrice **com_price = g_ptr_array_index(price_array, index);
2003 time64 price_t = gnc_price_get_time64(*com_price);
2007 retval = g_list_prepend(retval, *com_price);
2010 g_ptr_array_free(price_array, TRUE);
2011 return g_list_sort(retval, compare_prices_by_date);
2018 const gnc_commodity *commodity)
2026 const gnc_commodity *commodity,
2029 GList *prices = NULL, *result;
2033 if (!db || !commodity)
return NULL;
2034 ENTER (
"db=%p commodity=%p", db, commodity);
2036 pricedb_pricelist_traversal(db, price_list_scan_any_currency, &helper);
2037 prices = g_list_sort(prices, compare_prices_by_date);
2038 result = nearest_to(prices, commodity, t);
2046 const gnc_commodity *commodity,
2049 GList *prices = NULL, *result;
2053 if (!db || !commodity)
return NULL;
2054 ENTER (
"db=%p commodity=%p", db, commodity);
2056 pricedb_pricelist_traversal(db, price_list_scan_any_currency,
2058 prices = g_list_sort(prices, compare_prices_by_date);
2059 result = latest_before(prices, commodity, t);
2071 const gnc_commodity *commodity,
2072 const gnc_commodity *currency)
2075 GHashTable *currency_hash;
2078 if (!db || !commodity)
return FALSE;
2079 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2080 currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
2083 LEAVE(
"no, no currency_hash table");
2089 price_list = g_hash_table_lookup(currency_hash, currency);
2095 LEAVE(
"no, no price list");
2099 size = g_hash_table_size (currency_hash);
2100 LEAVE(
"%s", size > 0 ?
"yes" :
"no");
2110 const gnc_commodity *commodity,
2111 const gnc_commodity *currency)
2117 if (!db || !commodity)
return NULL;
2118 ENTER (
"db=%p commodity=%p currency=%p", db, commodity, currency);
2119 result = pricedb_get_prices_internal (db, commodity, currency, FALSE);
2120 if (!result)
return NULL;
2121 for (node = result; node; node = node->next)
2131 price_count_helper(gpointer key, gpointer value, gpointer data)
2134 GList *price_list = value;
2136 *result += g_list_length(price_list);
2141 const gnc_commodity *c)
2144 GHashTable *currency_hash;
2146 if (!db || !c)
return 0;
2147 ENTER (
"db=%p commodity=%p", db, c);
2149 currency_hash = g_hash_table_lookup(db->commodity_hash, c);
2152 g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result);
2155 LEAVE (
"count=%d", result);
2161 list_combine (gpointer element, gpointer data)
2163 GList *list = *(GList**)data;
2165 *(GList**)data = g_list_copy (element);
2168 GList *new_list = g_list_concat ((GList *)list, g_list_copy (element));
2169 *(GList**)data = new_list;
2187 const gnc_commodity *c,
2190 static const gnc_commodity *last_c = NULL;
2191 static GList *prices = NULL;
2193 GNCPrice *result = NULL;
2194 GHashTable *currency_hash;
2195 g_return_val_if_fail (GNC_IS_COMMODITY (c), NULL);
2197 if (!db || !c || n < 0)
return NULL;
2200 if (last_c && prices && last_c == c && db->reset_nth_price_cache == FALSE)
2202 result = g_list_nth_data (prices, n);
2203 LEAVE (
"price=%p", result);
2211 g_list_free (prices);
2215 db->reset_nth_price_cache = FALSE;
2217 currency_hash = g_hash_table_lookup (db->commodity_hash, c);
2220 GList *currencies = g_hash_table_get_values (currency_hash);
2221 g_list_foreach (currencies, list_combine, &prices);
2222 result = g_list_nth_data (prices, n);
2223 g_list_free (currencies);
2226 LEAVE (
"price=%p", result);
2231 gnc_pricedb_nth_price_reset_cache (GNCPriceDB *db)
2234 db->reset_nth_price_cache = TRUE;
2239 const gnc_commodity *c,
2240 const gnc_commodity *currency,
2243 return lookup_nearest_in_time(db, c, currency, t, TRUE);
2248 const gnc_commodity *c,
2249 const gnc_commodity *currency,
2255 if (!db || !c || !currency)
return NULL;
2256 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2257 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2261 GNCPrice *p = item->data;
2262 time64 price_time = gnc_price_get_time64(p);
2263 if (price_time == t)
2266 g_list_free (price_list);
2267 LEAVE(
"price is %p", p);
2272 g_list_free (price_list);
2278 lookup_nearest_in_time(GNCPriceDB *db,
2279 const gnc_commodity *c,
2280 const gnc_commodity *currency,
2285 GNCPrice *current_price = NULL;
2286 GNCPrice *next_price = NULL;
2287 GNCPrice *result = NULL;
2290 if (!db || !c || !currency)
return NULL;
2291 if (t == INT64_MAX)
return NULL;
2292 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2293 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2294 if (!price_list)
return NULL;
2298 current_price = item->data;
2302 while (!next_price && item)
2304 GNCPrice *p = item->data;
2305 time64 price_time = gnc_price_get_time64(p);
2306 if (price_time <= t)
2308 next_price = item->data;
2311 current_price = item->data;
2320 result = current_price;
2328 if (price_day != t_day)
2336 time64 current_t = gnc_price_get_time64(current_price);
2337 time64 next_t = gnc_price_get_time64(next_price);
2338 time64 diff_current = current_t - t;
2339 time64 diff_next = next_t - t;
2340 time64 abs_current = llabs(diff_current);
2341 time64 abs_next = llabs(diff_next);
2349 if (current_day == t_day)
2351 if (next_day == t_day)
2354 if (abs_current < abs_next)
2355 result = current_price;
2357 result = next_price;
2361 result = current_price;
2363 else if (next_day == t_day)
2365 result = next_price;
2372 if (abs_current < abs_next)
2374 result = current_price;
2378 result = next_price;
2385 g_list_free (price_list);
2392 const gnc_commodity *c,
2393 const gnc_commodity *currency,
2396 return lookup_nearest_in_time(db, c, currency, t, FALSE);
2402 const gnc_commodity *c,
2403 const gnc_commodity *currency,
2407 GNCPrice *current_price = NULL;
2413 if (!db || !c || !currency)
return NULL;
2414 ENTER (
"db=%p commodity=%p currency=%p", db, c, currency);
2415 price_list = pricedb_get_prices_internal (db, c, currency, TRUE);
2416 if (!price_list)
return NULL;
2420 price_time = gnc_price_get_time64 (item->data);
2421 if (price_time <= t)
2422 current_price = item->data;
2425 while (price_time > t && item);
2427 g_list_free (price_list);
2429 return current_price;
2440 extract_common_prices (PriceList *from_prices, PriceList *to_prices,
2441 const gnc_commodity *from,
const gnc_commodity *to)
2444 GList *from_node = NULL, *to_node = NULL;
2445 GNCPrice *from_price = NULL, *to_price = NULL;
2447 for (from_node = from_prices; from_node != NULL;
2448 from_node = g_list_next(from_node))
2450 for (to_node = to_prices; to_node != NULL;
2451 to_node = g_list_next(to_node))
2453 gnc_commodity *to_com, *to_cur;
2454 gnc_commodity *from_com, *from_cur;
2455 to_price = GNC_PRICE(to_node->data);
2456 from_price = GNC_PRICE(from_node->data);
2457 to_com = gnc_price_get_commodity (to_price);
2458 to_cur = gnc_price_get_currency (to_price);
2459 from_com = gnc_price_get_commodity (from_price);
2460 from_cur = gnc_price_get_currency (from_price);
2461 if (((to_com == from_com || to_com == from_cur) &&
2462 (to_com != from && to_com != to)) ||
2463 ((to_cur == from_com || to_cur == from_cur) &&
2464 (to_cur != from && to_cur != to)))
2469 if (to_price != NULL && from_price != NULL)
2472 if (from_price == NULL || to_price == NULL)
2476 retval.from = from_price;
2477 retval.to = to_price;
2483 convert_price (
const gnc_commodity *from,
const gnc_commodity *to,
PriceTuple tuple)
2485 gnc_commodity *from_com = gnc_price_get_commodity (tuple.from);
2486 gnc_commodity *from_cur = gnc_price_get_currency (tuple.from);
2487 gnc_commodity *to_com = gnc_price_get_commodity (tuple.to);
2488 gnc_commodity *to_cur = gnc_price_get_currency (tuple.to);
2489 gnc_numeric from_val = gnc_price_get_value (tuple.from);
2490 gnc_numeric to_val = gnc_price_get_value (tuple.to);
2499 if (from_cur == from && to_cur == to)
2502 if (from_com == from && to_com == to)
2507 if (from_cur == from)
2514 indirect_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2515 const gnc_commodity *to,
time64 t, gboolean before_date)
2517 GList *from_prices = NULL, *to_prices = NULL;
2519 gnc_numeric zero = gnc_numeric_zero();
2530 else if (before_date)
2542 if (!from_prices || !to_prices)
2548 tuple = extract_common_prices (from_prices, to_prices, from, to);
2552 return convert_price (from, to, tuple);
2558 direct_price_conversion (GNCPriceDB *db,
const gnc_commodity *from,
2559 const gnc_commodity *to,
time64 t, gboolean before_date)
2562 gnc_numeric retval = gnc_numeric_zero();
2564 if (!from || !to)
return retval;
2568 else if (before_date)
2573 if (!price)
return retval;
2575 retval = gnc_price_get_value (price);
2577 if (gnc_price_get_commodity (price) != from)
2585 get_nearest_price (GNCPriceDB *pdb,
2586 const gnc_commodity *orig_curr,
2587 const gnc_commodity *new_curr,
2594 return gnc_numeric_create (1, 1);
2597 price = direct_price_conversion (pdb, orig_curr, new_curr, t, before);
2603 price = indirect_price_conversion (pdb, orig_curr, new_curr, t, before);
2610 const gnc_commodity *orig_currency,
2611 const gnc_commodity *new_currency,
2614 return get_nearest_price (pdb, orig_currency, new_currency, t, TRUE);
2619 const gnc_commodity *orig_currency,
2620 const gnc_commodity *new_currency,
2623 return get_nearest_price (pdb, orig_currency, new_currency, t, FALSE);
2628 const gnc_commodity *orig_currency,
2629 const gnc_commodity *new_currency)
2631 return get_nearest_price (pdb, orig_currency, new_currency, INT64_MAX, FALSE);
2635 convert_amount_at_date (GNCPriceDB *pdb,
2637 const gnc_commodity *orig_currency,
2638 const gnc_commodity *new_currency,
2640 gboolean before_date)
2647 price = get_nearest_price (pdb, orig_currency, new_currency, t, before_date);
2651 return gnc_numeric_zero ();
2663 gnc_numeric balance,
2664 const gnc_commodity *balance_currency,
2665 const gnc_commodity *new_currency)
2667 return convert_amount_at_date
2668 (pdb, balance, balance_currency, new_currency, INT64_MAX, FALSE);
2673 gnc_numeric balance,
2674 const gnc_commodity *balance_currency,
2675 const gnc_commodity *new_currency,
2678 return convert_amount_at_date
2679 (pdb, balance, balance_currency, new_currency, t, FALSE);
2684 gnc_numeric balance,
2685 const gnc_commodity *balance_currency,
2686 const gnc_commodity *new_currency,
2689 return convert_amount_at_date
2690 (pdb, balance, balance_currency, new_currency, t, TRUE);
2700 gboolean (*func)(GNCPrice *p, gpointer user_data);
2705 pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2707 GList *price_list = (GList *) val;
2708 GList *node = price_list;
2712 while (foreach_data->ok && node)
2714 GNCPrice *p = (GNCPrice *) node->data;
2715 foreach_data->ok = foreach_data->func(p, foreach_data->user_data);
2721 pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2723 GHashTable *currencies_hash = (GHashTable *) val;
2724 g_hash_table_foreach(currencies_hash, pricedb_foreach_pricelist, user_data);
2728 unstable_price_traversal(GNCPriceDB *db,
2729 gboolean (*f)(GNCPrice *p, gpointer user_data),
2734 if (!db || !f)
return FALSE;
2735 foreach_data.ok = TRUE;
2736 foreach_data.func = f;
2737 foreach_data.user_data = user_data;
2738 if (db->commodity_hash == NULL)
2742 g_hash_table_foreach(db->commodity_hash,
2743 pricedb_foreach_currencies_hash,
2746 return foreach_data.ok;
2753 gboolean (*func)(GList *p, gpointer user_data);
2758 pricedb_pricelist_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
2760 GList *price_list = (GList *) val;
2762 if (foreach_data->ok)
2764 foreach_data->ok = foreach_data->func(price_list, foreach_data->user_data);
2769 pricedb_pricelist_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
2771 GHashTable *currencies_hash = (GHashTable *) val;
2772 g_hash_table_foreach(currencies_hash, pricedb_pricelist_foreach_pricelist, user_data);
2776 pricedb_pricelist_traversal(GNCPriceDB *db,
2777 gboolean (*f)(GList *p, gpointer user_data),
2782 if (!db || !f)
return FALSE;
2783 foreach_data.ok = TRUE;
2784 foreach_data.func = f;
2785 foreach_data.user_data = user_data;
2786 if (db->commodity_hash == NULL)
2790 g_hash_table_foreach(db->commodity_hash,
2791 pricedb_pricelist_foreach_currencies_hash,
2794 return foreach_data.ok;
2798 compare_hash_entries_by_commodity_key(gconstpointer a, gconstpointer b)
2806 if (a == b)
return 0;
2807 if (!a && !b)
return 0;
2811 ca = (gnc_commodity *) he_a->key;
2812 cb = (gnc_commodity *) he_b->key;
2817 if (cmp_result != 0)
return cmp_result;
2824 stable_price_traversal(GNCPriceDB *db,
2825 gboolean (*f)(GNCPrice *p, gpointer user_data),
2828 GSList *currency_hashes = NULL;
2832 if (!db || !f)
return FALSE;
2834 currency_hashes = hash_table_to_list(db->commodity_hash);
2835 currency_hashes = g_slist_sort(currency_hashes,
2836 compare_hash_entries_by_commodity_key);
2838 for (i = currency_hashes; i; i = i->next)
2841 GHashTable *currency_hash = (GHashTable *) entry->value;
2842 GSList *price_lists = hash_table_to_list(currency_hash);
2845 price_lists = g_slist_sort(price_lists, compare_hash_entries_by_commodity_key);
2846 for (j = price_lists; j; j = j->next)
2849 GList *price_list = (GList *) pricelist_entry->value;
2852 for (node = (GList *) price_list; node; node = node->next)
2854 GNCPrice *price = (GNCPrice *) node->data;
2857 if (FALSE == ok)
break;
2858 if (!f(price, user_data)) ok = FALSE;
2863 g_slist_foreach(price_lists, hash_entry_free_gfunc, NULL);
2864 g_slist_free(price_lists);
2869 if (currency_hashes)
2871 g_slist_foreach(currency_hashes, hash_entry_free_gfunc, NULL);
2872 g_slist_free(currency_hashes);
2879 GncPriceForeachFunc f,
2881 gboolean stable_order)
2883 ENTER (
"db=%p f=%p", db, f);
2886 LEAVE (
" stable order found");
2887 return stable_price_traversal(db, f, user_data);
2889 LEAVE (
" use unstable order");
2890 return unstable_price_traversal(db, f, user_data);
2898 gnc_commodity *old_c;
2899 gnc_commodity *new_c;
2903 add_price_to_list (GNCPrice *p, gpointer data)
2905 GList **list = data;
2907 *list = g_list_prepend (*list, p);
2913 gnc_price_fixup_legacy_commods(gpointer data, gpointer user_data)
2917 gnc_commodity *price_c;
2921 price_c = gnc_price_get_commodity(p);
2924 gnc_price_set_commodity (p, fixup_data->new_c);
2926 price_c = gnc_price_get_currency(p);
2929 gnc_price_set_currency (p, fixup_data->new_c);
2934 gnc_pricedb_substitute_commodity(GNCPriceDB *db,
2935 gnc_commodity *old_c,
2936 gnc_commodity *new_c)
2939 GList *prices = NULL;
2941 if (!db || !old_c || !new_c)
return;
2948 g_list_foreach (prices, gnc_price_fixup_legacy_commods, &data);
2950 g_list_free (prices);
2960 gnc_commodity *commodity;
2961 gnc_commodity *currency;
2968 commodity = gnc_price_get_commodity(p);
2969 currency = gnc_price_get_currency(p);
2971 if (!commodity)
return;
2972 if (!currency)
return;
2974 istr = g_strnfill(indent,
' ');
2976 fprintf(f,
"%s<pdb:price>\n", istr);
2977 fprintf(f,
"%s <pdb:commodity pointer=%p>\n", istr, commodity);
2979 str = str ? str :
"(null)";
2980 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2982 str = str ? str :
"(null)";
2983 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2984 fprintf(f,
"%s </pdb:commodity>\n", istr);
2985 fprintf(f,
"%s <pdb:currency pointer=%p>\n", istr, currency);
2987 str = str ? str :
"(null)";
2988 fprintf(f,
"%s <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n", istr, str);
2990 str = str ? str :
"(null)";
2991 fprintf(f,
"%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
2992 fprintf(f,
"%s </pdb:currency>\n", istr);
2993 str = source_names[gnc_price_get_source(p)];
2994 str = str ? str :
"invalid";
2995 fprintf(f,
"%s %s\n", istr, str);
2996 str = gnc_price_get_typestr(p);
2997 str = str ? str :
"(null)";
2998 fprintf(f,
"%s %s\n", istr, str);
3000 fprintf(f,
"%s</pdb:price>\n", istr);
3006 print_pricedb_adapter(GNCPrice *p, gpointer user_data)
3008 FILE *f = (FILE *) user_data;
3018 PERR(
"NULL PriceDB\n");
3023 PERR(
"NULL FILE*\n");
3027 fprintf(f,
"<gnc:pricedb>\n");
3029 fprintf(f,
"</gnc:pricedb>\n");
3036 pricedb_book_begin (QofBook *book)
3038 gnc_pricedb_create(book);
3042 pricedb_book_end (QofBook *book)
3051 qof_collection_set_data(col, NULL);
3056 price_create (QofBook *book)
3066 void (*func)(GNCPrice *p, gpointer user_data);
3072 void_pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
3074 GList *price_list = (GList *) val;
3075 GList *node = price_list;
3080 GNCPrice *p = (GNCPrice *) node->data;
3081 foreach_data->func(p, foreach_data->user_data);
3087 void_pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
3089 GHashTable *currencies_hash = (GHashTable *) val;
3090 g_hash_table_foreach(currencies_hash, void_pricedb_foreach_pricelist, user_data);
3094 void_unstable_price_traversal(GNCPriceDB *db,
3095 void (*f)(GNCPrice *p, gpointer user_data),
3100 if (!db || !f)
return;
3101 foreach_data.func = f;
3102 foreach_data.user_data = user_data;
3104 g_hash_table_foreach(db->commodity_hash,
3105 void_pricedb_foreach_currencies_hash,
3115 void_unstable_price_traversal(db,
3116 (
void (*)(GNCPrice *, gpointer)) cb,
3122 #ifdef DUMP_FUNCTIONS 3124 static void price_list_dump(GList *price_list,
const char *tag);
3128 price_printable(gpointer obj)
3131 gnc_commodity *commodity;
3132 gnc_commodity *currency;
3133 static char buff[2048];
3138 #ifdef DUMP_FUNCTIONS 3142 price_list_dump(NULL,
"");
3148 commodity = gnc_price_get_commodity(pr);
3149 currency = gnc_price_get_currency(pr);
3151 g_snprintf (buff, 2048,
"%s %s / %s on %s", val,
3160 #ifdef DUMP_FUNCTIONS 3163 price_list_dump(GList *price_list,
const char *tag)
3167 printf(
"Price list %s\n", tag);
3168 for (node = price_list; node != NULL; node = node->next)
3170 printf(
"%s\n", price_printable(node->data));
3182 static QofObject price_object_def =
3185 DI(.e_type = ) GNC_ID_PRICE,
3186 DI(.type_label = ) "Price",
3187 DI(.create = ) price_create,
3188 DI(.book_begin = ) NULL,
3189 DI(.book_end = ) NULL,
3192 DI(.foreach = ) price_foreach,
3193 DI(.printable = ) price_printable,
3194 DI(.version_cmp = ) NULL,
3197 static QofObject pricedb_object_def =
3200 DI(.e_type = ) GNC_ID_PRICEDB,
3201 DI(.type_label = ) "PriceDB",
3202 DI(.create = ) NULL,
3203 DI(.book_begin = ) pricedb_book_begin,
3204 DI(.book_end = ) pricedb_book_end,
3207 DI(.foreach = ) NULL,
3208 DI(.printable = ) NULL,
3209 DI(.version_cmp = ) NULL,
3213 gnc_pricedb_register (
void)
3215 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...
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
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.
GNCPrice * gnc_pricedb_lookup_at_time64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Find the price between two commodities at a time64.
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.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
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.
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
gboolean gnc_price_list_insert(PriceList **prices, GNCPrice *p, gboolean check_dupl)
gnc_price_list_insert - insert a price into the given list, calling gnc_price_ref on it during the pr...
#define ENTER(format, args...)
Print a function entry debugging message.
gnc_numeric gnc_pricedb_convert_balance_nearest_before_price_t64(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency, time64 t)
Convert a balance from one currency to another using the price nearest to before the given time...
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gnc_numeric gnc_numeric_reduce(gnc_numeric in)
Return input after reducing it by Greater Common Factor (GCF) elimination.
gnc_numeric gnc_pricedb_convert_balance_latest_price(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency)
Convert a balance from one currency to another using the most recent price between the two...
#define PWARN(format, args...)
Log a warning.
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
void gnc_pricedb_destroy(GNCPriceDB *db)
Destroy the given pricedb and unref all of the prices it contains.
guint gnc_pricedb_get_num_prices(GNCPriceDB *db)
Return the number of prices in the database.
void gnc_price_print(GNCPrice *p, FILE *f, int indent)
This simple function can be useful for debugging the price code.
gnc_numeric gnc_pricedb_get_nearest_before_price(GNCPriceDB *pdb, const gnc_commodity *orig_currency, const gnc_commodity *new_currency, const time64 t)
Retrieve the price one currency to another using the price nearest to before the given time...
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
Use unbiased ("banker's") rounding.
PriceList * gnc_pricedb_lookup_nearest_in_time_any_currency_t64(GNCPriceDB *db, const gnc_commodity *commodity, time64 t)
Return the price nearest in time to that given between the given commodity and every other...
void gnc_pricedb_begin_edit(GNCPriceDB *pdb)
Begin an edit.
PriceList * gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db, const gnc_commodity *commodity)
Find the most recent price between a commodity and all other commodities.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and ...
gboolean qof_instance_books_equal(gconstpointer ptr1, gconstpointer ptr2)
See if two QofInstances share the same book.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
GNCPrice * gnc_pricedb_lookup_nearest_in_time64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commoditiesz nearest to the given time.
Never round at all, and signal an error if there is a fractional result in a computation.
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 local time
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
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.
PriceList * gnc_pricedb_lookup_nearest_before_any_currency_t64(GNCPriceDB *db, const gnc_commodity *commodity, time64 t)
Return the nearest price between the given commodity and any other before the given time...
time64 time64CanonicalDayTime(time64 t)
convert a time64 on a certain day (localtime) to the time64 representing midday on that day...
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
The type used to store guids in C.
char * gnc_time64_to_iso8601_buff(time64 time, char *buff)
The gnc_time64_to_iso8601_buff() routine takes the input UTC time64 value and prints it as an ISO-860...
void gnc_pricedb_print_contents(GNCPriceDB *db, FILE *f)
This simple function can be useful for debugging the pricedb code.
GNCPrice * gnc_pricedb_lookup_nearest_before_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the nearest price between the given commodities before the given time.
void gnc_price_ref(GNCPrice *p)
gnc_price_ref - indicate your need for a given price to stick around (i.e.
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
void gnc_pricedb_commit_edit(GNCPriceDB *pdb)
Commit an edit.
PriceList * gnc_pricedb_get_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Return all the prices for a given commodity in another.
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).