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