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