GnuCash 038f90a+
Loading...
Searching...
No Matches
gnc-commodity.cpp
1/********************************************************************
2 * gnc-commodity.c -- api for tradable commodities (incl. currency) *
3 * Copyright (C) 2000 Bill Gribble *
4 * Copyright (C) 2001,2003 Linas Vepstas <linas@linas.org> *
5 * Copyright (c) 2006 David Hampton <hampton@employees.org> *
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License, or (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License*
18 * along with this program; if not, contact: *
19 * *
20 * Free Software Foundation Voice: +1-617-542-5942 *
21 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22 * Boston, MA 02110-1301, USA gnu@gnu.org *
23 * *
24 *******************************************************************/
25
26#include <config.h>
27
28#include <glib.h>
29#include <glib/gi18n.h>
30#include <ctype.h>
31#include <limits.h>
32#include <string.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <regex.h>
36#include <qofinstance-p.h>
37
38#include "gnc-commodity.hpp"
39#include "gnc-commodity.h"
40#include "gnc-locale-utils.h"
41#include "gnc-prefs.h"
42#include "guid.h"
43#include "qofinstance.h"
44
45#include <list>
46#include <unordered_map>
47
48static QofLogModule log_module = GNC_MOD_COMMODITY;
49
50/* Parts per unit is nominal, i.e. number of 'partname' units in
51 * a 'unitname' unit. fraction is transactional, i.e. how many
52 * of the smallest-transactional-units of the currency are there
53 * in a 'unitname' unit. */
54
55enum
56{
57 PROP_0,
58 PROP_NAMESPACE, /* Table */
59 PROP_FULL_NAME, /* Table */
60 PROP_MNEMONIC, /* Table */
61 PROP_PRINTNAME, /* Constructed */
62 PROP_CUSIP, /* Table */
63 PROP_FRACTION, /* Table */
64 PROP_UNIQUE_NAME, /* Constructed */
65 PROP_QUOTE_FLAG, /* Table */
66 PROP_QUOTE_SOURCE, /* Table */
67 PROP_QUOTE_TZ, /* Table */
68};
69
71{
72 QofInstance inst;
73};
74
76{
77 gnc_commodity_namespace *name_space;
78
79 const char *fullname;
80 const char *mnemonic;
81 char *printname;
82 const char *cusip; /* CUSIP or other identifying code */
83 int fraction;
84 char *unique_name;
85 char *user_symbol;
86
87 gboolean quote_flag; /* user wants price quotes */
88 gnc_quote_source *quote_source; /* current/old source of quotes */
89 const char *quote_tz;
90
91 /* the number of accounts using this commodity - this field is not
92 * persisted */
93 int usage_count;
94
95 /* the default display_symbol, set in iso-4217-currencies at start-up */
96 const char *default_symbol;
98
99#define GET_PRIVATE(o) \
100 ((gnc_commodityPrivate*)gnc_commodity_get_instance_private((gnc_commodity*)o))
101
103{
104 QofInstanceClass parent_class;
105};
106
107static void commodity_free(gnc_commodity * cm);
108static void gnc_commodity_set_default_symbol(gnc_commodity *, const char *);
109
111{
112 QofInstance inst;
113
114 const gchar *name;
115 gboolean iso4217;
116 GHashTable * cm_table;
117 GList * cm_list;
118};
119
121{
122 QofInstanceClass parent_class;
123};
124
126{
127 GHashTable * ns_table;
128 GList * ns_list;
129};
130
131static const std::unordered_map<std::string,std::string> gnc_new_iso_codes =
132{
133 {"RUR", "RUB"}, /* Russian Ruble: RUR through 1997-12, RUB from 1998-01 onwards; see bug #393185 */
134 {"PLZ", "PLN"}, /* Polish Zloty */
135 {"UAG", "UAH"}, /* Ukraine Hryvnia */
136 {"NIS", "ILS"}, /* New Israeli Shekel: The informal abbreviation may be "NIS", but
137 its iso-4217 is clearly ILS and only this! Incorrectly changed
138 due to bug#152755 (Nov 2004) and changed back again by bug#492417
139 (Oct 2008). */
140 {"MXP", "MXN"}, /* Mexican (Nuevo) Peso */
141 {"TRL", "TRY"}, /* New Turkish Lira: changed 2005 */
142
143 /* Only add currencies to this table when the old currency no longer
144 * exists in the file iso-4217-currencies.xml */
145};
146
147static std::string fq_version;
148
150{
151private:
152 gboolean m_supported;
153 QuoteSourceType m_type;
154 std::string m_user_name; /* User friendly name incl. region code*/
155 std::string m_internal_name; /* Name used internally and by finance::quote. */
156public:
157 bool get_supported () const { return m_supported; }
158 void set_supported (bool supported) { m_supported = supported; }
159 QuoteSourceType get_type () const { return m_type; }
160 const char* get_user_name () const { return m_user_name.c_str(); }
161 const char* get_internal_name () const { return m_internal_name.c_str(); }
162 gnc_quote_source_s (gboolean supported, QuoteSourceType type,
163 const char* username, const char* int_name)
164 : m_supported{supported}
165 , m_type{type}
166 , m_user_name{username ? username : ""}
167 , m_internal_name{int_name ? int_name: ""} { };
168};
169
170using QuoteSourceList = std::list<gnc_quote_source>;
171
172/* To update the following lists scan
173 * from github.com/finance-quote/finance-quote
174 * in lib/Finance/Quote/ all *.pm for "methods"
175 * because many of them have more than one -
176 * ideally after each release of them.
177 *
178 * Apply changes here also to the FQ appendix of help.
179 */
180static QuoteSourceList currency_quote_sources =
181{
182 { true, SOURCE_CURRENCY, "Currency", "currency" }
183};
184
185/* The single quote method is usually the module name, but
186 * sometimes it gets the suffix "_direct"
187 * and the failover method is without suffix.
188 */
189static QuoteSourceList single_quote_sources =
190{
191 { false, SOURCE_SINGLE, NC_("FQ Source", "Alphavantage"), "alphavantage" },
192 { false, SOURCE_SINGLE, NC_("FQ Source", "Association of Mutual Funds in India"), "amfiindia" },
193 { false, SOURCE_SINGLE, NC_("FQ Source", "Athens Exchange Group, GR"), "asegr" },
194 { false, SOURCE_SINGLE, NC_("FQ Source", "Australian Stock Exchange, AU"), "asx" },
195 { false, SOURCE_SINGLE, NC_("FQ Source", "Italian Stock Exchange, IT"), "borsa_italiana" },
196 { false, SOURCE_SINGLE, NC_("FQ Source", "BSE India, IN"), "bseindia" },
197 { false, SOURCE_SINGLE, NC_("FQ Source", "Bucharest Stock Exchange, RO"), "bvb" },
198 { false, SOURCE_SINGLE, NC_("FQ Source", "Colombo Stock Exchange, LK"), "cse" },
199 { false, SOURCE_SINGLE, NC_("FQ Source", "comdirect, DE"), "comdirect" },
200 { false, SOURCE_SINGLE, NC_("FQ Source", "Consors Bank, DE"), "consorsbank" },
201 { false, SOURCE_SINGLE, NC_("FQ Source", "Deka Investments, DE"), "deka" },
202 { false, SOURCE_SINGLE, NC_("FQ Source", "Financial Times Funds service, GB"), "ftfunds" },
203 { false, SOURCE_SINGLE, NC_("FQ Source", "Finanzpartner, DE"), "finanzpartner" },
204 { false, SOURCE_SINGLE, NC_("FQ Source", "Finnhub, US"), "finnhub" },
205 { false, SOURCE_SINGLE, NC_("FQ Source", "FondsWeb, DE"), "fondsweb" },
206 { false, SOURCE_SINGLE, NC_("FQ Source", "GoldMoney precious metals"), "goldmoney" },
207 { false, SOURCE_SINGLE, NC_("FQ Source", "Google Web, US Stocks"), "googleweb" },
208 { false, SOURCE_SINGLE, NC_("FQ Source", "Market Watch"), "marketwatch" },
209 { false, SOURCE_SINGLE, NC_("FQ Source", "Morningstar, JP"), "morningstarjp" },
210 { false, SOURCE_SINGLE, NC_("FQ Source", "Motley Fool"), "fool" },
211 { false, SOURCE_SINGLE, NC_("FQ Source", "New Zealand stock eXchange, NZ"), "nzx" },
212 { false, SOURCE_SINGLE, NC_("FQ Source", "NSE (National Stock Exchange), IN"), "nseindia" },
213 { false, SOURCE_SINGLE, NC_("FQ Source", "OnVista, DE"), "onvista"},
214 { false, SOURCE_SINGLE, NC_("FQ Source", "Paris Stock Exchange/Boursorama, FR"), "bourso" },
215 { false, SOURCE_SINGLE, NC_("FQ Source", "S-Investor, DE"), "sinvestor"},
216 { false, SOURCE_SINGLE, NC_("FQ Source", "Sharenet, ZA"), "za" },
217 { false, SOURCE_SINGLE, NC_("FQ Source", "SIX Swiss Exchange shares, CH"), "six" },
218 { false, SOURCE_SINGLE, NC_("FQ Source", "StockData"), "stockdata" },
219 { false, SOURCE_SINGLE, NC_("FQ Source", "Stooq, PL"), "stooq" },
220 { false, SOURCE_SINGLE, NC_("FQ Source", "Swiss Fund Data AG, CH"), "swissfunddata" },
221 { false, SOURCE_SINGLE, NC_("FQ Source", "Tesouro Direto bonds, BR"), "tesouro_direto" },
222 { false, SOURCE_SINGLE, NC_("FQ Source", "Toronto Stock eXchange, CA"), "tsx" },
223 { false, SOURCE_SINGLE, NC_("FQ Source", "Tradegate, DE"), "tradegate" },
224 { false, SOURCE_SINGLE, NC_("FQ Source", "Treasury Direct bonds, US"), "treasurydirect" },
225 { false, SOURCE_SINGLE, NC_("FQ Source", "Twelve Data"), "twelvedata" },
226 { false, SOURCE_SINGLE, NC_("FQ Source", "Union Investment, DE"), "unionfunds" },
227 { false, SOURCE_SINGLE, NC_("FQ Source", "US Savings Bonds, US"), "usbonds" },
228 { false, SOURCE_SINGLE, NC_("FQ Source", "US Govt. Thrift Savings Plan"), "tsp" },
229 { false, SOURCE_SINGLE, NC_("FQ Source", "XETRA, DE"), "xetra" },
230 { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo as JSON"), "yahoo_json" },
231 { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo Web"), "yahooweb" },
232 { false, SOURCE_SINGLE, NC_("FQ Source", "YH Finance (FinanceAPI)"), "financeapi" },
233};
234
235// Finance::Quote defines these as failover methods
236static QuoteSourceList multiple_quote_sources =
237{
238 { false, SOURCE_MULTI, NC_("FQ Source", "Canada (Alphavantage, TMX)"), "canada" },
239 { false, SOURCE_MULTI, NC_("FQ Source", "Europe (ASEGR, Bourso, …)"), "europe" },
240 { false, SOURCE_MULTI, NC_("FQ Source", "India (BSEIndia, NSEIndia)"), "india"},
241 { false, SOURCE_MULTI, NC_("FQ Source", "Nasdaq (Alphavantage, FinanceAPI, …)"), "nasdaq" },
242 { false, SOURCE_MULTI, NC_("FQ Source", "NYSE (Alphavantage, FinanceAPI, …)"), "nyse" },
243 { false, SOURCE_MULTI, NC_("FQ Source", "U.K. Funds (FTfunds)"), "ukfunds" },
244 { false, SOURCE_MULTI, NC_("FQ Source", "USA (Alphavantage, FinanceAPI, …)"), "usa" },
245};
246
247static QuoteSourceList new_quote_sources;
248
249// cannot use map or unordered_map because order must be preserved
250static const std::vector<std::pair<QuoteSourceType,QuoteSourceList&>> quote_sources_map =
251 {
252 { SOURCE_CURRENCY, currency_quote_sources },
253 { SOURCE_SINGLE, single_quote_sources },
254 { SOURCE_MULTI, multiple_quote_sources },
255 { SOURCE_UNKNOWN, new_quote_sources }
256 };
257
258/********************************************************************
259 * gnc_quote_source_fq_installed
260 *
261 * This function indicates whether or not the Finance::Quote module
262 * is installed on a users computer.
263 ********************************************************************/
264gboolean
266{
267 return (!fq_version.empty());
268}
269
270
271/********************************************************************
272 * gnc_quote_source_fq_version
273 *
274 * This function the version of the Finance::Quote module installed
275 * on a user's computer or nullptr if no installation is found.
276 ********************************************************************/
277const char*
279{
280 return fq_version.c_str();
281}
282
283static QuoteSourceList&
284get_quote_source_from_type (QuoteSourceType type)
285{
286 auto quote_sources_it = std::find_if (quote_sources_map.begin(), quote_sources_map.end(),
287 [type] (const auto& qs) { return type == qs.first; });
288
289 if (quote_sources_it != quote_sources_map.end())
290 return quote_sources_it->second;
291
292 PWARN ("Invalid Quote Source %d, returning new_quote_sources", type);
293 return new_quote_sources;
294}
295
296/********************************************************************
297 * gnc_quote_source_num_entries
298 *
299 * Return the number of entries for a given type of price source.
300 ********************************************************************/
302{
303 return get_quote_source_from_type(type).size();
304}
305
306
307
308/********************************************************************
309 * gnc_quote_source_add_new
310 *
311 * Add a new price source. Called when unknown source names are found
312 * either in the F::Q installation (a newly available source) or in
313 * the user's data file (a source that has vanished but needs to be
314 * tracked.)
315 ********************************************************************/
316gnc_quote_source *
317gnc_quote_source_add_new (const char *source_name, gboolean supported)
318{
319 DEBUG("Creating new source %s", (!source_name ? "(null)" : source_name));
320 /* This name can be changed if/when support for this price source is
321 * integrated into gnucash. */
322 /* This name is permanent and must be kept the same if/when support
323 * for this price source is integrated into gnucash (i.e. for a
324 * nice user name). */
325 return &new_quote_sources.emplace_back (supported, SOURCE_UNKNOWN, source_name, source_name);
326}
327
328/********************************************************************
329 * gnc_quote_source_lookup_by_xxx
330 *
331 * Lookup a price source data structure based upon various criteria.
332 ********************************************************************/
333gnc_quote_source *
335{
336 ENTER("type/index is %d/%d", type, index);
337 auto& sources = get_quote_source_from_type (type);
338 if ((size_t) index < sources.size())
339 {
340 auto it = std::next(sources.begin(), index);
341 LEAVE("found %s", it->get_user_name());
342 return &*it;
343 }
344
345 LEAVE("not found");
346 return nullptr;
347}
348
349gnc_quote_source *
351{
352 if (!name || !*name)
353 return nullptr;
354
355 for (const auto& [_, sources] : quote_sources_map)
356 {
357 auto source_it = std::find_if (sources.begin(), sources.end(),
358 [name] (const auto& qs)
359 { return (g_strcmp0(name, qs.get_internal_name()) == 0); });
360 if (source_it != sources.end())
361 return &(*source_it);
362 }
363
364 DEBUG("gnc_quote_source_lookup_by_internal: Unknown source %s", name);
365 return nullptr;
366}
367
368/********************************************************************
369 * gnc_quote_source_get_xxx
370 *
371 * Accessor functions - get functions only. There are no set functions.
372 ********************************************************************/
374gnc_quote_source_get_type (const gnc_quote_source *source)
375{
376 ENTER("%p", source);
377 if (!source)
378 {
379 LEAVE("bad source");
380 return SOURCE_SINGLE;
381 }
382
383 LEAVE("type is %d", source->get_type());
384 return source->get_type();
385}
386
387gint
388gnc_quote_source_get_index (const gnc_quote_source *source)
389{
390 if (!source)
391 {
392 PWARN ("bad source");
393 return 0;
394 }
395
396 auto& sources = get_quote_source_from_type (source->get_type());
397 auto is_source = [&source](const auto& findif_source)
398 { return &findif_source == source; };
399
400 auto iter = std::find_if (sources.begin(), sources.end(), is_source);
401 if (iter != sources.end())
402 return std::distance (sources.begin(), iter);
403
404 PWARN ("couldn't locate source");
405 return 0;
406}
407
408gboolean
409gnc_quote_source_get_supported (const gnc_quote_source *source)
410{
411 ENTER("%p", source);
412 if (!source)
413 {
414 LEAVE("bad source");
415 return FALSE;
416 }
417
418 LEAVE("%s supported", source && source->get_supported() ? "" : "not ");
419 return source->get_supported();
420}
421
422const char *
423gnc_quote_source_get_user_name (const gnc_quote_source *source)
424{
425 ENTER("%p", source);
426 if (!source)
427 {
428 LEAVE("bad source");
429 return nullptr;
430 }
431 LEAVE("user name %s", source->get_user_name());
432 return source->get_user_name();
433}
434
435const char *
436gnc_quote_source_get_internal_name (const gnc_quote_source *source)
437{
438 ENTER("%p", source);
439 if (!source)
440 {
441 LEAVE("bad source");
442 return nullptr;
443 }
444 LEAVE("internal name %s", source->get_internal_name());
445 return source->get_internal_name();
446}
447
448
449/********************************************************************
450 * gnc_quote_source_set_fq_installed
451 *
452 * Update gnucash internal tables on what Finance::Quote sources are
453 * installed.
454 ********************************************************************/
455void
456gnc_quote_source_set_fq_installed (const char* version_string,
457 const std::vector<std::string>& sources_list)
458{
459 ENTER(" ");
460
461 if (sources_list.empty())
462 return;
463
464 if (version_string)
465 fq_version = version_string;
466 else
467 fq_version.clear();
468
469 for (const auto& source_name_str : sources_list)
470 {
471 auto source_name = source_name_str.c_str();
472 auto source = gnc_quote_source_lookup_by_internal(source_name);
473
474 if (source)
475 {
476 DEBUG("Found source %s: %s", source_name, source->get_user_name());
477 source->set_supported (true);
478 continue;
479 }
480
481 gnc_quote_source_add_new(source_name, TRUE);
482 }
483 LEAVE(" ");
484}
485
486/********************************************************************
487 * QoF Helpers
488 ********************************************************************/
489
490void
491gnc_commodity_begin_edit (gnc_commodity *cm)
492{
493 qof_begin_edit(&cm->inst);
494}
495
496static void commit_err (QofInstance *inst, QofBackendError errcode)
497{
498 PERR ("Failed to commit: %d", errcode);
499 gnc_engine_signal_commit_error( errcode );
500}
501
502static void noop (QofInstance *inst) {}
503
504static void
505comm_free(QofInstance* inst)
506{
507 commodity_free( GNC_COMMODITY(inst) );
508}
509
510void
511gnc_commodity_commit_edit (gnc_commodity *cm)
512{
513 if (!qof_commit_edit (QOF_INSTANCE(cm))) return;
514 qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free);
515}
516
517/********************************************************************
518 * gnc_commodity_new
519 ********************************************************************/
520
521static void
522mark_commodity_dirty (gnc_commodity *cm)
523{
524 qof_instance_set_dirty(&cm->inst);
525 qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, nullptr);
526}
527
528static void
529reset_printname(gnc_commodityPrivate *priv)
530{
531 g_free(priv->printname);
532 priv->printname = g_strdup_printf("%s (%s)",
533 priv->mnemonic ? priv->mnemonic : "",
534 priv->fullname ? priv->fullname : "");
535}
536
537static void
538reset_unique_name(gnc_commodityPrivate *priv)
539{
540 gnc_commodity_namespace *ns;
541
542 g_free(priv->unique_name);
543 ns = priv->name_space;
544 priv->unique_name = g_strdup_printf("%s::%s",
545 ns ? ns->name : "",
546 priv->mnemonic ? priv->mnemonic : "");
547}
548
549/* GObject Initialization */
550G_DEFINE_TYPE_WITH_PRIVATE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE)
551
552static void
553gnc_commodity_init(gnc_commodity* com)
554{
556
557 priv = GET_PRIVATE(com);
558
559 priv->name_space = nullptr;
560 priv->fullname = CACHE_INSERT("");
561 priv->mnemonic = CACHE_INSERT("");
562 priv->cusip = CACHE_INSERT("");
563 priv->fraction = 10000;
564 priv->quote_flag = 0;
565 priv->quote_source = nullptr;
566 priv->quote_tz = CACHE_INSERT("");
567
568 reset_printname(priv);
569 reset_unique_name(priv);
570}
571
572static void
573gnc_commodity_dispose(GObject *comp)
574{
575 G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp);
576}
577
578static void
579gnc_commodity_finalize(GObject* comp)
580{
581 G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp);
582}
583/* Note that g_value_set_object() refs the object, as does
584 * g_object_get(). But g_object_get() only unrefs once when it disgorges
585 * the object, leaving an unbalanced ref, which leaks. So instead of
586 * using g_value_set_object(), use g_value_take_object() which doesn't
587 * ref the object when used in get_property().
588 */
589static void
590gnc_commodity_get_property (GObject *object,
591 guint prop_id,
592 GValue *value,
593 GParamSpec *pspec)
594{
595 gnc_commodity *commodity;
597
598 g_return_if_fail(GNC_IS_COMMODITY(object));
599
600 commodity = GNC_COMMODITY(object);
601 priv = GET_PRIVATE(commodity);
602 switch (prop_id)
603 {
604 case PROP_NAMESPACE:
605 g_value_take_object(value, priv->name_space);
606 break;
607 case PROP_FULL_NAME:
608 g_value_set_string(value, priv->fullname);
609 break;
610 case PROP_MNEMONIC:
611 g_value_set_string(value, priv->mnemonic);
612 break;
613 case PROP_PRINTNAME:
614 g_value_set_string(value, priv->printname);
615 break;
616 case PROP_CUSIP:
617 g_value_set_string(value, priv->cusip);
618 break;
619 case PROP_FRACTION:
620 g_value_set_int(value, priv->fraction);
621 break;
622 case PROP_UNIQUE_NAME:
623 g_value_set_string(value, priv->unique_name);
624 break;
625 case PROP_QUOTE_FLAG:
626 g_value_set_boolean(value, priv->quote_flag);
627 break;
628 case PROP_QUOTE_SOURCE:
629 g_value_set_pointer(value, priv->quote_source);
630 break;
631 case PROP_QUOTE_TZ:
632 g_value_set_string(value, priv->quote_tz);
633 break;
634 default:
635 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
636 break;
637 }
638}
639
640static void
641gnc_commodity_set_property (GObject *object,
642 guint prop_id,
643 const GValue *value,
644 GParamSpec *pspec)
645{
646 gnc_commodity *commodity;
647
648 g_return_if_fail(GNC_IS_COMMODITY(object));
649
650 commodity = GNC_COMMODITY(object);
651 g_assert (qof_instance_get_editlevel(commodity));
652
653 switch (prop_id)
654 {
655 case PROP_NAMESPACE:
656 gnc_commodity_set_namespace(commodity, static_cast<const char*>(g_value_get_object(value)));
657 break;
658 case PROP_FULL_NAME:
659 gnc_commodity_set_fullname(commodity, g_value_get_string(value));
660 break;
661 case PROP_MNEMONIC:
662 gnc_commodity_set_mnemonic(commodity, g_value_get_string(value));
663 break;
664 case PROP_CUSIP:
665 gnc_commodity_set_cusip(commodity, g_value_get_string(value));
666 break;
667 case PROP_FRACTION:
668 gnc_commodity_set_fraction(commodity, g_value_get_int(value));
669 break;
670 case PROP_QUOTE_FLAG:
671 gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value));
672 break;
673 case PROP_QUOTE_SOURCE:
674 gnc_commodity_set_quote_source(commodity, static_cast<gnc_quote_source*>(g_value_get_pointer(value)));
675 break;
676 case PROP_QUOTE_TZ:
677 gnc_commodity_set_quote_tz(commodity, g_value_get_string(value));
678 break;
679 default:
680 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
681 break;
682 }
683}
684static void
685gnc_commodity_class_init(struct _GncCommodityClass* klass)
686{
687 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
688
689 gobject_class->dispose = gnc_commodity_dispose;
690 gobject_class->finalize = gnc_commodity_finalize;
691 gobject_class->set_property = gnc_commodity_set_property;
692 gobject_class->get_property = gnc_commodity_get_property;
693
694 g_object_class_install_property(gobject_class,
695 PROP_NAMESPACE,
696 g_param_spec_object ("namespace",
697 "Namespace",
698 "The namespace field denotes the "
699 "namespace for this commodity, either "
700 "a currency or symbol from a quote source.",
701 GNC_TYPE_COMMODITY_NAMESPACE,
702 G_PARAM_READWRITE));
703 g_object_class_install_property(gobject_class,
704 PROP_FULL_NAME,
705 g_param_spec_string ("fullname",
706 "Full Commodity Name",
707 "The fullname is the official full name of"
708 "the currency.",
709 nullptr,
710 G_PARAM_READWRITE));
711 g_object_class_install_property(gobject_class,
712 PROP_MNEMONIC,
713 g_param_spec_string ("mnemonic",
714 "Commodity Mnemonic",
715 "The mnemonic is the official abbreviated"
716 "designation for the currency.",
717 nullptr,
718 G_PARAM_READWRITE));
719 g_object_class_install_property(gobject_class,
720 PROP_PRINTNAME,
721 g_param_spec_string ("printname",
722 "Commodity Print Name",
723 "Printable form of the commodity name.",
724 nullptr,
725 G_PARAM_READABLE));
726 g_object_class_install_property(gobject_class,
727 PROP_CUSIP,
728 g_param_spec_string ("cusip",
729 "Commodity CUSIP Code",
730 "?????",
731 nullptr,
732 G_PARAM_READWRITE));
733 g_object_class_install_property(gobject_class,
734 PROP_FRACTION,
735 g_param_spec_int ("fraction",
736 "Fraction",
737 "The fraction is the number of sub-units that "
738 "the basic commodity can be divided into.",
739 1,
741 1,
742 G_PARAM_READWRITE));
743 g_object_class_install_property(gobject_class,
744 PROP_UNIQUE_NAME,
745 g_param_spec_string ("unique-name",
746 "Commodity Unique Name",
747 "Unique form of the commodity name which combines "
748 "the namespace name and the commodity name.",
749 nullptr,
750 G_PARAM_READABLE));
751 g_object_class_install_property(gobject_class,
752 PROP_QUOTE_FLAG,
753 g_param_spec_boolean ("quote_flag",
754 "Quote Flag",
755 "TRUE if prices are to be downloaded for this "
756 "commodity from a quote source.",
757 FALSE,
758 G_PARAM_READWRITE));
759 g_object_class_install_property(gobject_class,
760 PROP_QUOTE_SOURCE,
761 g_param_spec_pointer("quote-source",
762 "Quote Source",
763 "The quote source from which prices are downloaded.",
764 G_PARAM_READWRITE));
765 g_object_class_install_property(gobject_class,
766 PROP_QUOTE_TZ,
767 g_param_spec_string ("quote-tz",
768 "Commodity Quote Timezone",
769 "?????",
770 nullptr,
771 G_PARAM_READWRITE));
772}
773
774gnc_commodity *
775gnc_commodity_new(QofBook *book, const char * fullname,
776 const char * name_space, const char * mnemonic,
777 const char * cusip, int fraction)
778{
779 auto retval = GNC_COMMODITY(g_object_new(GNC_TYPE_COMMODITY, nullptr));
780
781 qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book);
782 gnc_commodity_begin_edit(retval);
783
784 if ( name_space != nullptr )
785 {
786 /* Prevent setting anything except template in namespace template. */
787 if (g_strcmp0 (name_space, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
788 g_strcmp0 (mnemonic, "template") != 0)
789 {
790 PWARN("Converting commodity %s from namespace template to "
791 "namespace User", mnemonic);
792 name_space = "User";
793 }
794 gnc_commodity_set_namespace(retval, name_space);
795 if (gnc_commodity_namespace_is_iso(name_space))
796 {
799 }
800 }
801 gnc_commodity_set_fullname(retval, fullname);
802 gnc_commodity_set_mnemonic(retval, mnemonic);
803 gnc_commodity_set_cusip(retval, cusip);
804 gnc_commodity_set_fraction(retval, fraction);
805 mark_commodity_dirty (retval);
806 gnc_commodity_commit_edit(retval);
807
808 qof_event_gen (&retval->inst, QOF_EVENT_CREATE, nullptr);
809
810 return retval;
811}
812
813
814/********************************************************************
815 * gnc_commodity_destroy
816 ********************************************************************/
817
818static void
819commodity_free(gnc_commodity * cm)
820{
821 QofBook *book;
822 gnc_commodity_table *table;
824
825 if (!cm) return;
826
827 book = qof_instance_get_book(&cm->inst);
830 priv = GET_PRIVATE(cm);
831
832 qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, nullptr);
833
834 /* Set at creation */
835 CACHE_REMOVE (priv->fullname);
836 CACHE_REMOVE (priv->cusip);
837 CACHE_REMOVE (priv->mnemonic);
838 CACHE_REMOVE (priv->quote_tz);
839 priv->name_space = nullptr;
840
841 /* Set through accessor functions */
842 priv->quote_source = nullptr;
843
844 /* Automatically generated */
845 g_free(priv->printname);
846 priv->printname = nullptr;
847
848 g_free(priv->unique_name);
849 priv->unique_name = nullptr;
850
851#ifdef ACCOUNTS_CLEANED_UP
852 /* Account objects are not actually cleaned up when a book is closed (in fact
853 * a memory leak), but commodities are, so in currently this warning gets hit
854 * quite frequently. Disable the check until cleaning up of accounts objects
855 * on close is implemented. */
856 if (priv->usage_count != 0)
857 {
858 PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm,
859 priv->usage_count);
860 }
861#endif
862
863 /* qof_instance_release (&cm->inst); */
864 g_object_unref(cm);
865}
866
867void
868gnc_commodity_destroy(gnc_commodity * cm)
869{
870 gnc_commodity_begin_edit(cm);
871 qof_instance_set_destroying(cm, TRUE);
872 gnc_commodity_commit_edit(cm);
873}
874
875void
876gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src)
877{
878 gnc_commodityPrivate* src_priv = GET_PRIVATE(src);
879 gnc_commodityPrivate* dest_priv = GET_PRIVATE(dest);
880
881 gnc_commodity_set_fullname (dest, src_priv->fullname);
882 gnc_commodity_set_mnemonic (dest, src_priv->mnemonic);
883 dest_priv->name_space = src_priv->name_space;
884 gnc_commodity_set_fraction (dest, src_priv->fraction);
885 gnc_commodity_set_cusip (dest, src_priv->cusip);
886 gnc_commodity_set_quote_flag (dest, src_priv->quote_flag);
888 gnc_commodity_set_quote_tz (dest, src_priv->quote_tz);
889 qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
890}
891
892gnc_commodity *
893gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
894{
895 gnc_commodityPrivate* src_priv;
896 gnc_commodityPrivate* dest_priv;
897
898 auto dest = GNC_COMMODITY (g_object_new(GNC_TYPE_COMMODITY, nullptr));
899 qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book);
900 src_priv = GET_PRIVATE(src);
901 dest_priv = GET_PRIVATE(dest);
902
903 dest_priv->fullname = CACHE_INSERT(src_priv->fullname);
904 dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic);
905 dest_priv->cusip = CACHE_INSERT(src_priv->cusip);
906 dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz);
907
908 dest_priv->name_space = src_priv->name_space;
909
910 dest_priv->fraction = src_priv->fraction;
911 dest_priv->quote_flag = src_priv->quote_flag;
912
914
915 qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
916
917 reset_printname(dest_priv);
918 reset_unique_name(dest_priv);
919
920 return dest;
921}
922
923/********************************************************************
924 * gnc_commodity_get_mnemonic
925 ********************************************************************/
926
927const char *
928gnc_commodity_get_mnemonic(const gnc_commodity * cm)
929{
930 if (!cm) return nullptr;
931 return GET_PRIVATE(cm)->mnemonic;
932}
933
934/********************************************************************
935 * gnc_commodity_get_printname
936 ********************************************************************/
937
938const char *
939gnc_commodity_get_printname(const gnc_commodity * cm)
940{
941 if (!cm) return nullptr;
942 return GET_PRIVATE(cm)->printname;
943}
944
945
946/********************************************************************
947 * gnc_commodity_get_namespace
948 ********************************************************************/
949
950const char *
951gnc_commodity_get_namespace(const gnc_commodity * cm)
952{
953 if (!cm) return nullptr;
954 return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
955}
956
957gnc_commodity_namespace *
958gnc_commodity_get_namespace_ds(const gnc_commodity * cm)
959{
960 if (!cm) return nullptr;
961 return GET_PRIVATE(cm)->name_space;
962}
963
964/********************************************************************
965 * gnc_commodity_get_fullname
966 ********************************************************************/
967
968const char *
969gnc_commodity_get_fullname(const gnc_commodity * cm)
970{
971 if (!cm) return nullptr;
972 return GET_PRIVATE(cm)->fullname;
973}
974
975
976/********************************************************************
977 * gnc_commodity_get_unique_name
978 ********************************************************************/
979
980const char *
981gnc_commodity_get_unique_name(const gnc_commodity * cm)
982{
983 if (!cm) return nullptr;
984 return GET_PRIVATE(cm)->unique_name;
985}
986
987
988/********************************************************************
989 * gnc_commodity_get_cusip
990 ********************************************************************/
991
992const char *
993gnc_commodity_get_cusip(const gnc_commodity * cm)
994{
995 if (!cm) return nullptr;
996 return GET_PRIVATE(cm)->cusip;
997}
998
999/********************************************************************
1000 * gnc_commodity_get_fraction
1001 ********************************************************************/
1002
1003int
1004gnc_commodity_get_fraction(const gnc_commodity * cm)
1005{
1006 if (!cm) return 0;
1007 return GET_PRIVATE(cm)->fraction;
1008}
1009
1010/********************************************************************
1011 * gnc_commodity_get_auto_quote_control_flag
1012 ********************************************************************/
1013
1014gboolean
1015gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
1016{
1017 if (!cm) return FALSE;
1018 auto str{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE (cm), {"auto_quote_control"})};
1019 return !str || g_strcmp0 (*str, "false");
1020}
1021
1022/********************************************************************
1023 * gnc_commodity_get_quote_flag
1024 ********************************************************************/
1025
1026gboolean
1027gnc_commodity_get_quote_flag(const gnc_commodity *cm)
1028{
1029 if (!cm) return FALSE;
1030 return (GET_PRIVATE(cm)->quote_flag);
1031}
1032
1033/********************************************************************
1034 * gnc_commodity_get_quote_source
1035 ********************************************************************/
1036
1037gnc_quote_source*
1038gnc_commodity_get_quote_source(const gnc_commodity *cm)
1039{
1041
1042 if (!cm) return nullptr;
1043 priv = GET_PRIVATE(cm);
1044 if (!priv->quote_source && gnc_commodity_is_iso(cm))
1045 return &currency_quote_sources.front();
1046 return priv->quote_source;
1047}
1048
1049gnc_quote_source*
1050gnc_commodity_get_default_quote_source(const gnc_commodity *cm)
1051{
1052 if (cm && gnc_commodity_is_iso(cm))
1053 return &currency_quote_sources.front();
1054 /* Should make this a user option at some point. */
1055 return gnc_quote_source_lookup_by_internal("alphavantage");
1056}
1057
1058/********************************************************************
1059 * gnc_commodity_get_quote_tz
1060 ********************************************************************/
1061
1062const char*
1063gnc_commodity_get_quote_tz(const gnc_commodity *cm)
1064{
1065 if (!cm) return nullptr;
1066 return GET_PRIVATE(cm)->quote_tz;
1067}
1068
1069/********************************************************************
1070 * gnc_commodity_get_user_symbol
1071 ********************************************************************/
1072const char*
1073gnc_commodity_get_user_symbol(const gnc_commodity *cm)
1074{
1075 g_return_val_if_fail (GNC_IS_COMMODITY (cm), nullptr);
1076
1077 auto sym{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(cm), {"user_symbol"})};
1078 return sym ? *sym : nullptr;
1079}
1080
1081/********************************************************************
1082 * gnc_commodity_get_default_symbol
1083 *******************************************************************/
1084const char*
1085gnc_commodity_get_default_symbol(const gnc_commodity *cm)
1086{
1087 if (!cm) return nullptr;
1088 return GET_PRIVATE(cm)->default_symbol;
1089}
1090
1091/********************************************************************
1092 * gnc_commodity_get_nice_symbol
1093 *******************************************************************/
1094const char*
1095gnc_commodity_get_nice_symbol (const gnc_commodity *cm)
1096{
1097 const char *nice_symbol;
1098 struct lconv *lc;
1099 if (!cm) return nullptr;
1100
1101 nice_symbol = gnc_commodity_get_user_symbol(cm);
1102 if (nice_symbol && *nice_symbol)
1103 return nice_symbol;
1104
1105 lc = gnc_localeconv();
1106 nice_symbol = lc->currency_symbol;
1107 if (!g_strcmp0(gnc_commodity_get_mnemonic(cm), lc->int_curr_symbol))
1108 return nice_symbol;
1109
1110 nice_symbol = gnc_commodity_get_default_symbol(cm);
1111 if (nice_symbol && *nice_symbol)
1112 return nice_symbol;
1113
1114 return gnc_commodity_get_mnemonic(cm);
1115}
1116
1117/********************************************************************
1118 * gnc_commodity_set_mnemonic
1119 ********************************************************************/
1120
1121void
1122gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic)
1123{
1125
1126 if (!cm) return;
1127 priv = GET_PRIVATE(cm);
1128 if (priv->mnemonic == mnemonic) return;
1129
1130 gnc_commodity_begin_edit(cm);
1131 CACHE_REMOVE (priv->mnemonic);
1132 priv->mnemonic = CACHE_INSERT(mnemonic);
1133
1134 mark_commodity_dirty (cm);
1135 reset_printname(priv);
1136 reset_unique_name(priv);
1137 gnc_commodity_commit_edit(cm);
1138}
1139
1140/********************************************************************
1141 * gnc_commodity_set_namespace
1142 ********************************************************************/
1143
1144void
1145gnc_commodity_set_namespace(gnc_commodity * cm, const char * name_space)
1146{
1147 QofBook *book;
1148 gnc_commodity_table *table;
1149 gnc_commodity_namespace *nsp;
1151
1152 if (!cm) return;
1153 priv = GET_PRIVATE(cm);
1154 book = qof_instance_get_book (&cm->inst);
1156 nsp = gnc_commodity_table_add_namespace(table, name_space, book);
1157 if (priv->name_space == nsp)
1158 return;
1159
1160 gnc_commodity_begin_edit(cm);
1161 priv->name_space = nsp;
1162 if (nsp->iso4217)
1163 priv->quote_source = gnc_quote_source_lookup_by_internal("currency");
1164 mark_commodity_dirty(cm);
1165 reset_printname(priv);
1166 reset_unique_name(priv);
1167 gnc_commodity_commit_edit(cm);
1168}
1169
1170/********************************************************************
1171 * gnc_commodity_set_fullname
1172 ********************************************************************/
1173
1174void
1175gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname)
1176{
1178
1179 if (!cm) return;
1180 priv = GET_PRIVATE(cm);
1181 if (priv->fullname == fullname) return;
1182
1183 CACHE_REMOVE (priv->fullname);
1184 priv->fullname = CACHE_INSERT (fullname);
1185
1186 gnc_commodity_begin_edit(cm);
1187 mark_commodity_dirty(cm);
1188 reset_printname(priv);
1189 gnc_commodity_commit_edit(cm);
1190}
1191
1192/********************************************************************
1193 * gnc_commodity_set_cusip
1194 ********************************************************************/
1195
1196void
1197gnc_commodity_set_cusip(gnc_commodity * cm,
1198 const char * cusip)
1199{
1201
1202 if (!cm) return;
1203
1204 priv = GET_PRIVATE(cm);
1205 if (priv->cusip == cusip) return;
1206
1207 gnc_commodity_begin_edit(cm);
1208 CACHE_REMOVE (priv->cusip);
1209 priv->cusip = CACHE_INSERT (cusip);
1210 mark_commodity_dirty(cm);
1211 gnc_commodity_commit_edit(cm);
1212}
1213
1214/********************************************************************
1215 * gnc_commodity_set_fraction
1216 ********************************************************************/
1217
1218void
1219gnc_commodity_set_fraction(gnc_commodity * cm, int fraction)
1220{
1221 if (!cm) return;
1222 gnc_commodity_begin_edit(cm);
1223 GET_PRIVATE(cm)->fraction = fraction;
1224 mark_commodity_dirty(cm);
1225 gnc_commodity_commit_edit(cm);
1226}
1227
1228/********************************************************************
1229 * gnc_commodity_set_auto_quote_control_flag
1230 ********************************************************************/
1231
1232void
1233gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
1234 const gboolean flag)
1235{
1236 ENTER ("(cm=%p, flag=%d)", cm, flag);
1237
1238 if (!cm)
1239 {
1240 LEAVE("");
1241 return;
1242 }
1243 gnc_commodity_begin_edit(cm);
1244 auto val = flag ? std::nullopt : std::make_optional<const char*>(g_strdup("false"));
1245 qof_instance_set_path_kvp<const char*> (QOF_INSTANCE (cm), val, {"auto_quote_control"});
1246 mark_commodity_dirty(cm);
1247 gnc_commodity_commit_edit(cm);
1248 LEAVE("");
1249}
1250
1251/********************************************************************
1252 * gnc_commodity_user_set_quote_flag
1253 ********************************************************************/
1254
1255void
1256gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1257{
1259
1260 ENTER ("(cm=%p, flag=%d)", cm, flag);
1261
1262 if (!cm)
1263 {
1264 LEAVE("");
1265 return;
1266 }
1267
1268 priv = GET_PRIVATE(cm);
1269 gnc_commodity_begin_edit(cm);
1271 if (gnc_commodity_is_iso(cm))
1272 {
1273 /* For currencies, disable auto quote control if the quote flag is being
1274 * changed from its default value and enable it if the quote flag is being
1275 * reset to its default value. The defaults for the quote flag are
1276 * disabled if no accounts are using the currency, and true otherwise.
1277 * Thus enable auto quote control if flag is FALSE and there are not any
1278 * accounts using this currency OR flag is TRUE and there are accounts
1279 * using this currency; otherwise disable auto quote control */
1280 gnc_commodity_set_auto_quote_control_flag(cm,
1281 (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0)));
1282 }
1283 gnc_commodity_commit_edit(cm);
1284 LEAVE("");
1285}
1286
1287/********************************************************************
1288 * gnc_commodity_set_quote_flag
1289 ********************************************************************/
1290
1291void
1292gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1293{
1294 ENTER ("(cm=%p, flag=%d)", cm, flag);
1295
1296 if (!cm) return;
1297 gnc_commodity_begin_edit(cm);
1298 GET_PRIVATE(cm)->quote_flag = flag;
1299 mark_commodity_dirty(cm);
1300 gnc_commodity_commit_edit(cm);
1301 LEAVE(" ");
1302}
1303
1304/********************************************************************
1305 * gnc_commodity_set_quote_source
1306 ********************************************************************/
1307
1308void
1309gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
1310{
1311 ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->get_internal_name() : "unknown");
1312
1313 if (!cm) return;
1314 gnc_commodity_begin_edit(cm);
1315 GET_PRIVATE(cm)->quote_source = src;
1316 mark_commodity_dirty(cm);
1317 gnc_commodity_commit_edit(cm);
1318 LEAVE(" ");
1319}
1320
1321/********************************************************************
1322 * gnc_commodity_set_quote_tz
1323 ********************************************************************/
1324
1325void
1326gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
1327{
1329
1330 if (!cm) return;
1331
1332 ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)");
1333
1334 priv = GET_PRIVATE(cm);
1335
1336 if (tz == priv->quote_tz)
1337 {
1338 LEAVE("Already correct TZ");
1339 return;
1340 }
1341
1342 gnc_commodity_begin_edit(cm);
1343 CACHE_REMOVE (priv->quote_tz);
1344 priv->quote_tz = CACHE_INSERT (tz);
1345 mark_commodity_dirty(cm);
1346 gnc_commodity_commit_edit(cm);
1347 LEAVE(" ");
1348}
1349
1350/********************************************************************
1351 * gnc_commodity_set_user_symbol
1352 ********************************************************************/
1353
1354void
1355gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
1356{
1357 struct lconv *lc;
1358
1359 if (!cm) return;
1360
1361 ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
1362
1363 lc = gnc_localeconv();
1364 if (!user_symbol || !*user_symbol)
1365 user_symbol = nullptr;
1366 else if (!g_strcmp0(lc->int_curr_symbol, gnc_commodity_get_mnemonic(cm)) &&
1367 !g_strcmp0(lc->currency_symbol, user_symbol))
1368 /* if the user gives the ISO symbol for the locale currency or the
1369 * default symbol, actually remove the user symbol */
1370 user_symbol = nullptr;
1371 else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
1372 user_symbol = nullptr;
1373
1374 gnc_commodity_begin_edit (cm);
1375
1376 auto val = user_symbol ? std::make_optional<const char*>(g_strdup(user_symbol)) : std::nullopt;
1377 qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(cm), val, {"user_symbol"});
1378
1379 mark_commodity_dirty(cm);
1380 gnc_commodity_commit_edit(cm);
1381
1382 LEAVE(" ");
1383}
1384
1385/********************************************************************
1386 * gnc_commodity_set_default_symbol
1387 * Not made visible in gnc-commodity.h, it is only called from
1388 * iso-4217-currencies.c at startup.
1389 ********************************************************************/
1390void
1391gnc_commodity_set_default_symbol(gnc_commodity * cm,
1392 const char * default_symbol)
1393{
1394 GET_PRIVATE(cm)->default_symbol = default_symbol;
1395}
1396
1397/********************************************************************
1398 * gnc_commodity_increment_usage_count
1399 ********************************************************************/
1400
1401void
1403{
1405
1406 ENTER("(cm=%p)", cm);
1407
1408 if (!cm)
1409 {
1410 LEAVE("");
1411 return;
1412 }
1413
1414 priv = GET_PRIVATE(cm);
1415
1416 if ((priv->usage_count == 0) && !priv->quote_flag
1417 && gnc_commodity_get_auto_quote_control_flag(cm)
1418 && gnc_commodity_is_iso(cm))
1419 {
1420 /* compatibility hack - Gnucash 1.8 gets currency quotes when a
1421 non-default currency is assigned to an account. */
1422 gnc_commodity_begin_edit(cm);
1425 gnc_commodity_get_default_quote_source(cm));
1426 gnc_commodity_commit_edit(cm);
1427 }
1428 priv->usage_count++;
1429 LEAVE("(usage_count=%d)", priv->usage_count);
1430}
1431
1432/********************************************************************
1433 * gnc_commodity_decrement_usage_count
1434 ********************************************************************/
1435
1436void
1438{
1440
1441 ENTER("(cm=%p)", cm);
1442
1443 if (!cm)
1444 {
1445 LEAVE("");
1446 return;
1447 }
1448
1449 priv = GET_PRIVATE(cm);
1450
1451 if (priv->usage_count == 0)
1452 {
1453 PWARN("usage_count already zero");
1454 LEAVE("");
1455 return;
1456 }
1457
1458 priv->usage_count--;
1459 if ((priv->usage_count == 0) && priv->quote_flag
1460 && gnc_commodity_get_auto_quote_control_flag(cm)
1461 && gnc_commodity_is_iso(cm))
1462 {
1463 /* if this is a currency with auto quote control enabled and no more
1464 * accounts reference this currency, disable quote retrieval */
1466 }
1467 LEAVE("(usage_count=%d)", priv->usage_count);
1468}
1469
1470/********************************************************************\
1471\********************************************************************/
1472
1473
1474/********************************************************************
1475 * gnc_commodity_equiv
1476 * are two commodities the same?
1477 ********************************************************************/
1478
1479gboolean
1480gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b)
1481{
1482 gnc_commodityPrivate* priv_a;
1483 gnc_commodityPrivate* priv_b;
1484
1485 if (a == b) return TRUE;
1486 if (!a || !b) return FALSE;
1487
1488 priv_a = GET_PRIVATE(a);
1489 priv_b = GET_PRIVATE(b);
1490 if (priv_a->name_space != priv_b->name_space) return FALSE;
1491 if (g_strcmp0(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE;
1492
1493 return TRUE;
1494}
1495
1496gboolean
1497gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b)
1498{
1499 return gnc_commodity_compare(a, b) == 0;
1500}
1501
1502int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b)
1503{
1504 if (a == b) return 0;
1505 if (a && !b) return 1;
1506 if (b && !a) return -1;
1507 if (auto rv = g_strcmp0 (gnc_commodity_get_unique_name (a), gnc_commodity_get_unique_name (b)))
1508 return rv;
1509 return qof_instance_guid_compare(a, b);
1510}
1511
1512// Used as a callback to g_list_find_custom, it should return 0
1513// when the commodities match.
1514int gnc_commodity_compare_void(const void * a, const void * b)
1515{
1516 return gnc_commodity_compare(GNC_COMMODITY (a), GNC_COMMODITY (b));
1517}
1518
1519/************************************************************
1520 * Namespace functions *
1521 ************************************************************/
1522const char *
1523gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns)
1524{
1525 if (ns == nullptr)
1526 return nullptr;
1527 return ns->name;
1528}
1529
1530const char *
1531gnc_commodity_namespace_get_gui_name (const gnc_commodity_namespace *ns)
1532{
1533 if (ns == nullptr)
1534 return nullptr;
1535 if (g_strcmp0 (ns->name, GNC_COMMODITY_NS_CURRENCY) == 0)
1536 return GNC_COMMODITY_NS_ISO_GUI;
1537 return ns->name;
1538}
1539
1540GList *
1541gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
1542{
1543 if (!name_space)
1544 return nullptr;
1545
1546 return g_list_copy (name_space->cm_list);
1547}
1548
1549gboolean
1550gnc_commodity_namespace_is_iso(const char *name_space)
1551{
1552 return ((g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0) ||
1553 (g_strcmp0(name_space, GNC_COMMODITY_NS_CURRENCY) == 0));
1554}
1555
1556static const gchar *
1557gnc_commodity_table_map_namespace(const char * name_space)
1558{
1559 if (g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0)
1560 return GNC_COMMODITY_NS_CURRENCY;
1561 return name_space;
1562}
1563
1564/********************************************************************
1565 * gnc_commodity_table_new
1566 * make a new commodity table
1567 ********************************************************************/
1568
1569gnc_commodity_table *
1571{
1572 gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1);
1573 retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal);
1574 retval->ns_list = nullptr;
1575 return retval;
1576}
1577
1578/********************************************************************
1579 * book anchor functions
1580 ********************************************************************/
1581
1582gnc_commodity_table *
1584{
1585 if (!book) return nullptr;
1586 return static_cast<gnc_commodity_table*>(qof_book_get_data (book, GNC_COMMODITY_TABLE));
1587}
1588
1589gnc_commodity *
1590gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book)
1591{
1592 gnc_commodity *twin;
1593 const char * ucom;
1594 gnc_commodity_table * comtbl;
1595
1596 if (!from) return nullptr;
1597 comtbl = gnc_commodity_table_get_table (book);
1598 if (!comtbl) return nullptr;
1599
1600 ucom = gnc_commodity_get_unique_name (from);
1601 twin = gnc_commodity_table_lookup_unique (comtbl, ucom);
1602 if (!twin)
1603 {
1604 twin = gnc_commodity_clone (from, book);
1605 twin = gnc_commodity_table_insert (comtbl, twin);
1606 }
1607 return twin;
1608}
1609
1610/********************************************************************
1611 * gnc_commodity_table_get_size
1612 * get the size of the commodity table
1613 ********************************************************************/
1614
1615static void
1616count_coms(gpointer key, gpointer value, gpointer user_data)
1617{
1618 GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table;
1619 guint *count = (guint*)user_data;
1620
1621 if (g_strcmp0((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0)
1622 {
1623 /* don't count default commodities */
1624 return;
1625 }
1626
1627 if (!value) return;
1628
1629 *count += g_hash_table_size(tbl);
1630}
1631
1632guint
1633gnc_commodity_table_get_size(const gnc_commodity_table* tbl)
1634{
1635 guint count = 0;
1636 g_return_val_if_fail(tbl, 0);
1637 g_return_val_if_fail(tbl->ns_table, 0);
1638
1639 g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count);
1640
1641 return count;
1642}
1643
1644/********************************************************************
1645 * gnc_commodity_table_lookup
1646 * locate a commodity by namespace and mnemonic.
1647 ********************************************************************/
1648
1649gnc_commodity *
1650gnc_commodity_table_lookup(const gnc_commodity_table * table,
1651 const char * name_space, const char * mnemonic)
1652{
1653 gnc_commodity_namespace * nsp = nullptr;
1654
1655 if (!table || !name_space || !mnemonic) return nullptr;
1656
1657 nsp = gnc_commodity_table_find_namespace(table, name_space);
1658
1659 if (nsp)
1660 {
1661 /*
1662 * Backward compatibility support for currencies that have
1663 * recently changed.
1664 */
1665 if (nsp->iso4217)
1666 {
1667 auto it = gnc_new_iso_codes.find (mnemonic);
1668 if (it != gnc_new_iso_codes.end())
1669 mnemonic = it->second.c_str();
1670 }
1671 return GNC_COMMODITY(g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic));
1672 }
1673 else
1674 {
1675 return nullptr;
1676 }
1677}
1678
1679/********************************************************************
1680 * gnc_commodity_table_lookup
1681 * locate a commodity by unique name.
1682 ********************************************************************/
1683
1684gnc_commodity *
1685gnc_commodity_table_lookup_unique(const gnc_commodity_table *table,
1686 const char * unique_name)
1687{
1688 char *name_space;
1689 char *mnemonic;
1690 gnc_commodity *commodity;
1691
1692 if (!table || !unique_name) return nullptr;
1693
1694 name_space = g_strdup (unique_name);
1695 mnemonic = strstr (name_space, "::");
1696 if (!mnemonic)
1697 {
1698 g_free (name_space);
1699 return nullptr;
1700 }
1701
1702 *mnemonic = '\0';
1703 mnemonic += 2;
1704
1705 commodity = gnc_commodity_table_lookup (table, name_space, mnemonic);
1706
1707 g_free (name_space);
1708
1709 return commodity;
1710}
1711
1712/********************************************************************
1713 * gnc_commodity_table_find_full
1714 * locate a commodity by namespace and printable name
1715 ********************************************************************/
1716
1717gnc_commodity *
1718gnc_commodity_table_find_full(const gnc_commodity_table * table,
1719 const char * name_space,
1720 const char * fullname)
1721{
1722 gnc_commodity * retval = nullptr;
1723 GList * all;
1724 GList * iterator;
1725
1726 if (!fullname || (fullname[0] == '\0'))
1727 return nullptr;
1728
1729 all = gnc_commodity_table_get_commodities(table, name_space);
1730
1731 for (iterator = all; iterator; iterator = iterator->next)
1732 {
1733 auto commodity = GNC_COMMODITY (iterator->data);
1734 if (!strcmp(fullname,
1735 gnc_commodity_get_printname(commodity)))
1736 {
1737 retval = commodity;
1738 break;
1739 }
1740 }
1741
1742 g_list_free (all);
1743
1744 return retval;
1745}
1746
1747
1748/********************************************************************
1749 * gnc_commodity_table_insert
1750 * add a commodity to the table.
1751 ********************************************************************/
1752
1753gnc_commodity *
1754gnc_commodity_table_insert(gnc_commodity_table * table,
1755 gnc_commodity * comm)
1756{
1757 gnc_commodity_namespace * nsp = nullptr;
1758 gnc_commodity *c;
1759 const char *ns_name;
1761 QofBook *book;
1762
1763 if (!table) return nullptr;
1764 if (!comm) return nullptr;
1765
1766 priv = GET_PRIVATE(comm);
1767
1768 ENTER ("(table=%p, comm=%p) %s %s", table, comm,
1769 (priv->mnemonic == nullptr ? "(null)" : priv->mnemonic),
1770 (priv->fullname == nullptr ? "(null)" : priv->fullname));
1771 ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1772 c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1773
1774 if (c)
1775 {
1776 if (c == comm)
1777 {
1778 LEAVE("already in table");
1779 return c;
1780 }
1781
1782 /* Backward compatibility support for currencies that have
1783 * recently changed. */
1784 if (priv->name_space->iso4217)
1785 {
1786 auto it = gnc_new_iso_codes.find (priv->mnemonic);
1787 if (it != gnc_new_iso_codes.end())
1788 gnc_commodity_set_mnemonic(comm, it->second.c_str());
1789 }
1790 gnc_commodity_copy (c, comm);
1791 gnc_commodity_destroy (comm);
1792 LEAVE("found at %p", c);
1793 return c;
1794 }
1795
1796 /* Prevent setting anything except template in namespace template. */
1797 if (g_strcmp0 (ns_name, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
1798 g_strcmp0 (priv->mnemonic, "template") != 0)
1799 {
1800 PWARN("Converting commodity %s from namespace template to "
1801 "namespace User", priv->mnemonic);
1802 gnc_commodity_set_namespace (comm, "User");
1803 ns_name = "User";
1804 mark_commodity_dirty (comm);
1805 }
1806
1807 book = qof_instance_get_book (&comm->inst);
1808 nsp = gnc_commodity_table_add_namespace(table, ns_name, book);
1809
1810 PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic,
1811 nsp->cm_table, nsp->name);
1812 g_hash_table_insert(nsp->cm_table,
1813 (gpointer)CACHE_INSERT(priv->mnemonic),
1814 (gpointer)comm);
1815 nsp->cm_list = g_list_append(nsp->cm_list, comm);
1816
1817 qof_event_gen (&comm->inst, QOF_EVENT_ADD, nullptr);
1818 LEAVE ("(table=%p, comm=%p)", table, comm);
1819 return comm;
1820}
1821
1822/********************************************************************
1823 * gnc_commodity_table_remove
1824 * remove a commodity from the table.
1825 ********************************************************************/
1826
1827void
1828gnc_commodity_table_remove(gnc_commodity_table * table,
1829 gnc_commodity * comm)
1830{
1831 gnc_commodity_namespace * nsp;
1832 gnc_commodity *c;
1834 const char *ns_name;
1835
1836 if (!table) return;
1837 if (!comm) return;
1838
1839 priv = GET_PRIVATE(comm);
1840 ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1841 c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1842 if (c != comm) return;
1843
1844 qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, nullptr);
1845
1847 if (!nsp) return;
1848
1849 nsp->cm_list = g_list_remove(nsp->cm_list, comm);
1850 g_hash_table_remove (nsp->cm_table, priv->mnemonic);
1851 /* XXX minor mem leak, should remove the key as well */
1852}
1853
1854/********************************************************************
1855 * gnc_commodity_table_has_namespace
1856 * see if the commodities namespace exists. May have zero commodities.
1857 ********************************************************************/
1858
1859int
1860gnc_commodity_table_has_namespace(const gnc_commodity_table * table,
1861 const char * name_space)
1862{
1863 gnc_commodity_namespace * nsp = nullptr;
1864
1865 if (!table || !name_space)
1866 {
1867 return 0;
1868 }
1869
1870 nsp = gnc_commodity_table_find_namespace(table, name_space);
1871 if (nsp)
1872 {
1873 return 1;
1874 }
1875 else
1876 {
1877 return 0;
1878 }
1879}
1880
1881static void
1882hash_keys_helper(gpointer key, gpointer value, gpointer data)
1883{
1884 auto l = (GList**)data;
1885 *l = g_list_prepend(*l, key);
1886}
1887
1888static GList *
1889g_hash_table_keys(GHashTable * table)
1890{
1891 GList * l = nullptr;
1892 g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l);
1893 return l;
1894}
1895
1896static void
1897hash_values_helper(gpointer key, gpointer value, gpointer data)
1898{
1899 auto l = (GList**)data;
1900 *l = g_list_prepend(*l, value);
1901}
1902
1903static GList *
1904g_hash_table_values(GHashTable * table)
1905{
1906 GList * l = nullptr;
1907 g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l);
1908 return l;
1909}
1910
1911/********************************************************************
1912 * gnc_commodity_table_get_namespaces
1913 * see if any commodities in the namespace exist
1914 ********************************************************************/
1915
1916GList *
1917gnc_commodity_table_get_namespaces(const gnc_commodity_table * table)
1918{
1919 if (!table)
1920 return nullptr;
1921
1922 return g_hash_table_keys(table->ns_table);
1923}
1924
1925GList *
1927{
1928 if (!table)
1929 return nullptr;
1930
1931 return g_list_copy (table->ns_list);
1932}
1933
1934/* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to
1935 GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is
1936 either of these, the net result is that the iso4217 bit is set only
1937 for GNC_COMMODITY_NS_CURRENCY. This means that gnc_commodity_is_iso is
1938 a subset of gnc_commodity_is_currency. Most callers seem to use
1939 gnc_commodity_is_iso. */
1940gboolean
1941gnc_commodity_is_iso(const gnc_commodity * cm)
1942{
1944
1945 if (!cm) return FALSE;
1946
1947 priv = GET_PRIVATE(cm);
1948 if ( !priv->name_space) return FALSE;
1949 return priv->name_space->iso4217;
1950}
1951
1952gboolean
1953gnc_commodity_is_currency(const gnc_commodity *cm)
1954{
1955 const char *ns_name;
1956 if (!cm) return FALSE;
1957
1958 ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
1959 return (!g_strcmp0(ns_name, GNC_COMMODITY_NS_LEGACY) ||
1960 !g_strcmp0(ns_name, GNC_COMMODITY_NS_CURRENCY));
1961}
1962
1963/********************************************************************
1964 * gnc_commodity_table_get_commodities
1965 * list commodities in a given namespace
1966 ********************************************************************/
1967
1968static CommodityList*
1969commodity_table_get_all_noncurrency_commodities(const gnc_commodity_table* table)
1970{
1971 GList *node = nullptr, *nslist = gnc_commodity_table_get_namespaces(table);
1972 CommodityList *retval = nullptr;
1973 for (node = nslist; node; node=g_list_next(node))
1974 {
1975 gnc_commodity_namespace *ns = nullptr;
1976 if (g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_CURRENCY) == 0
1977 || g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_TEMPLATE) == 0)
1978 continue;
1979 ns = gnc_commodity_table_find_namespace(table, (char*)(node->data));
1980 if (!ns)
1981 continue;
1982 retval = g_list_concat(g_hash_table_values(ns->cm_table), retval);
1983 }
1984 g_list_free(nslist);
1985 return retval;
1986}
1987
1988CommodityList *
1989gnc_commodity_table_get_commodities(const gnc_commodity_table * table,
1990 const char * name_space)
1991{
1992 gnc_commodity_namespace * ns = nullptr;
1993
1994 if (!table)
1995 return nullptr;
1996 if (g_strcmp0(name_space, GNC_COMMODITY_NS_NONISO_GUI) == 0)
1997 return commodity_table_get_all_noncurrency_commodities(table);
1998 ns = gnc_commodity_table_find_namespace(table, name_space);
1999 if (!ns)
2000 return nullptr;
2001
2002 return g_hash_table_values(ns->cm_table);
2003}
2004
2005/********************************************************************
2006 * gnc_commodity_table_get_quotable_commodities
2007 * list commodities in a given namespace that get price quotes
2008 ********************************************************************/
2009
2010static void
2011get_quotables_helper1(gpointer key, gpointer value, gpointer data)
2012{
2013 auto comm = GNC_COMMODITY(value);
2014 gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2015 auto l = static_cast<GList**>(data);
2016
2017 if (!priv->quote_flag || !priv->quote_source || !priv->quote_source->get_supported())
2018 return;
2019 *l = g_list_prepend(*l, value);
2020}
2021
2022static gboolean
2023get_quotables_helper2 (gnc_commodity *comm, gpointer data)
2024{
2025 auto l = static_cast<GList**>(data);
2026 gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2027
2028 if (!priv->quote_flag || priv->quote_source || !priv->quote_source->get_supported())
2029 return TRUE;
2030 *l = g_list_prepend(*l, comm);
2031 return TRUE;
2032}
2033
2034CommodityList *
2036{
2037 gnc_commodity_namespace * ns = nullptr;
2038 const char *name_space;
2039 GList * nslist, * tmp;
2040 GList * l = nullptr;
2041 regex_t pattern;
2042 const char *expression = gnc_prefs_get_namespace_regexp();
2043
2044 ENTER("table=%p, expression=%s", table, expression);
2045 if (!table)
2046 return nullptr;
2047
2048 if (expression && *expression)
2049 {
2050 if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
2051 {
2052 LEAVE("Cannot compile regex");
2053 return nullptr;
2054 }
2055
2057 for (tmp = nslist; tmp; tmp = tmp->next)
2058 {
2059 name_space = static_cast<const char*>(tmp->data);
2060 if (regexec(&pattern, name_space, 0, nullptr, 0) == 0)
2061 {
2062 DEBUG("Running list of %s commodities", name_space);
2063 ns = gnc_commodity_table_find_namespace(table, name_space);
2064 if (ns)
2065 {
2066 g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l);
2067 }
2068 }
2069 }
2070 g_list_free(nslist);
2071 regfree(&pattern);
2072 }
2073 else
2074 {
2075 gnc_commodity_table_foreach_commodity(table, get_quotables_helper2,
2076 (gpointer) &l);
2077 }
2078 LEAVE("list head %p", l);
2079 return l;
2080}
2081
2082/********************************************************************
2083 * gnc_commodity_table_add_namespace
2084 * add an empty namespace if it does not exist
2085 ********************************************************************/
2086
2087/* GObject Initialization */
2088QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE)
2089
2090static void
2091gnc_commodity_namespace_init(gnc_commodity_namespace* ns)
2092{
2093}
2094
2095static void
2096gnc_commodity_namespace_dispose_real (GObject *nsp)
2097{
2098}
2099
2100static void
2101gnc_commodity_namespace_finalize_real(GObject* nsp)
2102{
2103}
2104
2105gnc_commodity_namespace *
2107 const char * name_space,
2108 QofBook *book)
2109{
2110 gnc_commodity_namespace * ns = nullptr;
2111
2112 if (!table) return nullptr;
2113
2114 name_space = gnc_commodity_table_map_namespace(name_space);
2115 ns = gnc_commodity_table_find_namespace(table, name_space);
2116 if (!ns)
2117 {
2118 ns = static_cast<gnc_commodity_namespace*>(g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, nullptr));
2119 ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal);
2120 ns->name = CACHE_INSERT(static_cast<const char*>(name_space));
2121 ns->iso4217 = gnc_commodity_namespace_is_iso(name_space);
2122 qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book);
2123 qof_event_gen (&ns->inst, QOF_EVENT_CREATE, nullptr);
2124
2125 g_hash_table_insert(table->ns_table,
2126 (gpointer) ns->name,
2127 (gpointer) ns);
2128 table->ns_list = g_list_append(table->ns_list, ns);
2129 qof_event_gen (&ns->inst, QOF_EVENT_ADD, nullptr);
2130 }
2131 return ns;
2132}
2133
2134bool
2136 const char * namespace_name,
2137 const char * new_namespace_name)
2138{
2139 if (!table || !namespace_name || !new_namespace_name ||
2140 (g_strcmp0 (namespace_name, new_namespace_name) == 0) ||
2141 (g_strcmp0 (new_namespace_name, GNC_COMMODITY_NS_ISO_GUI) == 0) ||
2142 (g_strcmp0 (new_namespace_name, _(GNC_COMMODITY_NS_ISO_GUI)) == 0) ||
2143 gnc_commodity_table_find_namespace (table, new_namespace_name))
2144 return false;
2145
2146 auto ns = gnc_commodity_table_find_namespace (table, namespace_name);
2147 if (!ns)
2148 return false;
2149
2150 ns->name = CACHE_INSERT(static_cast<const char*>(new_namespace_name));
2151
2152 g_hash_table_insert (table->ns_table,
2153 (gpointer) ns->name,
2154 (gpointer) ns);
2155
2156 g_hash_table_remove (table->ns_table,
2157 (gpointer) namespace_name);
2158
2159 CACHE_REMOVE(namespace_name);
2160
2161 qof_instance_set_dirty (&ns->inst);
2162 qof_event_gen (&ns->inst, QOF_EVENT_MODIFY, nullptr);
2163 return true;
2164}
2165
2166gnc_commodity_namespace *
2167gnc_commodity_table_find_namespace(const gnc_commodity_table * table,
2168 const char * name_space)
2169{
2170 if (!table || !name_space)
2171 return nullptr;
2172
2173 name_space = gnc_commodity_table_map_namespace(name_space);
2174 return static_cast<gnc_commodity_namespace*>(g_hash_table_lookup(table->ns_table, (gpointer)name_space));
2175}
2176
2177
2178gnc_commodity *
2179gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book)
2180{
2181 QofCollection *col;
2182 if (!guid || !book) return nullptr;
2183 col = qof_book_get_collection (book, GNC_ID_COMMODITY);
2184 return (gnc_commodity *) qof_collection_lookup_entity (col, guid);
2185}
2186
2187/********************************************************************
2188 * gnc_commodity_table_delete_namespace
2189 * delete a namespace
2190 ********************************************************************/
2191
2192static int
2193ns_helper(gpointer key, gpointer value, gpointer user_data)
2194{
2195 auto c = GNC_COMMODITY(value);
2197 CACHE_REMOVE(static_cast<char*>(key)); /* key is commodity mnemonic */
2198 return TRUE;
2199}
2200
2201void
2203 const char * name_space)
2204{
2205 gnc_commodity_namespace * ns;
2206
2207 if (!table) return;
2208
2209 ns = gnc_commodity_table_find_namespace(table, name_space);
2210 if (!ns)
2211 return;
2212
2213 qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, nullptr);
2214 g_hash_table_remove(table->ns_table, name_space);
2215 table->ns_list = g_list_remove(table->ns_list, ns);
2216
2217 g_list_free(ns->cm_list);
2218 ns->cm_list = nullptr;
2219
2220 g_hash_table_foreach_remove(ns->cm_table, ns_helper, nullptr);
2221 g_hash_table_destroy(ns->cm_table);
2222 CACHE_REMOVE(ns->name);
2223
2224 qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, nullptr);
2225 /* qof_instance_release(&ns->inst); */
2226 g_object_unref(ns);
2227}
2228
2229/********************************************************************
2230 * gnc_commodity_table_foreach_commodity
2231 * call user-defined function once for every commodity in every
2232 * namespace
2233 ********************************************************************/
2234
2235typedef struct
2236{
2237 gboolean ok;
2238 gboolean (*func)(gnc_commodity *, gpointer);
2239 gpointer user_data;
2240} IterData;
2241
2242static void
2243iter_commodity (gpointer key, gpointer value, gpointer user_data)
2244{
2245 IterData *iter_data = (IterData *) user_data;
2246 gnc_commodity *cm = (gnc_commodity *) value;
2247
2248 if (iter_data->ok)
2249 {
2250 iter_data->ok = (iter_data->func)(cm, iter_data->user_data);
2251 }
2252}
2253
2254static void
2255iter_namespace (gpointer key, gpointer value, gpointer user_data)
2256{
2257 GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table;
2258 g_hash_table_foreach (namespace_hash, iter_commodity, user_data);
2259}
2260
2261gboolean
2262gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl,
2263 gboolean (*f)(gnc_commodity *, gpointer),
2264 gpointer user_data)
2265{
2266 IterData iter_data;
2267
2268 if (!tbl || !f) return FALSE;
2269
2270 iter_data.ok = TRUE;
2271 iter_data.func = f;
2272 iter_data.user_data = user_data;
2273
2274 g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data);
2275
2276 return iter_data.ok;
2277}
2278
2279/********************************************************************
2280 * gnc_commodity_table_destroy
2281 * cleanup and free.
2282 ********************************************************************/
2283
2284void
2285gnc_commodity_table_destroy(gnc_commodity_table * t)
2286{
2287 gnc_commodity_namespace * ns;
2288 GList *item, *next;
2289
2290 if (!t) return;
2291 ENTER ("table=%p", t);
2292
2293 for (item = t->ns_list; item; item = next)
2294 {
2295 next = g_list_next(item);
2296 ns = static_cast<gnc_commodity_namespace*>(item->data);
2298 }
2299
2300 g_list_free(t->ns_list);
2301 t->ns_list = nullptr;
2302 g_hash_table_destroy(t->ns_table);
2303 t->ns_table = nullptr;
2304 LEAVE ("table=%p", t);
2305 g_free(t);
2306}
2307
2308/* =========================================================== */
2309
2310/********************************************************************
2311 * gnc_commodity_table_add_default_data
2312 ********************************************************************/
2313
2314#define CUR_I18N(String) dgettext ("iso_4217", String)
2315
2316gboolean
2318{
2319 QofCollection *col;
2320 gnc_commodity* c;
2321
2322 ENTER ("table=%p", table);
2323 gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_TEMPLATE, book);
2324 c = gnc_commodity_new(book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1);
2326
2327#include "iso-4217-currencies.c"
2328
2329 /* We've just created the default namespaces and currencies. Mark
2330 * these collections as clean because there is no USER entered data
2331 * in these collections as of yet. */
2332 col = qof_book_get_collection(book, GNC_ID_COMMODITY);
2334 col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE);
2336
2337 LEAVE ("table=%p", table);
2338 return TRUE;
2339}
2340
2341/********************************************************************
2342 ********************************************************************/
2343/* QofObject function implementation and registration */
2344
2345#ifdef _MSC_VER
2346/* MSVC compiler doesn't have C99 "designated initializers"
2347 * so we wrap them in a macro that is empty on MSVC. */
2348# define DI(x) /* */
2349#else
2350# define DI(x) x
2351#endif
2352static QofObject commodity_object_def =
2353{
2354 DI(.interface_version = ) QOF_OBJECT_VERSION,
2355 DI(.e_type = ) GNC_ID_COMMODITY,
2356 DI(.type_label = ) "Commodity",
2357 DI(.create = ) nullptr,
2358 DI(.book_begin = ) nullptr,
2359 DI(.book_end = ) nullptr,
2360 DI(.is_dirty = ) qof_collection_is_dirty,
2361 DI(.mark_clean = ) qof_collection_mark_clean,
2362 DI(.foreach = ) qof_collection_foreach,
2363 DI(.printable = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname,
2364};
2365
2366static QofObject namespace_object_def =
2367{
2368 DI(.interface_version = ) QOF_OBJECT_VERSION,
2369 DI(.e_type = ) GNC_ID_COMMODITY_NAMESPACE,
2370 DI(.type_label = ) "Namespace",
2371 DI(.create = ) nullptr,
2372 DI(.book_begin = ) nullptr,
2373 DI(.book_end = ) nullptr,
2374 DI(.is_dirty = ) nullptr,
2375 DI(.mark_clean = ) nullptr,
2376 DI(.foreach = ) nullptr,
2377 DI(.printable = ) nullptr,
2378};
2379
2380static void
2381commodity_table_book_begin (QofBook *book)
2382{
2383 gnc_commodity_table *ct;
2384 ENTER ("book=%p", book);
2385
2387 return;
2388
2390 qof_book_set_data (book, GNC_COMMODITY_TABLE, ct);
2391
2393 {
2394 PWARN("unable to initialize book's commodity_table");
2395 }
2396
2397 LEAVE ("book=%p", book);
2398}
2399
2400static void
2401commodity_table_book_end (QofBook *book)
2402{
2403 gnc_commodity_table *ct;
2404
2406 qof_book_set_data (book, GNC_COMMODITY_TABLE, nullptr);
2407 gnc_commodity_table_destroy (ct);
2408}
2409
2410static QofObject commodity_table_object_def =
2411{
2412 DI(.interface_version = ) QOF_OBJECT_VERSION,
2413 DI(.e_type = ) GNC_ID_COMMODITY_TABLE,
2414 DI(.type_label = ) "CommodityTable",
2415 DI(.create = ) nullptr,
2416 DI(.book_begin = ) commodity_table_book_begin,
2417 DI(.book_end = ) commodity_table_book_end,
2418 DI(.is_dirty = ) qof_collection_is_dirty,
2419 DI(.mark_clean = ) qof_collection_mark_clean,
2420 DI(.foreach = ) nullptr,
2421 DI(.printable = ) nullptr,
2422 DI(.version_cmp = ) nullptr,
2423};
2424
2425gboolean
2427{
2428 if (!qof_object_register (&commodity_object_def))
2429 return FALSE;
2430 if (!qof_object_register (&namespace_object_def))
2431 return FALSE;
2432 return qof_object_register (&commodity_table_object_def);
2433}
2434
2435/* *******************************************************************
2436* gnc_monetary methods
2437********************************************************************/
2438
2440MonetaryList *
2441gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
2442{
2443 MonetaryList *l = list, *tmp;
2444 for (tmp = list; tmp; tmp = tmp->next)
2445 {
2446 auto list_mon = static_cast<gnc_monetary*>(tmp->data);
2447 if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity))
2448 {
2449 list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
2451 break;
2452 }
2453 }
2454
2455 /* See if we found an entry, and add one if not */
2456 if (tmp == nullptr)
2457 {
2458 auto new_mon = static_cast<gnc_monetary*>(g_new0(gnc_monetary, 1));
2459 *new_mon = add_mon;
2460 l = g_list_prepend(l, new_mon);
2461 }
2462
2463 return l;
2464}
2465
2468MonetaryList *
2470{
2471 MonetaryList *node, *next;
2472 for (node = list; node; node = next)
2473 {
2474 auto mon = static_cast<gnc_monetary*>(node->data);
2475 next = node->next;
2476 if (gnc_numeric_zero_p(mon->value))
2477 {
2478 g_free(mon);
2479 list = g_list_delete_link(list, node);
2480 }
2481 }
2482 return list;
2483}
2484
2486void
2487gnc_monetary_list_free(MonetaryList *list)
2488{
2489 MonetaryList *tmp;
2490 for (tmp = list; tmp; tmp = tmp->next)
2491 {
2492 g_free(tmp->data);
2493 }
2494
2495 g_list_free(list);
2496}
2497
2498/* ========================= END OF FILE ============================== */
Commodity handling public routines.
Commodity handling public routines (C++ api)
Generic api to store and retrieve preferences.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition qofbackend.h:58
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition qofbook.cpp:521
int gnc_commodity_table_has_namespace(const gnc_commodity_table *table, const char *name_space)
Test to see if the indicated namespace exits in the commodity table.
const char * gnc_quote_source_fq_version(void)
This function returns the version of the Finance::Quote module installed on a user's computer.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
CommodityList * gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table *table)
This function returns a list of commodities for which price quotes should be retrieved.
const char * gnc_quote_source_get_user_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the user friendly name of this quote source.
guint gnc_commodity_table_get_size(const gnc_commodity_table *tbl)
Returns the number of commodities in the commodity table.
#define GNC_COMMODITY_MAX_FRACTION
Max fraction is 10^9 because 10^10 would require changing it to an int64_t.
void gnc_commodity_set_cusip(gnc_commodity *cm, const char *cusip)
Set the 'exchange code' 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.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
gboolean gnc_quote_source_get_supported(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the flag that indicates whether this particular quote...
const char * gnc_commodity_get_user_symbol(const gnc_commodity *cm)
Retrieve the user-defined symbol for the specified commodity.
gnc_commodity_namespace * gnc_commodity_table_find_namespace(const gnc_commodity_table *table, const char *name_space)
This function finds a commodity namespace in the set of existing commodity namespaces.
void gnc_quote_source_set_fq_installed(const char *version_string, const std::vector< std::string > &sources_list)
Update gnucash internal tables based on what Finance::Quote sources are installed.
void gnc_commodity_table_remove(gnc_commodity_table *table, gnc_commodity *comm)
Remove a commodity from the commodity table.
gboolean gnc_quote_source_fq_installed(void)
This function indicates whether or not the Finance::Quote module is installed on a user's computer.
void gnc_commodity_set_namespace(gnc_commodity *cm, const char *name_space)
Set the namespace for the specified commodity.
gint gnc_quote_source_get_index(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the index of this particular quote source within its ...
gnc_quote_source * gnc_quote_source_add_new(const char *source_name, gboolean supported)
Create a new quote source.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
const char * gnc_quote_source_get_internal_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the internal name of this quote source.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gnc_commodity_namespace * gnc_commodity_table_add_namespace(gnc_commodity_table *table, const char *name_space, QofBook *book)
This function adds a new string to the list of commodity namespaces.
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
Create a new commodity.
void gnc_commodity_set_fullname(gnc_commodity *cm, const char *fullname)
Set the full name for the specified commodity.
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
void gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity.
MonetaryList * gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
Add a gnc_monetary to the list.
gnc_commodity * gnc_commodity_obtain_twin(const gnc_commodity *from, QofBook *book)
Given the commodity 'findlike', this routine will find and return the equivalent commodity (commodity...
gboolean gnc_commodity_namespace_is_iso(const char *name_space)
Checks to see if the specified commodity namespace is the namespace for ISO 4217 currencies.
gboolean gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
Add all the standard namespaces and currencies to the commodity table.
const char * gnc_commodity_get_quote_tz(const gnc_commodity *cm)
Retrieve the automatic price quote timezone for the specified commodity.
const char * gnc_commodity_get_default_symbol(const gnc_commodity *cm)
Retrieve the default symbol for the specified commodity.
const char * gnc_commodity_get_nice_symbol(const gnc_commodity *cm)
Retrieve a symbol for the specified commodity, suitable for display to the user.
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
Set the automatic price quote source for the specified commodity.
int gnc_commodity_compare_void(const void *a, const void *b)
A wrapper around gnc_commodity_compare() which offers the function declaration that is needed for g_l...
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
void gnc_commodity_set_fraction(gnc_commodity *cm, int fraction)
Set the fraction for the specified commodity.
gboolean gnc_commodity_table_register(void)
You should probably not be using gnc_commodity_table_register() It is an internal routine for registe...
gnc_quote_source * gnc_commodity_get_quote_source(const gnc_commodity *cm)
Retrieve the automatic price quote source for the specified commodity.
bool gnc_commodity_table_rename_namespace(const gnc_commodity_table *table, const char *namespace_name, const char *new_namespace_name)
This function renames a namespace.
const char * gnc_commodity_get_cusip(const gnc_commodity *cm)
Retrieve the 'exchange code' for the specified commodity.
gnc_quote_source * gnc_quote_source_lookup_by_ti(QuoteSourceType type, gint index)
Given the type/index of a quote source, find the data structure identified by this pair.
gnc_quote_source * gnc_quote_source_lookup_by_internal(const char *name)
Given the internal (gnucash or F::Q) name of a quote source, find the data structure identified by th...
void gnc_commodity_table_delete_namespace(gnc_commodity_table *table, const char *name_space)
This function deletes a string from the list of commodity namespaces.
void gnc_commodity_increment_usage_count(gnc_commodity *cm)
Increment a commodity's internal counter that tracks how many accounts are using that commodity.
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the 'print' name for the specified commodity.
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
QuoteSourceType
The quote source type enum account types are used to determine how the transaction data in the accoun...
MonetaryList * gnc_monetary_list_delete_zeros(MonetaryList *list)
Delete all entries in the list that have zero value.
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
void gnc_commodity_set_user_symbol(gnc_commodity *cm, const char *user_symbol)
Set a user-defined symbol for the specified commodity.
void gnc_commodity_decrement_usage_count(gnc_commodity *cm)
Decrement a commodity's internal counter that tracks how many accounts are using that commodity.
gnc_commodity * gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
allocate and copy
void gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity, based on user input.
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
gnc_commodity_namespace * gnc_commodity_get_namespace_ds(const gnc_commodity *cm)
Retrieve the namespace data structure for the specified commodity.
gnc_commodity_table * gnc_commodity_table_new(void)
You probably shouldn't be using gnc_commodity_table_new() directly, it's for internal use only.
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
#define GNC_COMMODITY_NS_LEGACY
The commodity namespace definitions are used to tag a commodity by its type, or a stocks by the excha...
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm)
Retrieve the automatic price quote flag for the specified commodity.
void gnc_commodity_set_mnemonic(gnc_commodity *cm, const char *mnemonic)
Set the mnemonic for the specified commodity.
const char * gnc_commodity_namespace_get_gui_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure in a form suitable to present to the user.
void gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
Set the automatic price quote timezone for the specified commodity.
QuoteSourceType gnc_quote_source_get_type(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the type of this particular quote source.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the 'unique' name for the specified commodity.
void gnc_commodity_copy(gnc_commodity *dest, const gnc_commodity *src)
Copy src into dest.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency.
gint gnc_quote_source_num_entries(QuoteSourceType type)
Return the number of entries for a given type of quote source.
@ SOURCE_MULTI
This quote source may pull from multiple web sites.
@ SOURCE_SINGLE
This quote source pulls from a single specific web site.
@ SOURCE_CURRENCY
The special currency quote source.
@ SOURCE_UNKNOWN
This is a locally installed quote source that gnucash knows nothing about.
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition qofid.cpp:209
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
Definition qofid.cpp:232
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition qofevent.cpp:231
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_HOW_DENOM_EXACT
Use any denominator which gives an exactly correct ratio of numerator to denominator.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition qofid.cpp:238
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition qofobject.h:63
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
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
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
globally unique ID User API
Object instance holds common fields that most gnucash objects use.
The type used to store guids in C.
Definition guid.h:75
QofBook reference.
Definition qofbook-p.hpp:47
A gnc_commodity_namespace is an collection of commodities.
An article that is bought and sold.
A gnc_commodity_table is a database of commodity info.