GnuCash  4.12-75-g66025ae738+
gnc-budget.cpp
1 /********************************************************************\
2  * gnc-budget.c -- Implementation of the top level Budgeting API. *
3  * Copyright (C) 04 sep 2003 Darin Willits <darin@willits.ca> *
4  * Copyright (C) 2005-2006 Chris Shoemaker <c.shoemaker@cox.net> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23 \********************************************************************/
24 
25 #include <config.h>
26 #include <qof.h>
27 #include <qofbookslots.h>
28 #include <qofinstance-p.h>
29 #include <unordered_map>
30 #include <vector>
31 #include <memory>
32 
33 #include "Account.h"
34 
35 #include "guid.hpp"
36 #include "gnc-budget.h"
37 #include "gnc-commodity.h"
38 
39 static QofLogModule log_module = GNC_MOD_ENGINE;
40 
41 enum
42 {
43  PROP_0,
44  PROP_NAME, /* Table */
45  PROP_DESCRIPTION, /* Table */
46  PROP_NUM_PERIODS, /* Table */
47  PROP_RUNTIME_0,
48  PROP_RECURRENCE, /* Cached pointer; Recurrence table holds budget guid */
49 };
50 
51 struct budget_s
52 {
53  QofInstance inst;
54 };
55 
56 typedef struct
57 {
58  QofInstanceClass parent_class;
59 } BudgetClass;
60 
61 struct PeriodData
62 {
63  std::string note;
64  bool value_is_set;
65  gnc_numeric value;
66  PeriodData () = default;
67  PeriodData (const char* note, bool value_is_set, gnc_numeric value)
68  : note (note)
69  , value_is_set (value_is_set)
70  , value (value) {};
71 };
72 
73 using PeriodDataVec = std::vector<PeriodData>;
74 using AcctMap = std::unordered_map<const Account*, PeriodDataVec>;
75 using StringVec = std::vector<std::string>;
76 
77 typedef struct GncBudgetPrivate
78 {
79  /* The name is an arbitrary string assigned by the user. */
80  const gchar *name;
81 
82  /* The description is an arbitrary string assigned by the user. */
83  const gchar *description;
84 
85  /* Recurrence (period info) for the budget */
86  Recurrence recurrence;
87 
88  std::unique_ptr<AcctMap> acct_map;
89 
90  /* Number of periods */
91  guint num_periods;
93 
94 #define GET_PRIVATE(o) \
95  ((GncBudgetPrivate*)gnc_budget_get_instance_private((GncBudget*)o))
96 
98 {
99  QofInstanceClass parent_class;
100 };
101 
102 /* GObject Initialization */
103 G_DEFINE_TYPE_WITH_PRIVATE(GncBudget, gnc_budget, QOF_TYPE_INSTANCE)
104 
105 static void
106 gnc_budget_init(GncBudget* budget)
107 {
108  GncBudgetPrivate* priv;
109  GDate *date;
110 
111  priv = GET_PRIVATE(budget);
112  priv->name = CACHE_INSERT(_("Unnamed Budget"));
113  priv->description = CACHE_INSERT("");
114  priv->acct_map = std::make_unique<AcctMap>();
115 
116  priv->num_periods = 12;
117  date = gnc_g_date_new_today ();
118  g_date_subtract_days(date, g_date_get_day(date) - 1);
119  recurrenceSet(&priv->recurrence, 1, PERIOD_MONTH, date, WEEKEND_ADJ_NONE);
120  g_date_free (date);
121 }
122 
123 static void
124 gnc_budget_dispose (GObject *budgetp)
125 {
126  G_OBJECT_CLASS(gnc_budget_parent_class)->dispose(budgetp);
127 }
128 
129 static void
130 gnc_budget_finalize(GObject* budgetp)
131 {
132  G_OBJECT_CLASS(gnc_budget_parent_class)->finalize(budgetp);
133 }
134 
135 static void
136 gnc_budget_get_property( GObject* object,
137  guint prop_id,
138  GValue* value,
139  GParamSpec* pspec)
140 {
141  GncBudget* budget;
142  GncBudgetPrivate* priv;
143 
144  g_return_if_fail(GNC_IS_BUDGET(object));
145 
146  budget = GNC_BUDGET(object);
147  priv = GET_PRIVATE(budget);
148  switch ( prop_id )
149  {
150  case PROP_NAME:
151  g_value_set_string(value, priv->name);
152  break;
153  case PROP_DESCRIPTION:
154  g_value_set_string(value, priv->description);
155  break;
156  case PROP_NUM_PERIODS:
157  g_value_set_uint(value, priv->num_periods);
158  break;
159  case PROP_RECURRENCE:
160  /* TODO: Make this a BOXED type */
161  g_value_set_pointer(value, &priv->recurrence);
162  break;
163  default:
164  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
165  break;
166  }
167 }
168 
169 static void
170 gnc_budget_set_property( GObject* object,
171  guint prop_id,
172  const GValue* value,
173  GParamSpec* pspec)
174 {
175  GncBudget* budget;
176 
177  g_return_if_fail(GNC_IS_BUDGET(object));
178 
179  budget = GNC_BUDGET(object);
180  if (prop_id < PROP_RUNTIME_0)
181  g_assert (qof_instance_get_editlevel(budget));
182 
183  switch ( prop_id )
184  {
185  case PROP_NAME:
186  gnc_budget_set_name(budget, g_value_get_string(value));
187  break;
188  case PROP_DESCRIPTION:
189  gnc_budget_set_description(budget, g_value_get_string(value));
190  break;
191  case PROP_NUM_PERIODS:
192  gnc_budget_set_num_periods(budget, g_value_get_uint(value));
193  break;
194  case PROP_RECURRENCE:
195  gnc_budget_set_recurrence (budget, static_cast<Recurrence*>(g_value_get_pointer(value)));
196  break;
197  default:
198  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
199  break;
200  }
201 }
202 
203 static void
204 gnc_budget_class_init(GncBudgetClass* klass)
205 {
206  GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
207 
208  gobject_class->dispose = gnc_budget_dispose;
209  gobject_class->finalize = gnc_budget_finalize;
210  gobject_class->get_property = gnc_budget_get_property;
211  gobject_class->set_property = gnc_budget_set_property;
212 
213  g_object_class_install_property(
214  gobject_class,
215  PROP_NAME,
216  g_param_spec_string( "name",
217  "Budget Name",
218  "The name is an arbitrary string "
219  "assigned by the user. It is intended "
220  "to be a short, 5 to 30 character long string "
221  "that is displayed by the GUI as the "
222  "budget mnemonic",
223  NULL,
224  G_PARAM_READWRITE));
225 
226  g_object_class_install_property(
227  gobject_class,
228  PROP_DESCRIPTION,
229  g_param_spec_string( "description",
230  "Budget Description",
231  "The description is an arbitrary string "
232  "assigned by the user. It is intended "
233  "to be a longer, 1-5 sentence description of "
234  "what the budget is all about.",
235  NULL,
236  G_PARAM_READWRITE));
237 
238  g_object_class_install_property(
239  gobject_class,
240  PROP_NUM_PERIODS,
241  g_param_spec_uint( "num-periods",
242  "Number of Periods",
243  "The number of periods for this budget.",
244  0,
245  G_MAXUINT32,
246  12,
247  G_PARAM_READWRITE));
248 
249  g_object_class_install_property(
250  gobject_class,
251  PROP_RECURRENCE,
252  g_param_spec_pointer( "recurrence",
253  "Budget Recurrence",
254  "about.",
255  G_PARAM_READWRITE));
256 }
257 
258 static void commit_err (QofInstance *inst, QofBackendError errcode)
259 {
260  PERR ("Failed to commit: %d", errcode);
261  gnc_engine_signal_commit_error( errcode );
262 }
263 
264 static void
265 gnc_budget_free(QofInstance *inst)
266 {
267  GncBudget *budget;
268  GncBudgetPrivate* priv;
269 
270  if (inst == NULL)
271  return;
272  g_return_if_fail(GNC_IS_BUDGET(inst));
273 
274  budget = GNC_BUDGET(inst);
275  priv = GET_PRIVATE(budget);
276 
277  /* We first send the message that this object is about to be
278  * destroyed so that any GUI elements can remove it before it is
279  * actually gone. */
280  qof_event_gen( &budget->inst, QOF_EVENT_DESTROY, NULL);
281 
282  CACHE_REMOVE(priv->name);
283  CACHE_REMOVE(priv->description);
284  priv->acct_map = nullptr; // nullify to ensure unique_ptr is freed.
285 
286  /* qof_instance_release (&budget->inst); */
287  g_object_unref(budget);
288 }
289 
290 static void noop (QofInstance *inst) {}
291 
292 void
293 gnc_budget_begin_edit(GncBudget *bgt)
294 {
295  qof_begin_edit(QOF_INSTANCE(bgt));
296 }
297 
298 void
299 gnc_budget_commit_edit(GncBudget *bgt)
300 {
301  if (!qof_commit_edit(QOF_INSTANCE(bgt))) return;
302  qof_commit_edit_part2(QOF_INSTANCE(bgt), commit_err,
303  noop, gnc_budget_free);
304 }
305 
306 GncBudget*
307 gnc_budget_new(QofBook *book)
308 {
309  g_return_val_if_fail(book, NULL);
310 
311  ENTER(" ");
312 
313  auto budget { static_cast<GncBudget*>(g_object_new(GNC_TYPE_BUDGET, nullptr)) };
314  qof_instance_init_data (&budget->inst, GNC_ID_BUDGET, book);
315 
316  qof_event_gen( &budget->inst, QOF_EVENT_CREATE , NULL);
317 
318  LEAVE(" ");
319  return budget;
320 }
321 
322 void
323 gnc_budget_destroy(GncBudget *budget)
324 {
325  g_return_if_fail(GNC_IS_BUDGET(budget));
326  gnc_budget_begin_edit(budget);
327  qof_instance_set_dirty(&budget->inst);
328  qof_instance_set_destroying(budget, TRUE);
329  gnc_budget_commit_edit(budget);
330 }
331 
333 typedef struct
334 {
335  const GncBudget* old_b;
336  GncBudget* new_b;
337  guint num_periods;
339 
340 static void
341 clone_budget_values_cb(Account* a, gpointer user_data)
342 {
343  CloneBudgetData_t* data = (CloneBudgetData_t*)user_data;
344  guint i;
345 
346  for ( i = 0; i < data->num_periods; ++i )
347  {
348  if ( gnc_budget_is_account_period_value_set(data->old_b, a, i) )
349  {
350  gnc_budget_set_account_period_value(data->new_b, a, i,
351  gnc_budget_get_account_period_value(data->old_b, a, i));
352  }
353  }
354 }
355 
356 GncBudget*
357 gnc_budget_clone(const GncBudget* old_b)
358 {
359  GncBudget* new_b;
360  Account* root;
361  CloneBudgetData_t clone_data;
362 
363  g_return_val_if_fail(old_b != NULL, NULL);
364 
365  ENTER(" ");
366 
367  new_b = gnc_budget_new(qof_instance_get_book(old_b));
368  gnc_budget_begin_edit(new_b);
369  gnc_budget_set_name(new_b, gnc_budget_get_name(old_b));
370  gnc_budget_set_description(new_b, gnc_budget_get_description(old_b));
371  gnc_budget_set_recurrence(new_b, gnc_budget_get_recurrence(old_b));
372  gnc_budget_set_num_periods(new_b, gnc_budget_get_num_periods(old_b));
373 
374  root = gnc_book_get_root_account(qof_instance_get_book(old_b));
375  clone_data.old_b = old_b;
376  clone_data.new_b = new_b;
377  clone_data.num_periods = gnc_budget_get_num_periods(new_b);
378  gnc_account_foreach_descendant(root, clone_budget_values_cb, &clone_data);
379 
380  gnc_budget_commit_edit(new_b);
381 
382  LEAVE(" ");
383 
384  return new_b;
385 }
386 
387 void
388 gnc_budget_set_name(GncBudget* budget, const gchar* name)
389 {
390  GncBudgetPrivate* priv;
391 
392  g_return_if_fail(GNC_IS_BUDGET(budget) && name);
393 
394  priv = GET_PRIVATE(budget);
395  if ( name == priv->name ) return;
396 
397  gnc_budget_begin_edit(budget);
398  CACHE_REPLACE(priv->name, name);
399  qof_instance_set_dirty(&budget->inst);
400  gnc_budget_commit_edit(budget);
401 
402  qof_event_gen( &budget->inst, QOF_EVENT_MODIFY, NULL);
403 }
404 
405 const gchar*
406 gnc_budget_get_name(const GncBudget* budget)
407 {
408  g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
409  return GET_PRIVATE(budget)->name;
410 }
411 
412 void
413 gnc_budget_set_description(GncBudget* budget, const gchar* description)
414 {
415  GncBudgetPrivate* priv;
416 
417  g_return_if_fail(GNC_IS_BUDGET(budget));
418  g_return_if_fail(description);
419 
420  priv = GET_PRIVATE(budget);
421  if ( description == priv->description ) return;
422  gnc_budget_begin_edit(budget);
423  CACHE_REPLACE(priv->description, description);
424  qof_instance_set_dirty(&budget->inst);
425  gnc_budget_commit_edit(budget);
426 
427  qof_event_gen( &budget->inst, QOF_EVENT_MODIFY, NULL);
428 }
429 
430 const gchar*
431 gnc_budget_get_description(const GncBudget* budget)
432 {
433  g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
434  return GET_PRIVATE(budget)->description;
435 }
436 
437 void
438 gnc_budget_set_recurrence(GncBudget *budget, const Recurrence *r)
439 {
440  GncBudgetPrivate* priv;
441 
442  g_return_if_fail(budget && r);
443  priv = GET_PRIVATE(budget);
444 
445  gnc_budget_begin_edit(budget);
446  priv->recurrence = *r;
447  qof_instance_set_dirty(&budget->inst);
448  gnc_budget_commit_edit(budget);
449 
450  qof_event_gen(&budget->inst, QOF_EVENT_MODIFY, NULL);
451 }
452 
453 const Recurrence *
454 gnc_budget_get_recurrence(const GncBudget *budget)
455 {
456  g_return_val_if_fail(budget, NULL);
457  return (&GET_PRIVATE(budget)->recurrence);
458 }
459 
460 const GncGUID*
461 gnc_budget_get_guid(const GncBudget* budget)
462 {
463  g_return_val_if_fail(budget, NULL);
464  g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
465  return qof_instance_get_guid(QOF_INSTANCE(budget));
466 }
467 
468 void
469 gnc_budget_set_num_periods(GncBudget* budget, guint num_periods)
470 {
471  GncBudgetPrivate* priv;
472 
473  g_return_if_fail(GNC_IS_BUDGET(budget));
474  g_return_if_fail(num_periods > 0);
475 
476  priv = GET_PRIVATE(budget);
477  if ( priv->num_periods == num_periods ) return;
478 
479  gnc_budget_begin_edit(budget);
480  priv->num_periods = num_periods;
481  std::for_each (priv->acct_map->begin(),
482  priv->acct_map->end(),
483  [num_periods](auto& it)
484  {
485  it.second.resize(num_periods);
486  });
487  qof_instance_set_dirty(&budget->inst);
488  gnc_budget_commit_edit(budget);
489 
490  qof_event_gen( &budget->inst, QOF_EVENT_MODIFY, NULL);
491 }
492 
493 guint
494 gnc_budget_get_num_periods(const GncBudget* budget)
495 {
496  g_return_val_if_fail(GNC_IS_BUDGET(budget), 0);
497  return GET_PRIVATE(budget)->num_periods;
498 }
499 
500 static inline StringVec
501 make_period_data_path (const Account *account, guint period_num)
502 {
503  gnc::GUID acct_guid {*(xaccAccountGetGUID (account))};
504  return { acct_guid.to_string(), std::to_string (period_num) };
505 }
506 
507 static inline StringVec
508 make_period_note_path (const Account *account, guint period_num)
509 {
510  StringVec path { GNC_BUDGET_NOTES_PATH };
511  StringVec data_path { make_period_data_path (account, period_num) };
512  std::move (data_path.begin(), data_path.end(), std::back_inserter (path));
513  return path;
514 }
515 
516 static PeriodData& get_perioddata (const GncBudget *budget,
517  const Account *account,
518  guint period_num);
519 
520 /* period_num is zero-based */
521 /* What happens when account is deleted, after we have an entry for it? */
522 void
523 gnc_budget_unset_account_period_value(GncBudget *budget, const Account *account,
524  guint period_num)
525 {
526  g_return_if_fail (budget != NULL);
527  g_return_if_fail (account != NULL);
528  g_return_if_fail (period_num < GET_PRIVATE(budget)->num_periods);
529 
530  auto& data = get_perioddata (budget, account, period_num);
531  data.value_is_set = false;
532 
533  gnc_budget_begin_edit(budget);
534  auto path = make_period_data_path (account, period_num);
535  auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
536  delete budget_kvp->set_path (path, nullptr);
537  qof_instance_set_dirty(&budget->inst);
538  gnc_budget_commit_edit(budget);
539 
540  qof_event_gen( &budget->inst, QOF_EVENT_MODIFY, NULL);
541 
542 }
543 
544 /* period_num is zero-based */
545 /* What happens when account is deleted, after we have an entry for it? */
546 void
547 gnc_budget_set_account_period_value(GncBudget *budget, const Account *account,
548  guint period_num, gnc_numeric val)
549 {
550  /* Watch out for an off-by-one error here:
551  * period_num starts from 0 while num_periods starts from 1 */
552  if (period_num >= GET_PRIVATE(budget)->num_periods)
553  {
554  PWARN("Period %i does not exist", period_num);
555  return;
556  }
557 
558  g_return_if_fail (budget != NULL);
559  g_return_if_fail (account != NULL);
560 
561  auto& perioddata = get_perioddata (budget, account, period_num);
562  auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
563  auto path = make_period_data_path (account, period_num);
564 
565  gnc_budget_begin_edit(budget);
566  if (gnc_numeric_check(val))
567  {
568  delete budget_kvp->set_path (path, nullptr);
569  perioddata.value_is_set = false;
570  }
571  else
572  {
573  KvpValue* v = new KvpValue (val);
574  delete budget_kvp->set_path (path, v);
575  perioddata.value_is_set = true;
576  perioddata.value = val;
577  }
578  qof_instance_set_dirty(&budget->inst);
579  gnc_budget_commit_edit(budget);
580 
581  qof_event_gen( &budget->inst, QOF_EVENT_MODIFY, NULL);
582 
583 }
584 
585 gboolean
586 gnc_budget_is_account_period_value_set (const GncBudget *budget,
587  const Account *account,
588  guint period_num)
589 {
590  g_return_val_if_fail (period_num < GET_PRIVATE(budget)->num_periods, false);
591  return get_perioddata (budget, account, period_num).value_is_set;
592 }
593 
594 gnc_numeric
595 gnc_budget_get_account_period_value (const GncBudget *budget,
596  const Account *account,
597  guint period_num)
598 {
599  g_return_val_if_fail (period_num < GET_PRIVATE(budget)->num_periods,
600  gnc_numeric_zero());
601  auto& data = get_perioddata (budget, account, period_num);
602  if (!data.value_is_set)
603  return gnc_numeric_zero();
604 
605  return data.value;
606 }
607 
608 void
609 gnc_budget_set_account_period_note(GncBudget *budget, const Account *account,
610  guint period_num, const gchar *note)
611 {
612  /* Watch out for an off-by-one error here:
613  * period_num starts from 0 while num_periods starts from 1 */
614  if (period_num >= GET_PRIVATE(budget)->num_periods)
615  {
616  PWARN("Period %i does not exist", period_num);
617  return;
618  }
619 
620  g_return_if_fail (budget != NULL);
621  g_return_if_fail (account != NULL);
622 
623  auto& perioddata = get_perioddata (budget, account, period_num);
624  auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
625  auto path = make_period_note_path (account, period_num);
626 
627  gnc_budget_begin_edit(budget);
628  if (note == NULL)
629  {
630  delete budget_kvp->set_path (path, nullptr);
631  perioddata.note.clear ();
632  }
633  else
634  {
635  KvpValue* v = new KvpValue (g_strdup (note));
636 
637  delete budget_kvp->set_path (path, v);
638  perioddata.note = note;
639  }
640  qof_instance_set_dirty(&budget->inst);
641  gnc_budget_commit_edit(budget);
642 
643  qof_event_gen( &budget->inst, QOF_EVENT_MODIFY, NULL);
644 
645 }
646 
647 const gchar *
648 gnc_budget_get_account_period_note (const GncBudget *budget,
649  const Account *account, guint period_num)
650 {
651  g_return_val_if_fail (period_num < GET_PRIVATE(budget)->num_periods, nullptr);
652  auto& data = get_perioddata (budget, account, period_num);
653  return data.note.empty () ? nullptr : data.note.c_str();
654 }
655 
656 time64
657 gnc_budget_get_period_start_date(const GncBudget *budget, guint period_num)
658 {
659  g_return_val_if_fail (GNC_IS_BUDGET(budget), 0);
660  return recurrenceGetPeriodTime(&GET_PRIVATE(budget)->recurrence, period_num, FALSE);
661 }
662 
663 time64
664 gnc_budget_get_period_end_date(const GncBudget *budget, guint period_num)
665 {
666  g_return_val_if_fail (GNC_IS_BUDGET(budget), 0);
667  return recurrenceGetPeriodTime(&GET_PRIVATE(budget)->recurrence, period_num, TRUE);
668 }
669 
670 gnc_numeric
671 gnc_budget_get_account_period_actual_value(
672  const GncBudget *budget, Account *acc, guint period_num)
673 {
674  // FIXME: maybe zero is not best error return val.
675  g_return_val_if_fail(GNC_IS_BUDGET(budget) && acc, gnc_numeric_zero());
676  return recurrenceGetAccountPeriodValue(&GET_PRIVATE(budget)->recurrence,
677  acc, period_num);
678 }
679 
680 static PeriodData&
681 get_perioddata (const GncBudget *budget, const Account *account, guint period_num)
682 {
683  GncBudgetPrivate *priv = GET_PRIVATE (budget);
684 
685  if (period_num >= priv->num_periods)
686  throw std::out_of_range("period_num >= num_periods");
687 
688  auto& vec = priv->acct_map->operator[](account);
689 
690  if (vec.empty())
691  {
692  auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
693  vec.reserve (priv->num_periods);
694 
695  for (guint i = 0; i < priv->num_periods; i++)
696  {
697  auto kval1 { budget_kvp->get_slot (make_period_data_path (account, i)) };
698  auto kval2 { budget_kvp->get_slot (make_period_note_path (account, i)) };
699 
700  auto is_set = kval1 && kval1->get_type() == KvpValue::Type::NUMERIC;
701  auto num = is_set ? kval1->get<gnc_numeric>() : gnc_numeric_zero ();
702  auto note = (kval2 && kval2->get_type() == KvpValue::Type::STRING) ?
703  kval2->get<const char*>() : "";
704 
705  vec.emplace_back (note, is_set, num);
706  }
707  }
708 
709  return vec.at(period_num);
710 }
711 
712 GncBudget*
713 gnc_budget_lookup (const GncGUID *guid, const QofBook *book)
714 {
715  QofCollection *col;
716 
717  g_return_val_if_fail(guid, NULL);
718  g_return_val_if_fail(book, NULL);
719  col = qof_book_get_collection (book, GNC_ID_BUDGET);
720  return GNC_BUDGET(qof_collection_lookup_entity (col, guid));
721 }
722 
723 static void just_get_one(QofInstance *ent, gpointer data)
724 {
725  GncBudget **bgt = (GncBudget**)data;
726  if (bgt && !*bgt) *bgt = GNC_BUDGET(ent);
727 }
728 
729 GncBudget*
730 gnc_budget_get_default (QofBook *book)
731 {
732  QofCollection *col;
733  GncBudget *bgt = NULL;
734  GncGUID *default_budget_guid = NULL;
735 
736  g_return_val_if_fail(book, NULL);
737 
738  qof_instance_get (QOF_INSTANCE (book),
739  "default-budget", &default_budget_guid,
740  NULL);
741  if (default_budget_guid)
742  {
743  col = qof_book_get_collection(book, GNC_ID_BUDGET);
744  bgt = (GncBudget *) qof_collection_lookup_entity(col,
745  default_budget_guid);
746  }
747 
748  /* Revert to 2.2.x behavior if the book has no default budget. */
749 
750  if ( bgt == NULL )
751  {
752  col = qof_book_get_collection(book, GNC_ID_BUDGET);
753  if (qof_collection_count(col) > 0)
754  {
755  qof_collection_foreach(col, just_get_one, &bgt);
756  }
757  }
758 
759  guid_free (default_budget_guid);
760  return bgt;
761 }
762 
763 static void
764 destroy_budget_on_book_close(QofInstance *ent, gpointer data)
765 {
766  GncBudget* bgt = GNC_BUDGET(ent);
767 
768  gnc_budget_destroy(bgt);
769 }
770 
775 static void
776 gnc_budget_book_end(QofBook* book)
777 {
778  QofCollection *col;
779 
780  col = qof_book_get_collection(book, GNC_ID_BUDGET);
781  qof_collection_foreach(col, destroy_budget_on_book_close, NULL);
782 }
783 
784 #ifdef _MSC_VER
785 /* MSVC compiler doesn't have C99 "designated initializers"
786  * so we wrap them in a macro that is empty on MSVC. */
787 # define DI(x) /* */
788 #else
789 # define DI(x) x
790 #endif
791 
792 /* Define the QofObject. */
793 static QofObject budget_object_def =
794 {
795  DI(.interface_version = ) QOF_OBJECT_VERSION,
796  DI(.e_type = ) GNC_ID_BUDGET,
797  DI(.type_label = ) "Budget",
798  DI(.create = ) (void*(*)(QofBook*)) gnc_budget_new,
799  DI(.book_begin = ) NULL,
800  DI(.book_end = ) gnc_budget_book_end,
801  DI(.is_dirty = ) qof_collection_is_dirty,
802  DI(.mark_clean = ) qof_collection_mark_clean,
803  DI(.foreach = ) qof_collection_foreach,
804  DI(.printable = ) (const char * (*)(gpointer)) gnc_budget_get_name,
805  DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
806 };
807 
808 
809 /* Static wrapper getters for the recurrence params */
810 static PeriodType gnc_budget_get_rec_pt(const GncBudget *bgt)
811 {
812  return recurrenceGetPeriodType(&(GET_PRIVATE(bgt)->recurrence));
813 }
814 static guint gnc_budget_get_rec_mult(const GncBudget *bgt)
815 {
816  return recurrenceGetMultiplier(&(GET_PRIVATE(bgt)->recurrence));
817 }
818 static time64 gnc_budget_get_rec_time(const GncBudget *bgt)
819 {
820  return recurrenceGetTime(&(GET_PRIVATE(bgt)->recurrence));
821 }
822 
823 /* Register ourselves with the engine. */
824 gboolean gnc_budget_register (void)
825 {
826  static QofParam params[] =
827  {
828  {
829  "name", QOF_TYPE_STRING,
830  (QofAccessFunc) gnc_budget_get_name,
832  },
833  {
834  "description", QOF_TYPE_STRING,
835  (QofAccessFunc) gnc_budget_get_description,
837  },
838  {
839  "recurrence_period_type", QOF_TYPE_INT32,
840  (QofAccessFunc) gnc_budget_get_rec_pt, NULL
841  },
842  /* Signedness problem: Should be unsigned. */
843  {
844  "recurrence_multiplier", QOF_TYPE_INT32,
845  (QofAccessFunc) gnc_budget_get_rec_mult, NULL
846  },
847  /* This is the same way that SchedXaction.c uses QOF_TYPE_DATE
848  but I don't think QOF actually supports a GDate, so I think
849  this is wrong. */
850  {
851  "recurrence_date", QOF_TYPE_DATE,
852  (QofAccessFunc) gnc_budget_get_rec_time, NULL
853  },
854  /* Signedness problem: Should be unsigned. */
855  {
856  "num_periods", QOF_TYPE_INT32,
857  (QofAccessFunc) gnc_budget_get_num_periods,
859  },
860  {
861  QOF_PARAM_BOOK, QOF_ID_BOOK,
863  },
864  {
865  QOF_PARAM_GUID, QOF_TYPE_GUID,
867  },
868  { NULL },
869  };
870 
871  qof_class_register(GNC_ID_BUDGET, (QofSortFunc) NULL, params);
872  return qof_object_register(&budget_object_def);
873 }
void gnc_budget_set_num_periods(GncBudget *budget, guint num_periods)
Set/Get the number of periods in the Budget.
Definition: gnc-budget.cpp:469
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
void gnc_budget_destroy(GncBudget *budget)
Deletes the given budget object.
Definition: gnc-budget.cpp:323
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3245
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of &#39;dirty&#39; flag on collection.
Definition: qofid.cpp:257
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition: qofid.cpp:215
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
GnuCash Budgets.
GncBudget * gnc_budget_new(QofBook *book)
Creates and initializes a Budget.
Definition: gnc-budget.cpp:307
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
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition: qofclass.h:222
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition: qofobject.h:64
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
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition: qofquery.h:109
Data structure for containing info while cloning budget values.
Definition: gnc-budget.cpp:333
void qof_collection_foreach(const QofCollection *col, QofInstanceForeachCB cb_func, gpointer user_data)
Call the callback for each entity in the collection.
Definition: qofid.cpp:323
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:184
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
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
#define xaccAccountGetGUID(X)
Definition: Account.h:248
Account handling public routines.
The budget data.
Definition: gnc-budget.cpp:51
time64 gnc_budget_get_period_start_date(const GncBudget *budget, guint period_num)
Get the starting date of the Budget period.
Definition: gnc-budget.cpp:657
GncBudget * gnc_budget_clone(const GncBudget *old_b)
Clones a budget creating a copy.
Definition: gnc-budget.cpp:357
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
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:263
void gnc_budget_set_name(GncBudget *budget, const gchar *name)
Set/Get the name of the Budget.
Definition: gnc-budget.cpp:388
void gnc_budget_set_description(GncBudget *budget, const gchar *description)
Set/Get the description of the Budget.
Definition: gnc-budget.cpp:413
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:606
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
guint qof_collection_count(const QofCollection *col)
return the number of entities in the collection.
Definition: qofid.cpp:246
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
Definition: qofobject.cpp:317
time64 gnc_budget_get_period_end_date(const GncBudget *budget, guint period_num)
Get the ending date of the Budget period.
Definition: gnc-budget.cpp:664
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
The type used to store guids in C.
Definition: guid.h:75
Commodity handling public routines.
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).
Definition: gnc-date.cpp:1229