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