GnuCash  4.8a-132-gcdaeb421d+
qofbook.cpp
1 /********************************************************************\
2  * qofbook.c -- dataset access (set of books of entities) *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20 \********************************************************************/
21 
22 /*
23  * FILE:
24  * qofbook.cpp
25  *
26  * FUNCTION:
27  * Encapsulate all the information about a QOF dataset.
28  *
29  * HISTORY:
30  * Created by Linas Vepstas December 1998
31  * Copyright (c) 1998-2001,2003 Linas Vepstas <linas@linas.org>
32  * Copyright (c) 2000 Dave Peticolas
33  * Copyright (c) 2007 David Hampton <hampton@employees.org>
34  */
35 #include <glib.h>
36 
37 extern "C"
38 {
39 
40 #include <config.h>
41 
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #ifdef GNC_PLATFORM_WINDOWS
46  /* Mingw disables the standard type macros for C++ without this override. */
47 #define __STDC_FORMAT_MACROS = 1
48 #endif
49 #include <inttypes.h>
50 
51 }
52 
53 #include "qof.h"
54 #include "qofevent-p.h"
55 #include "qofbackend.h"
56 #include "qofbook-p.h"
57 #include "qofid-p.h"
58 #include "qofobject-p.h"
59 #include "qofbookslots.h"
60 #include "kvp-frame.hpp"
61 // For GNC_ID_ROOT_ACCOUNT:
62 #include "AccountP.h"
63 
64 static QofLogModule log_module = QOF_MOD_ENGINE;
65 #define AB_KEY "hbci"
66 #define AB_TEMPLATES "template-list"
67 
68 enum
69 {
70  PROP_0,
71 // PROP_ROOT_ACCOUNT, /* Table */
72 // PROP_ROOT_TEMPLATE, /* Table */
73 /* keep trading accounts property, while adding book-currency, default gains
74  policy and default gains account properties, so that files prior to 2.7 can
75  be read/processed; GUI changed to use all four properties as of 2.7.
76  Trading accounts, on the one hand, and book-currency plus default-gains-
77  policy, and optionally, default gains account, on the other, are mutually
78  exclusive */
79  PROP_OPT_TRADING_ACCOUNTS, /* KVP */
80 /* Book currency and default gains policy properties only apply if currency
81  accounting method selected in GUI is 'book-currency'; both required and
82  both are exclusive with trading accounts */
83  PROP_OPT_BOOK_CURRENCY, /* KVP */
84  PROP_OPT_DEFAULT_GAINS_POLICY, /* KVP */
85 /* Default gains account property only applies if currency accounting method
86  selected in GUI is 'book-currency'; its use is optional but exclusive with
87  trading accounts */
88  PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID, /* KVP */
89  PROP_OPT_AUTO_READONLY_DAYS, /* KVP */
90  PROP_OPT_NUM_FIELD_SOURCE, /* KVP */
91  PROP_OPT_DEFAULT_BUDGET, /* KVP */
92  PROP_OPT_FY_END, /* KVP */
93  PROP_AB_TEMPLATES, /* KVP */
94  N_PROPERTIES /* Just a counter */
95 };
96 
97 static void
98 qof_book_option_num_field_source_changed_cb (GObject *gobject,
99  GParamSpec *pspec,
100  gpointer user_data);
101 static void
102 qof_book_option_num_autoreadonly_changed_cb (GObject *gobject,
103  GParamSpec *pspec,
104  gpointer user_data);
105 
106 // Use a #define for the GParam name to avoid typos
107 #define PARAM_NAME_NUM_FIELD_SOURCE "split-action-num-field"
108 #define PARAM_NAME_NUM_AUTOREAD_ONLY "autoreadonly-days"
109 
110 G_DEFINE_TYPE(QofBook, qof_book, QOF_TYPE_INSTANCE);
111 QOF_GOBJECT_DISPOSE(qof_book);
112 QOF_GOBJECT_FINALIZE(qof_book);
113 
114 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
115 #undef G_PARAM_READWRITE
116 #define G_PARAM_READWRITE static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_WRITABLE)
117 /* ====================================================================== */
118 /* constructor / destructor */
119 
120 static void coll_destroy(gpointer col)
121 {
122  qof_collection_destroy((QofCollection *) col);
123 }
124 
125 static void
126 qof_book_init (QofBook *book)
127 {
128  if (!book) return;
129 
130  book->hash_of_collections = g_hash_table_new_full(
131  g_str_hash, g_str_equal,
132  (GDestroyNotify)qof_string_cache_remove, /* key_destroy_func */
133  coll_destroy); /* value_destroy_func */
134 
135  qof_instance_init_data (&book->inst, QOF_ID_BOOK, book);
136 
137  book->data_tables = g_hash_table_new (g_str_hash, g_str_equal);
138  book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal);
139 
140  book->book_open = 'y';
141  book->read_only = FALSE;
142  book->session_dirty = FALSE;
143  book->version = 0;
144  book->cached_num_field_source_isvalid = FALSE;
145  book->cached_num_days_autoreadonly_isvalid = FALSE;
146 
147  // Register a callback on this NUM_FIELD_SOURCE property of that object
148  // because it gets called quite a lot, so that its value must be stored in
149  // a bool member variable instead of a KVP lookup on each getter call.
150  g_signal_connect (G_OBJECT(book),
151  "notify::" PARAM_NAME_NUM_FIELD_SOURCE,
152  G_CALLBACK (qof_book_option_num_field_source_changed_cb),
153  book);
154 
155  // Register a callback on this NUM_AUTOREAD_ONLY property of that object
156  // because it gets called quite a lot, so that its value must be stored in
157  // a bool member variable instead of a KVP lookup on each getter call.
158  g_signal_connect (G_OBJECT(book),
159  "notify::" PARAM_NAME_NUM_AUTOREAD_ONLY,
160  G_CALLBACK (qof_book_option_num_autoreadonly_changed_cb),
161  book);
162 }
163 
164 static const std::string str_KVP_OPTION_PATH(KVP_OPTION_PATH);
165 static const std::string str_OPTION_SECTION_ACCOUNTS(OPTION_SECTION_ACCOUNTS);
166 static const std::string str_OPTION_SECTION_BUDGETING(OPTION_SECTION_BUDGETING);
167 static const std::string str_OPTION_NAME_DEFAULT_BUDGET(OPTION_NAME_DEFAULT_BUDGET);
168 static const std::string str_OPTION_NAME_TRADING_ACCOUNTS(OPTION_NAME_TRADING_ACCOUNTS);
169 static const std::string str_OPTION_NAME_AUTO_READONLY_DAYS(OPTION_NAME_AUTO_READONLY_DAYS);
170 static const std::string str_OPTION_NAME_NUM_FIELD_SOURCE(OPTION_NAME_NUM_FIELD_SOURCE);
171 
172 static void
173 qof_book_get_property (GObject* object,
174  guint prop_id,
175  GValue* value,
176  GParamSpec* pspec)
177 {
178  QofBook *book;
179  gchar *key;
180 
181  g_return_if_fail (QOF_IS_BOOK (object));
182  book = QOF_BOOK (object);
183  switch (prop_id)
184  {
185  case PROP_OPT_TRADING_ACCOUNTS:
186  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
187  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
188  break;
189  case PROP_OPT_BOOK_CURRENCY:
190  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
191  str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
192  break;
193  case PROP_OPT_DEFAULT_GAINS_POLICY:
194  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
195  str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
196  break;
197  case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
198  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
199  str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
200  break;
201  case PROP_OPT_AUTO_READONLY_DAYS:
202  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
203  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
204  break;
205  case PROP_OPT_NUM_FIELD_SOURCE:
206  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
207  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_NUM_FIELD_SOURCE});
208  break;
209  case PROP_OPT_DEFAULT_BUDGET:
210  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
211  str_OPTION_SECTION_BUDGETING, str_OPTION_NAME_DEFAULT_BUDGET});
212  break;
213  case PROP_OPT_FY_END:
214  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
215  break;
216  case PROP_AB_TEMPLATES:
217  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"AB_KEY", "AB_TEMPLATES"});
218  break;
219  default:
220  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
221  break;
222  }
223 }
224 
225 static void
226 qof_book_set_property (GObject *object,
227  guint prop_id,
228  const GValue *value,
229  GParamSpec *pspec)
230 {
231  QofBook *book;
232  gchar *key;
233 
234  g_return_if_fail (QOF_IS_BOOK (object));
235  book = QOF_BOOK (object);
236  g_assert (qof_instance_get_editlevel(book));
237 
238  switch (prop_id)
239  {
240  case PROP_OPT_TRADING_ACCOUNTS:
241  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
242  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
243  break;
244  case PROP_OPT_BOOK_CURRENCY:
245  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
246  str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
247  break;
248  case PROP_OPT_DEFAULT_GAINS_POLICY:
249  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
250  str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
251  break;
252  case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
253  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
254  str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
255  break;
256  case PROP_OPT_AUTO_READONLY_DAYS:
257  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
258  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
259  break;
260  case PROP_OPT_NUM_FIELD_SOURCE:
261  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
262  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_NUM_FIELD_SOURCE});
263  break;
264  case PROP_OPT_DEFAULT_BUDGET:
265  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
266  str_OPTION_SECTION_BUDGETING, OPTION_NAME_DEFAULT_BUDGET});
267  break;
268  case PROP_OPT_FY_END:
269  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
270  break;
271  case PROP_AB_TEMPLATES:
272  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {AB_KEY, AB_TEMPLATES});
273  break;
274  default:
275  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
276  break;
277  }
278 }
279 
280 static void
281 qof_book_class_init (QofBookClass *klass)
282 {
283  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
284  gobject_class->dispose = qof_book_dispose;
285  gobject_class->finalize = qof_book_finalize;
286  gobject_class->get_property = qof_book_get_property;
287  gobject_class->set_property = qof_book_set_property;
288 
289  g_object_class_install_property
290  (gobject_class,
291  PROP_OPT_TRADING_ACCOUNTS,
292  g_param_spec_string("trading-accts",
293  "Use Trading Accounts",
294  "Scheme true ('t') or NULL. If 't', then the book "
295  "uses trading accounts for managing multiple-currency "
296  "transactions.",
297  NULL,
298  G_PARAM_READWRITE));
299 
300  g_object_class_install_property
301  (gobject_class,
302  PROP_OPT_BOOK_CURRENCY,
303  g_param_spec_string("book-currency",
304  "Select Book Currency",
305  "The reference currency used to manage multiple-currency "
306  "transactions when 'book-currency' currency accounting method "
307  "selected; requires valid default gains/loss policy.",
308  NULL,
309  G_PARAM_READWRITE));
310 
311  g_object_class_install_property
312  (gobject_class,
313  PROP_OPT_DEFAULT_GAINS_POLICY,
314  g_param_spec_string("default-gains-policy",
315  "Select Default Gains Policy",
316  "The default policy to be used to calculate gains/losses on "
317  "dispositions of currencies/commodities other than "
318  "'book-currency' when 'book-currency' currency accounting "
319  "method selected; requires valid book-currency.",
320  NULL,
321  G_PARAM_READWRITE));
322 
323  g_object_class_install_property
324  (gobject_class,
325  PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID,
326  g_param_spec_boxed("default-gain-loss-account-guid",
327  "Select Default Gain/Loss Account",
328  "The default account to be used for calculated gains/losses on "
329  "dispositions of currencies/commodities other than "
330  "'book-currency' when 'book-currency' currency accounting "
331  "method selected; requires valid book-currency.",
332  GNC_TYPE_GUID,
333  G_PARAM_READWRITE));
334 
335  g_object_class_install_property
336  (gobject_class,
337  PROP_OPT_NUM_FIELD_SOURCE,
338  g_param_spec_string(PARAM_NAME_NUM_FIELD_SOURCE,
339  "Use Split-Action in the Num Field",
340  "Scheme true ('t') or NULL. If 't', then the book "
341  "will put the split action value in the Num field.",
342  NULL,
343  G_PARAM_READWRITE));
344 
345  g_object_class_install_property
346  (gobject_class,
347  PROP_OPT_AUTO_READONLY_DAYS,
348  g_param_spec_double("autoreadonly-days",
349  "Transaction Auto-read-only Days",
350  "Prevent editing of transactions posted more than "
351  "this many days ago.",
352  0,
353  G_MAXDOUBLE,
354  0,
355  G_PARAM_READWRITE));
356 
357  g_object_class_install_property
358  (gobject_class,
359  PROP_OPT_DEFAULT_BUDGET,
360  g_param_spec_boxed("default-budget",
361  "Book Default Budget",
362  "The default Budget for this book.",
363  GNC_TYPE_GUID,
364  G_PARAM_READWRITE));
365  g_object_class_install_property
366  (gobject_class,
367  PROP_OPT_FY_END,
368  g_param_spec_boxed("fy-end",
369  "Book Fiscal Year End",
370  "A GDate with a bogus year having the last Month and "
371  "Day of the Fiscal year for the book.",
372  G_TYPE_DATE,
373  G_PARAM_READWRITE));
374  g_object_class_install_property
375  (gobject_class,
376  PROP_AB_TEMPLATES,
377  g_param_spec_boxed("ab-templates",
378  "AQBanking Template List",
379  "A GList of AQBanking Templates",
380  GNC_TYPE_VALUE_LIST,
381  G_PARAM_READWRITE));
382 }
383 
384 QofBook *
386 {
387  QofBook *book;
388 
389  ENTER (" ");
390  book = static_cast<QofBook*>(g_object_new(QOF_TYPE_BOOK, NULL));
391  qof_object_book_begin (book);
392 
393  qof_event_gen (&book->inst, QOF_EVENT_CREATE, NULL);
394  LEAVE ("book=%p", book);
395  return book;
396 }
397 
398 static void
399 book_final (gpointer key, gpointer value, gpointer booq)
400 {
401  QofBookFinalCB cb = reinterpret_cast<QofBookFinalCB>(value);
402  QofBook *book = static_cast<QofBook*>(booq);
403 
404  gpointer user_data = g_hash_table_lookup (book->data_tables, key);
405  (*cb) (book, key, user_data);
406 }
407 
408 static void
409 qof_book_dispose_real (G_GNUC_UNUSED GObject *bookp)
410 {
411 }
412 
413 static void
414 qof_book_finalize_real (G_GNUC_UNUSED GObject *bookp)
415 {
416 }
417 
418 void
419 qof_book_destroy (QofBook *book)
420 {
421  GHashTable* cols;
422 
423  if (!book) return;
424  ENTER ("book=%p", book);
425 
426  book->shutting_down = TRUE;
427  qof_event_force (&book->inst, QOF_EVENT_DESTROY, NULL);
428 
429  /* Call the list of finalizers, let them do their thing.
430  * Do this before tearing into the rest of the book.
431  */
432  g_hash_table_foreach (book->data_table_finalizers, book_final, book);
433 
434  qof_object_book_end (book);
435 
436  g_hash_table_destroy (book->data_table_finalizers);
437  book->data_table_finalizers = NULL;
438  g_hash_table_destroy (book->data_tables);
439  book->data_tables = NULL;
440 
441  /* qof_instance_release (&book->inst); */
442 
443  /* Note: we need to save this hashtable until after we remove ourself
444  * from it, otherwise we'll crash in our dispose() function when we
445  * DO remove ourself from the collection but the collection had already
446  * been destroyed.
447  */
448  cols = book->hash_of_collections;
449  g_object_unref (book);
450  g_hash_table_destroy (cols);
451  /*book->hash_of_collections = NULL;*/
452 
453  LEAVE ("book=%p", book);
454 }
455 
456 /* ====================================================================== */
457 
458 gboolean
459 qof_book_session_not_saved (const QofBook *book)
460 {
461  if (!book) return FALSE;
462  return !qof_book_empty(book) && book->session_dirty;
463 
464 }
465 
466 void
468 {
469  if (!book) return;
470 
471  book->dirty_time = 0;
472  if (book->session_dirty)
473  {
474  /* Set the session clean upfront, because the callback will check. */
475  book->session_dirty = FALSE;
476  if (book->dirty_cb)
477  book->dirty_cb(book, FALSE, book->dirty_data);
478  }
479 }
480 
481 void qof_book_mark_session_dirty (QofBook *book)
482 {
483  if (!book) return;
484  if (!book->session_dirty)
485  {
486  /* Set the session dirty upfront, because the callback will check. */
487  book->session_dirty = TRUE;
488  book->dirty_time = gnc_time (NULL);
489  if (book->dirty_cb)
490  book->dirty_cb(book, TRUE, book->dirty_data);
491  }
492 }
493 
494 void
495 qof_book_print_dirty (const QofBook *book)
496 {
497  if (qof_book_session_not_saved(book))
498  PINFO("book is dirty.");
499  qof_book_foreach_collection
500  (book, (QofCollectionForeachCB)qof_collection_print_dirty, NULL);
501 }
502 
503 time64
504 qof_book_get_session_dirty_time (const QofBook *book)
505 {
506  return book->dirty_time;
507 }
508 
509 void
510 qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
511 {
512  g_return_if_fail(book);
513  if (book->dirty_cb)
514  PWARN("Already existing callback %p, will be overwritten by %p\n",
515  book->dirty_cb, cb);
516  book->dirty_data = user_data;
517  book->dirty_cb = cb;
518 }
519 
520 /* ====================================================================== */
521 /* getters */
522 
523 QofBackend *
524 qof_book_get_backend (const QofBook *book)
525 {
526  if (!book) return NULL;
527  return book->backend;
528 }
529 
530 gboolean
531 qof_book_shutting_down (const QofBook *book)
532 {
533  if (!book) return FALSE;
534  return book->shutting_down;
535 }
536 
537 /* ====================================================================== */
538 /* setters */
539 
540 void
541 qof_book_set_backend (QofBook *book, QofBackend *be)
542 {
543  if (!book) return;
544  ENTER ("book=%p be=%p", book, be);
545  book->backend = be;
546  LEAVE (" ");
547 }
548 
549 /* ====================================================================== */
550 /* Store arbitrary pointers in the QofBook for data storage extensibility */
551 /* XXX if data is NULL, we should remove the key from the hash table!
552  */
553 void
554 qof_book_set_data (QofBook *book, const char *key, gpointer data)
555 {
556  if (!book || !key) return;
557  g_hash_table_insert (book->data_tables, (gpointer)key, data);
558 }
559 
560 void
561 qof_book_set_data_fin (QofBook *book, const char *key, gpointer data, QofBookFinalCB cb)
562 {
563  if (!book || !key) return;
564  g_hash_table_insert (book->data_tables, (gpointer)key, data);
565 
566  if (!cb) return;
567  g_hash_table_insert (book->data_table_finalizers, (gpointer)key,
568  reinterpret_cast<void*>(cb));
569 }
570 
571 gpointer
572 qof_book_get_data (const QofBook *book, const char *key)
573 {
574  if (!book || !key) return NULL;
575  return g_hash_table_lookup (book->data_tables, (gpointer)key);
576 }
577 
578 /* ====================================================================== */
579 gboolean
580 qof_book_is_readonly(const QofBook *book)
581 {
582  g_return_val_if_fail( book != NULL, TRUE );
583  return book->read_only;
584 }
585 
586 void
588 {
589  g_return_if_fail( book != NULL );
590  book->read_only = TRUE;
591 }
592 
593 gboolean
594 qof_book_empty(const QofBook *book)
595 {
596  if (!book) return TRUE;
597  auto root_acct_col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
598  return qof_collection_get_data(root_acct_col) == nullptr;
599 }
600 
601 /* ====================================================================== */
602 
603 QofCollection *
604 qof_book_get_collection (const QofBook *book, QofIdType entity_type)
605 {
606  QofCollection *col;
607 
608  if (!book || !entity_type) return NULL;
609 
610  col = static_cast<QofCollection*>(g_hash_table_lookup (book->hash_of_collections, entity_type));
611  if (!col)
612  {
613  col = qof_collection_new (entity_type);
614  g_hash_table_insert(
615  book->hash_of_collections,
616  (gpointer)qof_string_cache_insert(entity_type), col);
617  }
618  return col;
619 }
620 
621 struct _iterate
622 {
624  gpointer data;
625 };
626 
627 static void
628 foreach_cb (G_GNUC_UNUSED gpointer key, gpointer item, gpointer arg)
629 {
630  struct _iterate *iter = static_cast<_iterate*>(arg);
631  QofCollection *col = static_cast<QofCollection*>(item);
632 
633  iter->fn (col, iter->data);
634 }
635 
636 void
637 qof_book_foreach_collection (const QofBook *book,
638  QofCollectionForeachCB cb, gpointer user_data)
639 {
640  struct _iterate iter;
641 
642  g_return_if_fail (book);
643  g_return_if_fail (cb);
644 
645  iter.fn = cb;
646  iter.data = user_data;
647 
648  g_hash_table_foreach (book->hash_of_collections, foreach_cb, &iter);
649 }
650 
651 /* ====================================================================== */
652 
653 void qof_book_mark_closed (QofBook *book)
654 {
655  if (!book)
656  {
657  return;
658  }
659  book->book_open = 'n';
660 }
661 
662 gint64
663 qof_book_get_counter (QofBook *book, const char *counter_name)
664 {
665  KvpFrame *kvp;
666  KvpValue *value;
667 
668  if (!book)
669  {
670  PWARN ("No book!!!");
671  return -1;
672  }
673 
674  if (!counter_name || *counter_name == '\0')
675  {
676  PWARN ("Invalid counter name.");
677  return -1;
678  }
679 
680  /* Use the KVP in the book */
681  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
682 
683  if (!kvp)
684  {
685  PWARN ("Book has no KVP_Frame");
686  return -1;
687  }
688 
689  value = kvp->get_slot({"counters", counter_name});
690  if (value)
691  {
692  /* found it */
693  return value->get<int64_t>();
694  }
695  else
696  {
697  /* New counter */
698  return 0;
699  }
700 }
701 
702 gchar *
703 qof_book_increment_and_format_counter (QofBook *book, const char *counter_name)
704 {
705  KvpFrame *kvp;
706  KvpValue *value;
707  gint64 counter;
708  gchar* format;
709  gchar* result;
710 
711  if (!book)
712  {
713  PWARN ("No book!!!");
714  return NULL;
715  }
716 
717  if (!counter_name || *counter_name == '\0')
718  {
719  PWARN ("Invalid counter name.");
720  return NULL;
721  }
722 
723  /* Get the current counter value from the KVP in the book. */
724  counter = qof_book_get_counter(book, counter_name);
725 
726  /* Check if an error occurred */
727  if (counter < 0)
728  return NULL;
729 
730  /* Increment the counter */
731  counter++;
732 
733  /* Get the KVP from the current book */
734  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
735 
736  if (!kvp)
737  {
738  PWARN ("Book has no KVP_Frame");
739  return NULL;
740  }
741 
742  /* Save off the new counter */
743  qof_book_begin_edit(book);
744  value = new KvpValue(counter);
745  delete kvp->set_path({"counters", counter_name}, value);
746  qof_instance_set_dirty (QOF_INSTANCE (book));
747  qof_book_commit_edit(book);
748 
749  format = qof_book_get_counter_format(book, counter_name);
750 
751  if (!format)
752  {
753  PWARN("Cannot get format for counter");
754  return NULL;
755  }
756 
757  /* Generate a string version of the counter */
758  result = g_strdup_printf(format, counter);
759  g_free (format);
760  return result;
761 }
762 
763 char *
764 qof_book_get_counter_format(const QofBook *book, const char *counter_name)
765 {
766  KvpFrame *kvp;
767  const char *user_format = NULL;
768  gchar *norm_format = NULL;
769  KvpValue *value;
770  gchar *error = NULL;
771 
772  if (!book)
773  {
774  PWARN ("No book!!!");
775  return NULL;
776  }
777 
778  if (!counter_name || *counter_name == '\0')
779  {
780  PWARN ("Invalid counter name.");
781  return NULL;
782  }
783 
784  /* Get the KVP from the current book */
785  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
786 
787  if (!kvp)
788  {
789  PWARN ("Book has no KVP_Frame");
790  return NULL;
791  }
792 
793  /* Get the format string */
794  value = kvp->get_slot({"counter_formats", counter_name});
795  if (value)
796  {
797  user_format = value->get<const char*>();
798  norm_format = qof_book_normalize_counter_format(user_format, &error);
799  if (!norm_format)
800  {
801  PWARN("Invalid counter format string. Format string: '%s' Counter: '%s' Error: '%s')", user_format, counter_name, error);
802  /* Invalid format string */
803  user_format = NULL;
804  g_free(error);
805  }
806  }
807 
808  /* If no (valid) format string was found, use the default format
809  * string */
810  if (!norm_format)
811  {
812  /* Use the default format */
813  norm_format = g_strdup ("%.6" PRIi64);
814  }
815  return norm_format;
816 }
817 
818 gchar *
819 qof_book_normalize_counter_format(const gchar *p, gchar **err_msg)
820 {
821  const gchar *valid_formats [] = {
822  G_GINT64_FORMAT,
823  "lli",
824  "I64i",
825  PRIi64,
826  "li",
827  NULL,
828  };
829  int i = 0;
830  gchar *normalized_spec = NULL;
831 
832  while (valid_formats[i])
833  {
834 
835  if (err_msg && *err_msg)
836  {
837  g_free (*err_msg);
838  *err_msg = NULL;
839  }
840 
841  normalized_spec = qof_book_normalize_counter_format_internal(p, valid_formats[i], err_msg);
842  if (normalized_spec)
843  return normalized_spec; /* Found a valid format specifier, return */
844  i++;
845  }
846 
847  return NULL;
848 }
849 
850 gchar *
852  const gchar *gint64_format, gchar **err_msg)
853 {
854  const gchar *conv_start, *base, *tmp = NULL;
855  gchar *normalized_str = NULL, *aux_str = NULL;
856 
857  /* Validate a counter format. This is a very simple "parser" that
858  * simply checks for a single gint64 conversion specification,
859  * allowing all modifiers and flags that printf(3) specifies (except
860  * for the * width and precision, which need an extra argument). */
861  base = p;
862 
863  /* Skip a prefix of any character except % */
864  while (*p)
865  {
866  /* Skip two adjacent percent marks, which are literal percent
867  * marks */
868  if (p[0] == '%' && p[1] == '%')
869  {
870  p += 2;
871  continue;
872  }
873  /* Break on a single percent mark, which is the start of the
874  * conversion specification */
875  if (*p == '%')
876  break;
877  /* Skip all other characters */
878  p++;
879  }
880 
881  if (!*p)
882  {
883  if (err_msg)
884  *err_msg = g_strdup("Format string ended without any conversion specification");
885  return NULL;
886  }
887 
888  /* Store the start of the conversion for error messages */
889  conv_start = p;
890 
891  /* Skip the % */
892  p++;
893 
894  /* See whether we have already reached the correct format
895  * specification (e.g. "li" on Unix, "I64i" on Windows). */
896  tmp = strstr(p, gint64_format);
897 
898  if (!tmp)
899  {
900  if (err_msg)
901  *err_msg = g_strdup_printf("Format string doesn't contain requested format specifier: %s", gint64_format);
902  return NULL;
903  }
904 
905  /* Skip any number of flag characters */
906  while (*p && (tmp != p) && strchr("#0- +'I", *p))
907  {
908  p++;
909  tmp = strstr(p, gint64_format);
910  }
911 
912  /* Skip any number of field width digits,
913  * and precision specifier digits (including the leading dot) */
914  while (*p && (tmp != p) && strchr("0123456789.", *p))
915  {
916  p++;
917  tmp = strstr(p, gint64_format);
918  }
919 
920  if (!*p)
921  {
922  if (err_msg)
923  *err_msg = g_strdup_printf("Format string ended during the conversion specification. Conversion seen so far: %s", conv_start);
924  return NULL;
925  }
926 
927  /* See if the format string starts with the correct format
928  * specification. */
929  tmp = strstr(p, gint64_format);
930  if (tmp == NULL)
931  {
932  if (err_msg)
933  *err_msg = g_strdup_printf("Invalid length modifier and/or conversion specifier ('%.4s'), it should be: %s", p, gint64_format);
934  return NULL;
935  }
936  else if (tmp != p)
937  {
938  if (err_msg)
939  *err_msg = g_strdup_printf("Garbage before length modifier and/or conversion specifier: '%*s'", (int)(tmp - p), p);
940  return NULL;
941  }
942 
943  /* Copy the string we have so far and add normalized format specifier for long int */
944  aux_str = g_strndup (base, p - base);
945  normalized_str = g_strconcat (aux_str, PRIi64, nullptr);
946  g_free (aux_str);
947 
948  /* Skip length modifier / conversion specifier */
949  p += strlen(gint64_format);
950  tmp = p;
951 
952  /* Skip a suffix of any character except % */
953  while (*p)
954  {
955  /* Skip two adjacent percent marks, which are literal percent
956  * marks */
957  if (p[0] == '%' && p[1] == '%')
958  {
959  p += 2;
960  continue;
961  }
962  /* Break on a single percent mark, which is the start of the
963  * conversion specification */
964  if (*p == '%')
965  {
966  if (err_msg)
967  *err_msg = g_strdup_printf("Format string contains unescaped %% signs (or multiple conversion specifications) at '%s'", p);
968  g_free (normalized_str);
969  return NULL;
970  }
971  /* Skip all other characters */
972  p++;
973  }
974 
975  /* Add the suffix to our normalized string */
976  aux_str = normalized_str;
977  normalized_str = g_strconcat (aux_str, tmp, nullptr);
978  g_free (aux_str);
979 
980  /* If we end up here, the string was valid, so return no error
981  * message */
982  return normalized_str;
983 }
984 
990 const gchar *
992 {
993  const gchar *opt = NULL;
994  qof_instance_get (QOF_INSTANCE (book),
995  "book-currency", &opt,
996  NULL);
997  return opt;
998 }
999 
1005 const gchar *
1007 {
1008  const gchar *opt = NULL;
1009  qof_instance_get (QOF_INSTANCE (book),
1010  "default-gains-policy", &opt,
1011  NULL);
1012  return opt;
1013 }
1014 
1020 GncGUID *
1022 {
1023  GncGUID *guid = NULL;
1024  qof_instance_get (QOF_INSTANCE (book),
1025  "default-gain-loss-account-guid", &guid,
1026  NULL);
1027  return guid;
1028 
1029 }
1030 
1031 /* Determine whether this book uses trading accounts */
1032 gboolean
1033 qof_book_use_trading_accounts (const QofBook *book)
1034 {
1035  char *opt = nullptr;
1036  qof_instance_get (QOF_INSTANCE (book), "trading-accts", &opt, nullptr);
1037  auto retval = (opt && opt[0] == 't' && opt[1] == 0);
1038  g_free (opt);
1039  return retval;
1040 }
1041 
1042 /* Returns TRUE if this book uses split action field as the 'Num' field, FALSE
1043  * if it uses transaction number field */
1044 gboolean
1046 {
1047  g_return_val_if_fail (book, FALSE);
1048  if (!book->cached_num_field_source_isvalid)
1049  {
1050  // No cached value? Then do the expensive KVP lookup
1051  gboolean result;
1052  char *opt = NULL;
1053  qof_instance_get (QOF_INSTANCE (book),
1054  PARAM_NAME_NUM_FIELD_SOURCE, &opt,
1055  NULL);
1056 
1057  if (opt && opt[0] == 't' && opt[1] == 0)
1058  result = TRUE;
1059  else
1060  result = FALSE;
1061  g_free (opt);
1062 
1063  // We need to const_cast the "book" argument into a non-const pointer,
1064  // but as we are dealing only with cache variables, I think this is
1065  // understandable enough.
1066  const_cast<QofBook*>(book)->cached_num_field_source = result;
1067  const_cast<QofBook*>(book)->cached_num_field_source_isvalid = TRUE;
1068  }
1069  // Value is cached now. Use the cheap variable returning.
1070  return book->cached_num_field_source;
1071 }
1072 
1073 // The callback that is called when the KVP option value of
1074 // "split-action-num-field" changes, so that we mark the cached value as
1075 // invalid.
1076 static void
1077 qof_book_option_num_field_source_changed_cb (GObject *gobject,
1078  GParamSpec *pspec,
1079  gpointer user_data)
1080 {
1081  QofBook *book = reinterpret_cast<QofBook*>(user_data);
1082  g_return_if_fail(QOF_IS_BOOK(book));
1083  book->cached_num_field_source_isvalid = FALSE;
1084 }
1085 
1086 gboolean qof_book_uses_autoreadonly (const QofBook *book)
1087 {
1088  g_assert(book);
1089  return (qof_book_get_num_days_autoreadonly(book) != 0);
1090 }
1091 
1092 gint qof_book_get_num_days_autoreadonly (const QofBook *book)
1093 {
1094  g_assert(book);
1095 
1096  if (!book->cached_num_days_autoreadonly_isvalid)
1097  {
1098  double tmp;
1099 
1100  // No cached value? Then do the expensive KVP lookup
1101  qof_instance_get (QOF_INSTANCE (book),
1102  PARAM_NAME_NUM_AUTOREAD_ONLY, &tmp,
1103  NULL);
1104 
1105  const_cast<QofBook*>(book)->cached_num_days_autoreadonly = tmp;
1106  const_cast<QofBook*>(book)->cached_num_days_autoreadonly_isvalid = TRUE;
1107  }
1108  // Value is cached now. Use the cheap variable returning.
1109  return (gint) book->cached_num_days_autoreadonly;
1110 }
1111 
1112 GDate* qof_book_get_autoreadonly_gdate (const QofBook *book)
1113 {
1114  gint num_days;
1115  GDate* result = NULL;
1116 
1117  g_assert(book);
1118  num_days = qof_book_get_num_days_autoreadonly(book);
1119  if (num_days > 0)
1120  {
1121  result = gnc_g_date_new_today();
1122  g_date_subtract_days(result, num_days);
1123  }
1124  return result;
1125 }
1126 
1127 // The callback that is called when the KVP option value of
1128 // "autoreadonly-days" changes, so that we mark the cached value as
1129 // invalid.
1130 static void
1131 qof_book_option_num_autoreadonly_changed_cb (GObject *gobject,
1132  GParamSpec *pspec,
1133  gpointer user_data)
1134 {
1135  QofBook *book = reinterpret_cast<QofBook*>(user_data);
1136  g_return_if_fail(QOF_IS_BOOK(book));
1137  book->cached_num_days_autoreadonly_isvalid = FALSE;
1138 }
1139 
1140 /* Note: this will fail if the book slots we're looking for here are flattened at some point !
1141  * When that happens, this function can be removed. */
1142 static Path opt_name_to_path (const char* opt_name)
1143 {
1144  Path result;
1145  g_return_val_if_fail (opt_name, result);
1146  auto opt_name_list = g_strsplit(opt_name, "/", -1);
1147  for (int i=0; opt_name_list[i]; i++)
1148  result.push_back (opt_name_list[i]);
1149  g_strfreev (opt_name_list);
1150  return result;
1151 }
1152 
1153 const char*
1154 qof_book_get_string_option(const QofBook* book, const char* opt_name)
1155 {
1156  auto slot = qof_instance_get_slots(QOF_INSTANCE (book))->get_slot(opt_name_to_path(opt_name));
1157  if (slot == nullptr)
1158  return nullptr;
1159  return slot->get<const char*>();
1160 }
1161 
1162 void
1163 qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_val)
1164 {
1165  qof_book_begin_edit(book);
1166  auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
1167  auto opt_path = opt_name_to_path(opt_name);
1168  if (opt_val && (*opt_val != '\0'))
1169  delete frame->set_path(opt_path, new KvpValue(g_strdup(opt_val)));
1170  else
1171  delete frame->set_path(opt_path, nullptr);
1172  qof_instance_set_dirty (QOF_INSTANCE (book));
1173  qof_book_commit_edit(book);
1174 }
1175 
1176 const GncGUID*
1177 qof_book_get_guid_option(QofBook* book, GSList* path)
1178 {
1179  g_return_val_if_fail(book != nullptr, nullptr);
1180  g_return_val_if_fail(path != nullptr, nullptr);
1181 
1182  auto table_value = qof_book_get_option(book, path);
1183  if (!table_value)
1184  return nullptr;
1185  return table_value->get<GncGUID*>();
1186 }
1187 
1188 void
1189 qof_book_option_frame_delete (QofBook *book, const char* opt_name)
1190 {
1191  if (opt_name && (*opt_name != '\0'))
1192  {
1193  qof_book_begin_edit(book);
1194  auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
1195  auto opt_path = opt_name_to_path(opt_name);
1196  delete frame->set_path(opt_path, nullptr);
1197  qof_instance_set_dirty (QOF_INSTANCE (book));
1198  qof_book_commit_edit(book);
1199  }
1200 }
1201 
1202 void
1203 qof_book_begin_edit (QofBook *book)
1204 {
1205  qof_begin_edit(&book->inst);
1206 }
1207 
1208 static void commit_err (G_GNUC_UNUSED QofInstance *inst, QofBackendError errcode)
1209 {
1210  PERR ("Failed to commit: %d", errcode);
1211 // gnc_engine_signal_commit_error( errcode );
1212 }
1213 
1214 #define GNC_FEATURES "features"
1215 static void
1216 add_feature_to_hash (const gchar *key, KvpValue *value, GHashTable * user_data)
1217 {
1218  gchar *descr = g_strdup(value->get<const char*>());
1219  g_hash_table_insert (user_data, (gchar*)key, descr);
1220 }
1221 
1222 GHashTable *
1223 qof_book_get_features (QofBook *book)
1224 {
1225  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1226  GHashTable *features = g_hash_table_new_full (g_str_hash, g_str_equal,
1227  NULL, g_free);
1228 
1229  auto slot = frame->get_slot({GNC_FEATURES});
1230  if (slot != nullptr)
1231  {
1232  frame = slot->get<KvpFrame*>();
1233  frame->for_each_slot_temp(&add_feature_to_hash, features);
1234  }
1235  return features;
1236 }
1237 
1238 void
1239 qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr)
1240 {
1241  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1242  KvpValue* feature = nullptr;
1243  auto feature_slot = frame->get_slot({GNC_FEATURES});
1244  if (feature_slot)
1245  {
1246  auto feature_frame = feature_slot->get<KvpFrame*>();
1247  feature = feature_frame->get_slot({key});
1248  }
1249  if (feature == nullptr || g_strcmp0 (feature->get<const char*>(), descr))
1250  {
1251  qof_book_begin_edit (book);
1252  delete frame->set_path({GNC_FEATURES, key}, new KvpValue(g_strdup (descr)));
1253  qof_instance_set_dirty (QOF_INSTANCE (book));
1254  qof_book_commit_edit (book);
1255  }
1256 }
1257 
1258 void
1259 qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb)
1260 {
1261  load_cb (odb, book);
1262 }
1263 
1264 void
1265 qof_book_save_options (QofBook *book, GNCOptionSave save_cb,
1266  GNCOptionDB* odb, gboolean clear)
1267 {
1268  /* Wrap this in begin/commit so that it commits only once instead of doing
1269  * so for every option. Qof_book_set_option will take care of dirtying the
1270  * book.
1271  */
1272  qof_book_begin_edit (book);
1273  save_cb (odb, book, clear);
1274  qof_book_commit_edit (book);
1275 }
1276 
1277 static void noop (QofInstance *inst) {}
1278 
1279 void
1280 qof_book_commit_edit(QofBook *book)
1281 {
1282  if (!qof_commit_edit (QOF_INSTANCE(book))) return;
1283  qof_commit_edit_part2 (&book->inst, commit_err, noop, noop/*lot_free*/);
1284 }
1285 
1286 /* Deal with the fact that some options are not in the "options" tree but rather
1287  * in the "counters" tree */
1288 static Path gslist_to_option_path (GSList *gspath)
1289 {
1290  Path tmp_path;
1291  if (!gspath) return tmp_path;
1292 
1293  Path path_v {str_KVP_OPTION_PATH};
1294  for (auto item = gspath; item != nullptr; item = g_slist_next(item))
1295  tmp_path.push_back(static_cast<const char*>(item->data));
1296  if (tmp_path.front() == "counters")
1297  return tmp_path;
1298 
1299  path_v.insert(path_v.end(), tmp_path.begin(), tmp_path.end());
1300  return path_v;
1301 }
1302 
1303 void
1304 qof_book_set_option (QofBook *book, KvpValue *value, GSList *path)
1305 {
1306  KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE (book));
1307  qof_book_begin_edit (book);
1308  delete root->set_path(gslist_to_option_path(path), value);
1309  qof_instance_set_dirty (QOF_INSTANCE (book));
1310  qof_book_commit_edit (book);
1311 
1312  // Also, mark any cached value as invalid
1313  book->cached_num_field_source_isvalid = FALSE;
1314 }
1315 
1316 KvpValue*
1317 qof_book_get_option (QofBook *book, GSList *path)
1318 {
1319  KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
1320  return root->get_slot(gslist_to_option_path(path));
1321 }
1322 
1323 void
1324 qof_book_options_delete (QofBook *book, GSList *path)
1325 {
1326  KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
1327  if (path != nullptr)
1328  {
1329  Path path_v {str_KVP_OPTION_PATH};
1330  Path tmp_path;
1331  for (auto item = path; item != nullptr; item = g_slist_next(item))
1332  tmp_path.push_back(static_cast<const char*>(item->data));
1333  delete root->set_path(gslist_to_option_path(path), nullptr);
1334  }
1335  else
1336  delete root->set_path({str_KVP_OPTION_PATH}, nullptr);
1337 }
1338 
1339 /* QofObject function implementation and registration */
1340 gboolean qof_book_register (void)
1341 {
1342  static QofParam params[] =
1343  {
1344  { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_entity_get_guid, NULL },
1345  { QOF_PARAM_KVP, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, NULL },
1346  { NULL },
1347  };
1348 
1349  qof_class_register (QOF_ID_BOOK, NULL, params);
1350 
1351  return TRUE;
1352 }
1353 
1354 /* ========================== END OF FILE =============================== */
API for data storage Backend.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
void qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
Set the function to call when a book transitions from clean to dirty, or vice versa.
Definition: qofbook.cpp:510
KvpValue * qof_book_get_option(QofBook *book, GSList *path)
Read a single option value.
Definition: qofbook.cpp:1317
void(* QofCollectionForeachCB)(QofCollection *, gpointer user_data)
Invoke the indicated callback on each collection in the book.
Definition: qofbook.h:235
gboolean qof_book_register(void)
Register the book object with the QOF object system.
Definition: qofbook.cpp:1340
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void qof_object_book_begin(QofBook *book)
To be called from within the book.
Definition: qofobject.cpp:92
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
time64 qof_book_get_session_dirty_time(const QofBook *book)
Retrieve the earliest modification time on the book.
Definition: qofbook.cpp:504
gchar * qof_book_increment_and_format_counter(QofBook *book, const char *counter_name)
This will increment the named counter for this book and format it.
Definition: qofbook.cpp:703
gint qof_book_get_num_days_autoreadonly(const QofBook *book)
Returns the number of days for auto-read-only transactions.
Definition: qofbook.cpp:1092
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
Returns TRUE if this book uses split action field as the &#39;Num&#39; field, FALSE if it uses transaction nu...
Definition: qofbook.cpp:1045
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition: qofclass.cpp:86
QofBook * qof_book_new(void)
Allocate, initialise and return a new QofBook.
Definition: qofbook.cpp:385
void qof_book_mark_closed(QofBook *book)
Close a book to editing.
Definition: qofbook.cpp:653
char * qof_book_get_counter_format(const QofBook *book, const char *counter_name)
Get the format string to use for the named counter.
Definition: qofbook.cpp:764
void qof_book_mark_readonly(QofBook *book)
Mark the book as read only.
Definition: qofbook.cpp:587
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GHashTable * qof_book_get_features(QofBook *book)
Access functions for reading and setting the used-features on this book.
Definition: qofbook.cpp:1223
const char * qof_string_cache_insert(const char *key)
You can use this function with g_hash_table_insert(), for the key (or value), as long as you use the ...
the Core Object Registration/Lookup Private Interface
const gchar * qof_book_get_default_gains_policy(QofBook *book)
Returns pointer to default gain/loss policy for book, if one exists in the KVP, or NULL; does not val...
Definition: qofbook.cpp:1006
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
gint64 qof_book_get_counter(QofBook *book, const char *counter_name)
This will get the named counter for this book.
Definition: qofbook.cpp:663
gboolean qof_book_empty(const QofBook *book)
Check if the book has had anything loaded into it.
Definition: qofbook.cpp:594
const gchar * QofIdType
QofIdType declaration.
Definition: qofid.h:85
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
GDate * qof_book_get_autoreadonly_gdate(const QofBook *book)
Returns the GDate that is the threshold for auto-read-only.
Definition: qofbook.cpp:1112
void qof_book_mark_session_saved(QofBook *book)
The qof_book_mark_saved() routine marks the book as having been saved (to a file, to a database)...
Definition: qofbook.cpp:467
void qof_book_set_data_fin(QofBook *book, const gchar *key, gpointer data, QofBookFinalCB)
Same as qof_book_set_data(), except that the callback will be called when the book is destroyed...
void qof_book_set_data(QofBook *book, const gchar *key, gpointer data)
The qof_book_set_data() allows arbitrary pointers to structs to be stored in QofBook.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:177
gchar * qof_book_normalize_counter_format(const gchar *p, gchar **err_msg)
Validate a counter format string.
Definition: qofbook.cpp:819
gchar * qof_book_normalize_counter_format_internal(const gchar *p, const gchar *gint64_format, gchar **err_msg)
Validate a counter format string with a given format specifier.
Definition: qofbook.cpp:851
gboolean qof_book_session_not_saved(const QofBook *book)
qof_book_not_saved() returns the value of the session_dirty flag, set when changes to any object in t...
Definition: qofbook.cpp:459
const GncGUID * qof_entity_get_guid(gconstpointer ent)
#define QOF_PARAM_KVP
"Known" Object Parameters – some objects might support these
Definition: qofquery.h:113
void qof_collection_destroy(QofCollection *col)
destroy the collection
Definition: qofid.cpp:62
void qof_book_mark_session_dirty(QofBook *book)
The qof_book_mark_dirty() routine marks the book as having been modified.
Definition: qofbook.cpp:481
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:580
void qof_book_load_options(QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb)
Load a GNCOptionsDB from KVP data.
Definition: qofbook.cpp:1259
const gchar * qof_book_get_book_currency_name(QofBook *book)
Returns pointer to book-currency name for book, if one exists in the KVP, or NULL; does not validate ...
Definition: qofbook.cpp:991
This is the private header for the account structure.
GncGUID * qof_book_get_default_gain_loss_acct_guid(QofBook *book)
Returns pointer to default gain/loss account GUID for book, if one exists in the KVP, or NULL; does not validate contents nor determine if there is a valid book-currency, both of which are required, for the &#39;book-currency&#39; currency accounting method to apply.
Definition: qofbook.cpp:1021
void qof_book_print_dirty(const QofBook *book)
This debugging function can be used to traverse the book structure and all subsidiary structures...
Definition: qofbook.cpp:495
void qof_book_save_options(QofBook *book, GNCOptionSave save_cb, GNCOptionDB *odb, gboolean clear)
Save a GNCOptionsDB back to the book&#39;s KVP.
Definition: qofbook.cpp:1265
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.cpp:273
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:604
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
gpointer qof_collection_get_data(const QofCollection *col)
Store and retrieve arbitrary object-defined data.
Definition: qofid.cpp:291
void qof_book_set_option(QofBook *book, KvpValue *value, GSList *path)
Save a single option value.
Definition: qofbook.cpp:1304
gboolean qof_book_uses_autoreadonly(const QofBook *book)
Returns TRUE if the auto-read-only feature should be used, otherwise FALSE.
Definition: qofbook.cpp:1086
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
Definition: qofbook.cpp:524
gboolean qof_book_shutting_down(const QofBook *book)
Is the book shutting down?
Definition: qofbook.cpp:531
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
void qof_book_options_delete(QofBook *book, GSList *path)
Delete the options.
Definition: qofbook.cpp:1324
The type used to store guids in C.
Definition: guid.h:75
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
Retrieves arbitrary pointers to structs stored by qof_book_set_data.
gboolean qof_book_use_trading_accounts(const QofBook *book)
Returns flag indicating whether this book uses trading accounts.
Definition: qofbook.cpp:1033
void qof_string_cache_remove(const char *key)
You can use this function as a destroy notifier for a GHashTable that uses common strings as keys (or...
void qof_book_destroy(QofBook *book)
End any editing sessions associated with book, and free all memory associated with it...
Definition: qofbook.cpp:419
QofCollection * qof_collection_new(QofIdType type)
create a new collection of entities of type
Definition: qofid.cpp:51
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).
Definition: gnc-date.cpp:1229