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