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