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.h"
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
331 qof_book_destroy (QofBook *book)
332 {
333  GHashTable* cols;
334 
335  if (!book) 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
375 qof_book_session_not_saved (const QofBook *book)
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 
397 void qof_book_mark_session_dirty (QofBook *book)
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
411 qof_book_print_dirty (const QofBook *book)
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
420 qof_book_get_session_dirty_time (const QofBook *book)
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 *
440 qof_book_get_backend (const QofBook *book)
441 {
442  if (!book) return nullptr;
443  return book->backend;
444 }
445 
446 gboolean
447 qof_book_shutting_down (const QofBook *book)
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
497 qof_book_is_readonly(const QofBook *book)
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
511 qof_book_empty(const QofBook *book)
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 
570 void qof_book_mark_closed (QofBook *book)
571 {
572  if (!book)
573  {
574  return;
575  }
576  book->book_open = 'n';
577 }
578 
579 gint64
580 qof_book_get_counter (QofBook *book, const char *counter_name)
581 {
582  KvpFrame *kvp;
583  KvpValue *value;
584 
585  if (!book)
586  {
587  PWARN ("No book!!!");
588  return -1;
589  }
590 
591  if (!counter_name || *counter_name == '\0')
592  {
593  PWARN ("Invalid counter name.");
594  return -1;
595  }
596 
597  /* Use the KVP in the book */
598  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
599 
600  if (!kvp)
601  {
602  PWARN ("Book has no KVP_Frame");
603  return -1;
604  }
605 
606  value = kvp->get_slot({"counters", counter_name});
607  if (value)
608  {
609  auto int_value{value->get<int64_t>()};
610  /* Might be a double because of
611  * https://bugs.gnucash.org/show_bug.cgi?id=798930
612  */
613  if (!int_value)
614  int_value = static_cast<int64_t>(value->get<double>());
615  return int_value;
616  }
617  else
618  {
619  /* New counter */
620  return 0;
621  }
622 }
623 
624 gchar *
625 qof_book_increment_and_format_counter (QofBook *book, const char *counter_name)
626 {
627  KvpFrame *kvp;
628  KvpValue *value;
629  gint64 counter;
630  gchar* format;
631  gchar* result;
632 
633  if (!book)
634  {
635  PWARN ("No book!!!");
636  return nullptr;
637  }
638 
639  if (!counter_name || *counter_name == '\0')
640  {
641  PWARN ("Invalid counter name.");
642  return nullptr;
643  }
644 
645  /* Get the current counter value from the KVP in the book. */
646  counter = qof_book_get_counter(book, counter_name);
647 
648  /* Check if an error occurred */
649  if (counter < 0)
650  return nullptr;
651 
652  /* Increment the counter */
653  counter++;
654 
655  /* Get the KVP from the current book */
656  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
657 
658  if (!kvp)
659  {
660  PWARN ("Book has no KVP_Frame");
661  return nullptr;
662  }
663 
664  /* Save off the new counter */
665  qof_book_begin_edit(book);
666  value = new KvpValue(counter);
667  delete kvp->set_path({"counters", counter_name}, value);
668  qof_instance_set_dirty (QOF_INSTANCE (book));
669  qof_book_commit_edit(book);
670 
671  format = qof_book_get_counter_format(book, counter_name);
672 
673  if (!format)
674  {
675  PWARN("Cannot get format for counter");
676  return nullptr;
677  }
678 
679  /* Generate a string version of the counter */
680  result = g_strdup_printf(format, counter);
681  g_free (format);
682  return result;
683 }
684 
685 char *
686 qof_book_get_counter_format(const QofBook *book, const char *counter_name)
687 {
688  KvpFrame *kvp;
689  const char *user_format = nullptr;
690  gchar *norm_format = nullptr;
691  KvpValue *value;
692  gchar *error = nullptr;
693 
694  if (!book)
695  {
696  PWARN ("No book!!!");
697  return nullptr;
698  }
699 
700  if (!counter_name || *counter_name == '\0')
701  {
702  PWARN ("Invalid counter name.");
703  return nullptr;
704  }
705 
706  /* Get the KVP from the current book */
707  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
708 
709  if (!kvp)
710  {
711  PWARN ("Book has no KVP_Frame");
712  return nullptr;
713  }
714 
715  /* Get the format string */
716  value = kvp->get_slot({"counter_formats", counter_name});
717  if (value)
718  {
719  user_format = value->get<const char*>();
720  norm_format = qof_book_normalize_counter_format(user_format, &error);
721  if (!norm_format)
722  {
723  PWARN("Invalid counter format string. Format string: '%s' Counter: '%s' Error: '%s')", user_format, counter_name, error);
724  /* Invalid format string */
725  user_format = nullptr;
726  g_free(error);
727  }
728  }
729 
730  /* If no (valid) format string was found, use the default format
731  * string */
732  if (!norm_format)
733  {
734  /* Use the default format */
735  norm_format = g_strdup ("%.6" PRIi64);
736  }
737  return norm_format;
738 }
739 
740 gchar *
741 qof_book_normalize_counter_format(const gchar *p, gchar **err_msg)
742 {
743  const gchar *valid_formats [] = {
744  G_GINT64_FORMAT,
745  "lli",
746  "I64i",
747  PRIi64,
748  "li",
749  nullptr,
750  };
751  int i = 0;
752  gchar *normalized_spec = nullptr;
753 
754  while (valid_formats[i])
755  {
756 
757  if (err_msg && *err_msg)
758  {
759  g_free (*err_msg);
760  *err_msg = nullptr;
761  }
762 
763  normalized_spec = qof_book_normalize_counter_format_internal(p, valid_formats[i], err_msg);
764  if (normalized_spec)
765  return normalized_spec; /* Found a valid format specifier, return */
766  i++;
767  }
768 
769  return nullptr;
770 }
771 
772 gchar *
774  const gchar *gint64_format, gchar **err_msg)
775 {
776  const gchar *conv_start, *base, *tmp = nullptr;
777  gchar *normalized_str = nullptr, *aux_str = nullptr;
778 
779  /* Validate a counter format. This is a very simple "parser" that
780  * simply checks for a single gint64 conversion specification,
781  * allowing all modifiers and flags that printf(3) specifies (except
782  * for the * width and precision, which need an extra argument). */
783  base = p;
784 
785  /* Skip a prefix of any character except % */
786  while (*p)
787  {
788  /* Skip two adjacent percent marks, which are literal percent
789  * marks */
790  if (p[0] == '%' && p[1] == '%')
791  {
792  p += 2;
793  continue;
794  }
795  /* Break on a single percent mark, which is the start of the
796  * conversion specification */
797  if (*p == '%')
798  break;
799  /* Skip all other characters */
800  p++;
801  }
802 
803  if (!*p)
804  {
805  if (err_msg)
806  *err_msg = g_strdup("Format string ended without any conversion specification");
807  return nullptr;
808  }
809 
810  /* Store the start of the conversion for error messages */
811  conv_start = p;
812 
813  /* Skip the % */
814  p++;
815 
816  /* See whether we have already reached the correct format
817  * specification (e.g. "li" on Unix, "I64i" on Windows). */
818  tmp = strstr(p, gint64_format);
819 
820  if (!tmp)
821  {
822  if (err_msg)
823  *err_msg = g_strdup_printf("Format string doesn't contain requested format specifier: %s", gint64_format);
824  return nullptr;
825  }
826 
827  /* Skip any number of flag characters */
828  while (*p && (tmp != p) && strchr("#0- +'I", *p))
829  {
830  p++;
831  tmp = strstr(p, gint64_format);
832  }
833 
834  /* Skip any number of field width digits,
835  * and precision specifier digits (including the leading dot) */
836  while (*p && (tmp != p) && strchr("0123456789.", *p))
837  {
838  p++;
839  tmp = strstr(p, gint64_format);
840  }
841 
842  if (!*p)
843  {
844  if (err_msg)
845  *err_msg = g_strdup_printf("Format string ended during the conversion specification. Conversion seen so far: %s", conv_start);
846  return nullptr;
847  }
848 
849  /* See if the format string starts with the correct format
850  * specification. */
851  tmp = strstr(p, gint64_format);
852  if (tmp == nullptr)
853  {
854  if (err_msg)
855  *err_msg = g_strdup_printf("Invalid length modifier and/or conversion specifier ('%.4s'), it should be: %s", p, gint64_format);
856  return nullptr;
857  }
858  else if (tmp != p)
859  {
860  if (err_msg)
861  *err_msg = g_strdup_printf("Garbage before length modifier and/or conversion specifier: '%*s'", (int)(tmp - p), p);
862  return nullptr;
863  }
864 
865  /* Copy the string we have so far and add normalized format specifier for long int */
866  aux_str = g_strndup (base, p - base);
867  normalized_str = g_strconcat (aux_str, PRIi64, nullptr);
868  g_free (aux_str);
869 
870  /* Skip length modifier / conversion specifier */
871  p += strlen(gint64_format);
872  tmp = p;
873 
874  /* Skip a suffix of any character except % */
875  while (*p)
876  {
877  /* Skip two adjacent percent marks, which are literal percent
878  * marks */
879  if (p[0] == '%' && p[1] == '%')
880  {
881  p += 2;
882  continue;
883  }
884  /* Break on a single percent mark, which is the start of the
885  * conversion specification */
886  if (*p == '%')
887  {
888  if (err_msg)
889  *err_msg = g_strdup_printf("Format string contains unescaped %% signs (or multiple conversion specifications) at '%s'", p);
890  g_free (normalized_str);
891  return nullptr;
892  }
893  /* Skip all other characters */
894  p++;
895  }
896 
897  /* Add the suffix to our normalized string */
898  aux_str = normalized_str;
899  normalized_str = g_strconcat (aux_str, tmp, nullptr);
900  g_free (aux_str);
901 
902  /* If we end up here, the string was valid, so return no error
903  * message */
904  return normalized_str;
905 }
906 
907 /* Determine whether this book uses trading accounts */
908 gboolean
909 qof_book_use_trading_accounts (const QofBook *book)
910 {
911  char *opt = nullptr;
912  qof_instance_get (QOF_INSTANCE (book), "trading-accts", &opt, nullptr);
913  auto retval = (opt && opt[0] == 't' && opt[1] == 0);
914  g_free (opt);
915  return retval;
916 }
917 
918 /* Returns TRUE if this book uses split action field as the 'Num' field, FALSE
919  * if it uses transaction number field */
920 gboolean
922 {
923  g_return_val_if_fail (book, FALSE);
924  if (!book->cached_num_field_source_isvalid)
925  {
926  // No cached value? Then do the expensive KVP lookup
927  gboolean result;
928  char *opt = nullptr;
929  qof_instance_get (QOF_INSTANCE (book),
930  PARAM_NAME_NUM_FIELD_SOURCE, &opt,
931  nullptr);
932 
933  if (opt && opt[0] == 't' && opt[1] == 0)
934  result = TRUE;
935  else
936  result = FALSE;
937  g_free (opt);
938 
939  // We need to const_cast the "book" argument into a non-const pointer,
940  // but as we are dealing only with cache variables, I think this is
941  // understandable enough.
942  const_cast<QofBook*>(book)->cached_num_field_source = result;
943  const_cast<QofBook*>(book)->cached_num_field_source_isvalid = TRUE;
944  }
945  // Value is cached now. Use the cheap variable returning.
946  return book->cached_num_field_source;
947 }
948 
949 // The callback that is called when the KVP option value of
950 // "split-action-num-field" changes, so that we mark the cached value as
951 // invalid.
952 static void
953 qof_book_option_num_field_source_changed_cb (GObject *gobject,
954  GParamSpec *pspec,
955  gpointer user_data)
956 {
957  QofBook *book = reinterpret_cast<QofBook*>(user_data);
958  g_return_if_fail(QOF_IS_BOOK(book));
959  book->cached_num_field_source_isvalid = FALSE;
960 }
961 
962 gboolean qof_book_uses_autoreadonly (const QofBook *book)
963 {
964  g_assert(book);
965  return (qof_book_get_num_days_autoreadonly(book) != 0);
966 }
967 
968 gint qof_book_get_num_days_autoreadonly (const QofBook *book)
969 {
970  g_assert(book);
971 
972  if (!book->cached_num_days_autoreadonly_isvalid)
973  {
974  double tmp;
975 
976  // No cached value? Then do the expensive KVP lookup
977  qof_instance_get (QOF_INSTANCE (book),
978  PARAM_NAME_NUM_AUTOREAD_ONLY, &tmp,
979  nullptr);
980 
981  const_cast<QofBook*>(book)->cached_num_days_autoreadonly = tmp;
982  const_cast<QofBook*>(book)->cached_num_days_autoreadonly_isvalid = TRUE;
983  }
984  // Value is cached now. Use the cheap variable returning.
985  return (gint) book->cached_num_days_autoreadonly;
986 }
987 
988 GDate* qof_book_get_autoreadonly_gdate (const QofBook *book)
989 {
990  gint num_days;
991  GDate* result = nullptr;
992 
993  g_assert(book);
994  num_days = qof_book_get_num_days_autoreadonly(book);
995  if (num_days > 0)
996  {
997  result = gnc_g_date_new_today();
998  g_date_subtract_days(result, num_days);
999  }
1000  return result;
1001 }
1002 
1003 // The callback that is called when the KVP option value of
1004 // "autoreadonly-days" changes, so that we mark the cached value as
1005 // invalid.
1006 static void
1007 qof_book_option_num_autoreadonly_changed_cb (GObject *gobject,
1008  GParamSpec *pspec,
1009  gpointer user_data)
1010 {
1011  QofBook *book = reinterpret_cast<QofBook*>(user_data);
1012  g_return_if_fail(QOF_IS_BOOK(book));
1013  book->cached_num_days_autoreadonly_isvalid = FALSE;
1014 }
1015 
1016 static KvpValue*
1017 get_option_default_invoice_report_value (QofBook *book)
1018 {
1019  KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE(book));
1020  return root->get_slot ({KVP_OPTION_PATH,
1021  OPTION_SECTION_BUSINESS,
1022  OPTION_NAME_DEFAULT_INVOICE_REPORT});
1023 }
1024 
1025 void
1026 qof_book_set_default_invoice_report (QofBook *book, const gchar *guid,
1027  const gchar *name)
1028 {
1029  const gchar *existing_guid_name = nullptr;
1030  gchar *new_guid_name;
1031 
1032  if (!book)
1033  {
1034  PWARN ("No book!!!");
1035  return;
1036  }
1037 
1038  if (!guid)
1039  {
1040  PWARN ("No guid!!!");
1041  return;
1042  }
1043 
1044  if (!name)
1045  {
1046  PWARN ("No name!!!");
1047  return;
1048  }
1049 
1050  KvpValue *value = get_option_default_invoice_report_value (book);
1051 
1052  if (value)
1053  existing_guid_name = {value->get<const char*>()};
1054 
1055  new_guid_name = g_strconcat (guid, "/", name, nullptr);
1056 
1057  if (g_strcmp0 (existing_guid_name, new_guid_name) != 0)
1058  {
1059  auto value = new KvpValue {g_strdup(new_guid_name)};
1060  KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE(book));
1061  qof_book_begin_edit (book);
1062  delete root->set_path ({KVP_OPTION_PATH,
1063  OPTION_SECTION_BUSINESS,
1064  OPTION_NAME_DEFAULT_INVOICE_REPORT}, value);
1065  qof_instance_set_dirty (QOF_INSTANCE(book));
1066  qof_book_commit_edit (book);
1067  }
1068  g_free (new_guid_name);
1069 }
1070 
1071 gchar *
1073 {
1074  gchar *report_guid = nullptr;
1075 
1076  if (!book)
1077  {
1078  PWARN ("No book!!!");
1079  return report_guid;
1080  }
1081 
1082  KvpValue *value = get_option_default_invoice_report_value (const_cast<QofBook*>(book));
1083 
1084  if (value)
1085  {
1086  auto str {value->get<const char*>()};
1087  auto ptr = strchr (str, '/');
1088  if (ptr)
1089  {
1090  if (ptr - str == GUID_ENCODING_LENGTH)
1091  {
1092  if (strlen (str) > GUID_ENCODING_LENGTH)
1093  report_guid = g_strndup (&str[0], GUID_ENCODING_LENGTH);
1094  }
1095  }
1096  }
1097  return report_guid;
1098 }
1099 
1100 gchar *
1102 {
1103  gchar *report_name = nullptr;
1104 
1105  if (!book)
1106  {
1107  PWARN ("No book!!!");
1108  return report_name;
1109  }
1110 
1111  KvpValue *value = get_option_default_invoice_report_value (const_cast<QofBook*>(book));
1112 
1113  if (value)
1114  {
1115  auto str {value->get<const char*>()};
1116  auto ptr = strchr (str, '/');
1117  if (ptr)
1118  {
1119  if (ptr - str == GUID_ENCODING_LENGTH)
1120  {
1121  if (strlen (str) > GUID_ENCODING_LENGTH + 1)
1122  report_name = g_strdup (&str[GUID_ENCODING_LENGTH + 1]);
1123  else
1124  report_name = g_strdup ("");
1125  }
1126  }
1127  }
1128  return report_name;
1129 }
1130 
1131 gdouble
1133 {
1134  double ret = 0;
1135 
1136  if (!book)
1137  {
1138  PWARN ("No book!!!");
1139  return ret;
1140  }
1141 
1142  KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE(book));
1143  KvpValue *value = root->get_slot ({KVP_OPTION_PATH,
1144  OPTION_SECTION_BUSINESS,
1145  OPTION_NAME_DEFAULT_INVOICE_REPORT_TIMEOUT});
1146 
1147  if (value)
1148  ret = {value->get<double>()};
1149 
1150  return ret;
1151 }
1152 
1153 /* Note: this will fail if the book slots we're looking for here are flattened at some point !
1154  * When that happens, this function can be removed. */
1155 static Path opt_name_to_path (const char* opt_name)
1156 {
1157  Path result;
1158  g_return_val_if_fail (opt_name, result);
1159  auto opt_name_list = g_strsplit(opt_name, "/", -1);
1160  for (int i=0; opt_name_list[i]; i++)
1161  result.push_back (opt_name_list[i]);
1162  g_strfreev (opt_name_list);
1163  return result;
1164 }
1165 
1166 const char*
1167 qof_book_get_string_option(const QofBook* book, const char* opt_name)
1168 {
1169  auto slot = qof_instance_get_slots(QOF_INSTANCE (book))->get_slot(opt_name_to_path(opt_name));
1170  if (slot == nullptr)
1171  return nullptr;
1172  return slot->get<const char*>();
1173 }
1174 
1175 void
1176 qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_val)
1177 {
1178  qof_book_begin_edit(book);
1179  auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
1180  auto opt_path = opt_name_to_path(opt_name);
1181  if (opt_val && (*opt_val != '\0'))
1182  delete frame->set_path(opt_path, new KvpValue(g_strdup(opt_val)));
1183  else
1184  delete frame->set_path(opt_path, nullptr);
1185  qof_instance_set_dirty (QOF_INSTANCE (book));
1186  qof_book_commit_edit(book);
1187 }
1188 
1189 const GncGUID*
1190 qof_book_get_guid_option(QofBook* book, GSList* path)
1191 {
1192  g_return_val_if_fail(book != nullptr, nullptr);
1193  g_return_val_if_fail(path != nullptr, nullptr);
1194 
1195  auto table_value = qof_book_get_option(book, path);
1196  if (!table_value)
1197  return nullptr;
1198  return table_value->get<GncGUID*>();
1199 }
1200 
1201 void
1202 qof_book_option_frame_delete (QofBook *book, const char* opt_name)
1203 {
1204  if (opt_name && (*opt_name != '\0'))
1205  {
1206  qof_book_begin_edit(book);
1207  auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
1208  auto opt_path = opt_name_to_path(opt_name);
1209  delete frame->set_path(opt_path, nullptr);
1210  qof_instance_set_dirty (QOF_INSTANCE (book));
1211  qof_book_commit_edit(book);
1212  }
1213 }
1214 
1215 void
1216 qof_book_begin_edit (QofBook *book)
1217 {
1218  qof_begin_edit(&book->inst);
1219 }
1220 
1221 static void commit_err (G_GNUC_UNUSED QofInstance *inst, QofBackendError errcode)
1222 {
1223  PERR ("Failed to commit: %d", errcode);
1224 // gnc_engine_signal_commit_error( errcode );
1225 }
1226 
1227 #define GNC_FEATURES "features"
1228 static void
1229 add_feature_to_hash (const gchar *key, KvpValue *value, GHashTable * user_data)
1230 {
1231  gchar *descr = g_strdup(value->get<const char*>());
1232  g_hash_table_insert (user_data, (gchar*)key, descr);
1233 }
1234 
1235 GHashTable *
1236 qof_book_get_features (QofBook *book)
1237 {
1238  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1239  GHashTable *features = g_hash_table_new_full (g_str_hash, g_str_equal,
1240  nullptr, g_free);
1241 
1242  PWARN ("qof_book_get_features is now deprecated.");
1243 
1244  auto slot = frame->get_slot({GNC_FEATURES});
1245  if (slot != nullptr)
1246  {
1247  frame = slot->get<KvpFrame*>();
1248  frame->for_each_slot_temp(&add_feature_to_hash, features);
1249  }
1250  return features;
1251 }
1252 
1253 void
1254 qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr)
1255 {
1256  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1257  KvpValue* feature = nullptr;
1258  auto feature_slot = frame->get_slot({GNC_FEATURES});
1259  if (feature_slot)
1260  {
1261  auto feature_frame = feature_slot->get<KvpFrame*>();
1262  feature = feature_frame->get_slot({key});
1263  }
1264  if (feature == nullptr || g_strcmp0 (feature->get<const char*>(), descr))
1265  {
1266  qof_book_begin_edit (book);
1267  delete frame->set_path({GNC_FEATURES, key}, new KvpValue(g_strdup (descr)));
1268  qof_instance_set_dirty (QOF_INSTANCE (book));
1269  qof_book_commit_edit (book);
1270  }
1271 }
1272 
1273 FeatureSet
1274 qof_book_get_unknown_features (QofBook *book, const FeaturesTable& features)
1275 {
1276  FeatureSet rv;
1277  auto test_feature = [&](const KvpFrameImpl::map_type::value_type& feature)
1278  {
1279  if (features.find (feature.first) == features.end ())
1280  rv.emplace_back (feature.first, feature.second->get<const char*>());
1281  };
1282  auto frame = qof_instance_get_slots (QOF_INSTANCE (book));
1283  auto slot = frame->get_slot({GNC_FEATURES});
1284  if (slot != nullptr)
1285  {
1286  frame = slot->get<KvpFrame*>();
1287  std::for_each (frame->begin (), frame->end (), test_feature);
1288  }
1289  return rv;
1290 }
1291 
1292 bool
1293 qof_book_test_feature (QofBook *book, const char *feature)
1294 {
1295  auto frame = qof_instance_get_slots (QOF_INSTANCE (book));
1296  return (frame->get_slot({GNC_FEATURES, feature}) != nullptr);
1297 }
1298 
1299 void
1300 qof_book_unset_feature (QofBook *book, const gchar *key)
1301 {
1302  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1303  auto feature_slot = frame->get_slot({GNC_FEATURES, key});
1304  if (!feature_slot)
1305  {
1306  PWARN ("no feature %s. bail out.", key);
1307  return;
1308  }
1309  qof_book_begin_edit (book);
1310  delete frame->set_path({GNC_FEATURES, key}, nullptr);
1311  qof_instance_set_dirty (QOF_INSTANCE (book));
1312  qof_book_commit_edit (book);
1313 }
1314 
1315 void
1316 qof_book_load_options (QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb)
1317 {
1318  load_cb (odb, book);
1319 }
1320 
1321 void
1322 qof_book_save_options (QofBook *book, GncOptionSave save_cb,
1323  GncOptionDB* odb, gboolean clear)
1324 {
1325  /* Wrap this in begin/commit so that it commits only once instead of doing
1326  * so for every option. Qof_book_set_option will take care of dirtying the
1327  * book.
1328  */
1329  qof_book_begin_edit (book);
1330  save_cb (odb, book, clear);
1331  qof_book_commit_edit (book);
1332 }
1333 
1334 static void noop (QofInstance *inst) {}
1335 
1336 void
1337 qof_book_commit_edit(QofBook *book)
1338 {
1339  if (!qof_commit_edit (QOF_INSTANCE(book))) return;
1340  qof_commit_edit_part2 (&book->inst, commit_err, noop, noop/*lot_free*/);
1341 }
1342 
1343 /* Deal with the fact that some options are not in the "options" tree but rather
1344  * in the "counters" or "counter_formats" tree */
1345 static Path gslist_to_option_path (GSList *gspath)
1346 {
1347  Path tmp_path;
1348  if (!gspath) return tmp_path;
1349 
1350  Path path_v {str_KVP_OPTION_PATH};
1351  for (auto item = gspath; item != nullptr; item = g_slist_next(item))
1352  tmp_path.push_back(static_cast<const char*>(item->data));
1353  if ((tmp_path.front() == "counters") || (tmp_path.front() == "counter_formats"))
1354  return tmp_path;
1355 
1356  path_v.insert(path_v.end(), tmp_path.begin(), tmp_path.end());
1357  return path_v;
1358 }
1359 
1360 void
1361 qof_book_set_option (QofBook *book, KvpValue *value, GSList *path)
1362 {
1363  KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE (book));
1364  qof_book_begin_edit (book);
1365  delete root->set_path(gslist_to_option_path(path), value);
1366  qof_instance_set_dirty (QOF_INSTANCE (book));
1367  qof_book_commit_edit (book);
1368 
1369  // Also, mark any cached value as invalid
1370  book->cached_num_field_source_isvalid = FALSE;
1371 }
1372 
1373 KvpValue*
1374 qof_book_get_option (QofBook *book, GSList *path)
1375 {
1376  KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
1377  return root->get_slot(gslist_to_option_path(path));
1378 }
1379 
1380 void
1381 qof_book_options_delete (QofBook *book, GSList *path)
1382 {
1383  KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
1384  if (path != nullptr)
1385  {
1386  Path path_v {str_KVP_OPTION_PATH};
1387  Path tmp_path;
1388  for (auto item = path; item != nullptr; item = g_slist_next(item))
1389  tmp_path.push_back(static_cast<const char*>(item->data));
1390  delete root->set_path(gslist_to_option_path(path), nullptr);
1391  }
1392  else
1393  delete root->set_path({str_KVP_OPTION_PATH}, nullptr);
1394 }
1395 
1396 /* QofObject function implementation and registration */
1397 gboolean qof_book_register (void)
1398 {
1399  static QofParam params[] =
1400  {
1401  { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_entity_get_guid, nullptr },
1402  { QOF_PARAM_KVP, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, nullptr },
1403  { nullptr },
1404  };
1405 
1406  qof_class_register (QOF_ID_BOOK, nullptr, params);
1407 
1408  return TRUE;
1409 }
1410 
1411 /* ========================== 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:1316
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:1374
void(* QofCollectionForeachCB)(QofCollection *, gpointer user_data)
Invoke the indicated callback on each collection in the book.
Definition: qofbook.h:238
gboolean qof_book_register(void)
Register the book object with the QOF object system.
Definition: qofbook.cpp:1397
#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:1132
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:625
gint qof_book_get_num_days_autoreadonly(const QofBook *book)
Returns the number of days for auto-read-only transactions.
Definition: qofbook.cpp:968
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:921
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:686
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:1236
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:580
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:1322
GDate * qof_book_get_autoreadonly_gdate(const QofBook *book)
Returns the GDate that is the threshold for auto-read-only.
Definition: qofbook.cpp:988
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:177
gchar * qof_book_normalize_counter_format(const gchar *p, gchar **err_msg)
Validate a counter format string.
Definition: qofbook.cpp:741
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:773
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:113
void qof_collection_destroy(QofCollection *col)
destroy the collection
Definition: qofid.cpp:59
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
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:1026
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:1101
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:261
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:289
void qof_book_set_option(QofBook *book, KvpValue *value, GSList *path)
Save a single option value.
Definition: qofbook.cpp:1361
gboolean qof_book_uses_autoreadonly(const QofBook *book)
Returns TRUE if the auto-read-only feature should be used, otherwise FALSE.
Definition: qofbook.cpp:962
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:1381
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:1072
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:909
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:1224