27 #include <qofbookslots.h> 28 #include <qofinstance-p.h> 30 #include <unordered_map> 39 static QofLogModule log_module = GNC_MOD_ENGINE;
58 QofInstanceClass parent_class;
64 std::optional<gnc_numeric> opt_value;
66 PeriodData (
const char* note, std::optional<gnc_numeric> opt_value)
68 , opt_value (opt_value) {};
71 using PeriodDataVec = std::vector<PeriodData>;
72 using AcctMap = std::unordered_map<const Account*, PeriodDataVec>;
73 using StringVec = std::vector<std::string>;
81 const gchar *description;
92 #define GET_PRIVATE(o) \ 93 ((GncBudgetPrivate*)gnc_budget_get_instance_private((GncBudget*)o)) 97 QofInstanceClass parent_class;
101 G_DEFINE_TYPE_WITH_PRIVATE(GncBudget, gnc_budget, QOF_TYPE_INSTANCE)
104 gnc_budget_init(GncBudget* budget)
109 priv = GET_PRIVATE(budget);
110 priv->name = CACHE_INSERT(_(
"Unnamed Budget"));
111 priv->description = CACHE_INSERT(
"");
112 new (&priv->acct_map) AcctMap ();
114 priv->num_periods = 12;
116 g_date_subtract_days(date, g_date_get_day(date) - 1);
117 recurrenceSet(&priv->recurrence, 1, PERIOD_MONTH, date, WEEKEND_ADJ_NONE);
122 gnc_budget_dispose (GObject *budgetp)
124 G_OBJECT_CLASS(gnc_budget_parent_class)->dispose(budgetp);
128 gnc_budget_finalize(GObject* budgetp)
130 G_OBJECT_CLASS(gnc_budget_parent_class)->finalize(budgetp);
134 gnc_budget_get_property( GObject*
object,
142 g_return_if_fail(GNC_IS_BUDGET(
object));
144 budget = GNC_BUDGET(
object);
145 priv = GET_PRIVATE(budget);
149 g_value_set_string(value, priv->name);
151 case PROP_DESCRIPTION:
152 g_value_set_string(value, priv->description);
154 case PROP_NUM_PERIODS:
155 g_value_set_uint(value, priv->num_periods);
157 case PROP_RECURRENCE:
159 g_value_set_pointer(value, &priv->recurrence);
162 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
168 gnc_budget_set_property( GObject*
object,
175 g_return_if_fail(GNC_IS_BUDGET(
object));
177 budget = GNC_BUDGET(
object);
178 if (prop_id < PROP_RUNTIME_0)
179 g_assert (qof_instance_get_editlevel(budget));
186 case PROP_DESCRIPTION:
189 case PROP_NUM_PERIODS:
192 case PROP_RECURRENCE:
193 gnc_budget_set_recurrence (budget, static_cast<Recurrence*>(g_value_get_pointer(value)));
196 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
202 gnc_budget_class_init(GncBudgetClass* klass)
204 GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
206 gobject_class->dispose = gnc_budget_dispose;
207 gobject_class->finalize = gnc_budget_finalize;
208 gobject_class->get_property = gnc_budget_get_property;
209 gobject_class->set_property = gnc_budget_set_property;
211 g_object_class_install_property(
214 g_param_spec_string(
"name",
216 "The name is an arbitrary string " 217 "assigned by the user. It is intended " 218 "to be a short, 5 to 30 character long string " 219 "that is displayed by the GUI as the " 224 g_object_class_install_property(
227 g_param_spec_string(
"description",
228 "Budget Description",
229 "The description is an arbitrary string " 230 "assigned by the user. It is intended " 231 "to be a longer, 1-5 sentence description of " 232 "what the budget is all about.",
236 g_object_class_install_property(
239 g_param_spec_uint(
"num-periods",
241 "The number of periods for this budget.",
247 g_object_class_install_property(
250 g_param_spec_pointer(
"recurrence",
258 PERR (
"Failed to commit: %d", errcode);
259 gnc_engine_signal_commit_error( errcode );
270 g_return_if_fail(GNC_IS_BUDGET(inst));
272 budget = GNC_BUDGET(inst);
273 priv = GET_PRIVATE(budget);
280 CACHE_REMOVE(priv->name);
281 CACHE_REMOVE(priv->description);
282 priv->acct_map.~AcctMap();
285 g_object_unref(budget);
291 gnc_budget_begin_edit(GncBudget *bgt)
297 gnc_budget_commit_edit(GncBudget *bgt)
301 noop, gnc_budget_free);
307 g_return_val_if_fail(book,
nullptr);
311 auto budget {
static_cast<GncBudget*
>(g_object_new(GNC_TYPE_BUDGET,
nullptr)) };
323 g_return_if_fail(GNC_IS_BUDGET(budget));
324 gnc_budget_begin_edit(budget);
325 qof_instance_set_dirty(&budget->inst);
326 qof_instance_set_destroying(budget, TRUE);
327 gnc_budget_commit_edit(budget);
333 const GncBudget* old_b;
339 clone_budget_values_cb(
Account* a, gpointer user_data)
344 for ( i = 0; i < data->num_periods; ++i )
346 if ( gnc_budget_is_account_period_value_set(data->old_b, a, i) )
348 gnc_budget_set_account_period_value(data->new_b, a, i,
349 gnc_budget_get_account_period_value(data->old_b, a, i));
361 g_return_val_if_fail(old_b !=
nullptr,
nullptr);
366 gnc_budget_begin_edit(new_b);
369 gnc_budget_set_recurrence(new_b, gnc_budget_get_recurrence(old_b));
373 clone_data.old_b = old_b;
374 clone_data.new_b = new_b;
375 clone_data.num_periods = gnc_budget_get_num_periods(new_b);
376 gnc_account_foreach_descendant(root, clone_budget_values_cb, &clone_data);
378 gnc_budget_commit_edit(new_b);
390 g_return_if_fail(GNC_IS_BUDGET(budget) && name);
392 priv = GET_PRIVATE(budget);
393 if ( name == priv->name )
return;
395 gnc_budget_begin_edit(budget);
396 CACHE_REPLACE(priv->name, name);
397 qof_instance_set_dirty(&budget->inst);
398 gnc_budget_commit_edit(budget);
404 gnc_budget_get_name(
const GncBudget* budget)
406 g_return_val_if_fail(GNC_IS_BUDGET(budget),
nullptr);
407 return GET_PRIVATE(budget)->name;
415 g_return_if_fail(GNC_IS_BUDGET(budget));
416 g_return_if_fail(description);
418 priv = GET_PRIVATE(budget);
419 if ( description == priv->description )
return;
420 gnc_budget_begin_edit(budget);
421 CACHE_REPLACE(priv->description, description);
422 qof_instance_set_dirty(&budget->inst);
423 gnc_budget_commit_edit(budget);
429 gnc_budget_get_description(
const GncBudget* budget)
431 g_return_val_if_fail(GNC_IS_BUDGET(budget),
nullptr);
432 return GET_PRIVATE(budget)->description;
436 gnc_budget_set_recurrence(GncBudget *budget,
const Recurrence *r)
440 g_return_if_fail(budget && r);
441 priv = GET_PRIVATE(budget);
443 gnc_budget_begin_edit(budget);
444 priv->recurrence = *r;
445 qof_instance_set_dirty(&budget->inst);
446 gnc_budget_commit_edit(budget);
452 gnc_budget_get_recurrence(
const GncBudget *budget)
454 g_return_val_if_fail(budget,
nullptr);
455 return (&GET_PRIVATE(budget)->recurrence);
459 gnc_budget_get_guid(
const GncBudget* budget)
461 g_return_val_if_fail(budget,
nullptr);
462 g_return_val_if_fail(GNC_IS_BUDGET(budget),
nullptr);
471 g_return_if_fail(GNC_IS_BUDGET(budget));
472 g_return_if_fail(num_periods > 0);
474 priv = GET_PRIVATE(budget);
475 if ( priv->num_periods == num_periods )
return;
477 gnc_budget_begin_edit(budget);
478 priv->num_periods = num_periods;
479 std::for_each (priv->acct_map.begin(), priv->acct_map.end(),
480 [num_periods](
auto& it){ it.second.resize(num_periods); });
481 qof_instance_set_dirty(&budget->inst);
482 gnc_budget_commit_edit(budget);
488 gnc_budget_get_num_periods(
const GncBudget* budget)
490 g_return_val_if_fail(GNC_IS_BUDGET(budget), 0);
491 return GET_PRIVATE(budget)->num_periods;
494 static inline StringVec
495 make_period_data_path (
const Account *account, guint period_num)
498 return { acct_guid.to_string(), std::to_string (period_num) };
501 static inline StringVec
502 make_period_note_path (
const Account *account, guint period_num)
504 StringVec path { GNC_BUDGET_NOTES_PATH };
505 StringVec data_path { make_period_data_path (account, period_num) };
506 std::move (data_path.begin(), data_path.end(), std::back_inserter (path));
510 static PeriodData& get_perioddata (
const GncBudget *budget,
517 gnc_budget_unset_account_period_value(GncBudget *budget,
const Account *account,
520 g_return_if_fail (budget !=
nullptr);
521 g_return_if_fail (account !=
nullptr);
522 g_return_if_fail (period_num < GET_PRIVATE(budget)->num_periods);
524 auto& data = get_perioddata (budget, account, period_num);
525 data.opt_value.reset();
527 gnc_budget_begin_edit(budget);
528 auto path = make_period_data_path (account, period_num);
529 auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
530 delete budget_kvp->set_path (path,
nullptr);
531 qof_instance_set_dirty(&budget->inst);
532 gnc_budget_commit_edit(budget);
541 gnc_budget_set_account_period_value(GncBudget *budget,
const Account *account,
542 guint period_num, gnc_numeric val)
546 if (period_num >= GET_PRIVATE(budget)->num_periods)
548 PWARN(
"Period %i does not exist", period_num);
552 g_return_if_fail (budget !=
nullptr);
553 g_return_if_fail (account !=
nullptr);
555 auto& perioddata = get_perioddata (budget, account, period_num);
556 auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
557 auto path = make_period_data_path (account, period_num);
559 gnc_budget_begin_edit(budget);
562 delete budget_kvp->set_path (path,
nullptr);
563 perioddata.opt_value.reset();
567 KvpValue* v =
new KvpValue (val);
568 delete budget_kvp->set_path (path, v);
569 perioddata.opt_value = val;
571 qof_instance_set_dirty(&budget->inst);
572 gnc_budget_commit_edit(budget);
579 gnc_budget_is_account_period_value_set (
const GncBudget *budget,
583 g_return_val_if_fail (period_num < GET_PRIVATE(budget)->num_periods,
false);
584 return get_perioddata (budget, account, period_num).opt_value.has_value();
588 gnc_budget_get_account_period_value (
const GncBudget *budget,
592 g_return_val_if_fail (period_num < GET_PRIVATE(budget)->num_periods,
594 auto& data = get_perioddata (budget, account, period_num);
596 return data.opt_value.has_value() ? data.opt_value.value() : gnc_numeric_zero();
600 gnc_budget_set_account_period_note(GncBudget *budget,
const Account *account,
601 guint period_num,
const gchar *note)
605 if (period_num >= GET_PRIVATE(budget)->num_periods)
607 PWARN(
"Period %i does not exist", period_num);
611 g_return_if_fail (budget !=
nullptr);
612 g_return_if_fail (account !=
nullptr);
614 auto& perioddata = get_perioddata (budget, account, period_num);
615 auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
616 auto path = make_period_note_path (account, period_num);
618 gnc_budget_begin_edit(budget);
621 delete budget_kvp->set_path (path,
nullptr);
622 perioddata.note.clear ();
626 KvpValue* v =
new KvpValue (g_strdup (note));
628 delete budget_kvp->set_path (path, v);
629 perioddata.note = note;
631 qof_instance_set_dirty(&budget->inst);
632 gnc_budget_commit_edit(budget);
639 gnc_budget_get_account_period_note (
const GncBudget *budget,
640 const Account *account, guint period_num)
642 g_return_val_if_fail (period_num < GET_PRIVATE(budget)->num_periods,
nullptr);
643 auto& data = get_perioddata (budget, account, period_num);
644 return data.note.empty () ? nullptr : data.note.c_str();
650 g_return_val_if_fail (GNC_IS_BUDGET(budget), 0);
651 return recurrenceGetPeriodTime(&GET_PRIVATE(budget)->recurrence, period_num, FALSE);
657 g_return_val_if_fail (GNC_IS_BUDGET(budget), 0);
658 return recurrenceGetPeriodTime(&GET_PRIVATE(budget)->recurrence, period_num, TRUE);
662 gnc_budget_get_account_period_actual_value(
663 const GncBudget *budget,
Account *acc, guint period_num)
666 g_return_val_if_fail(GNC_IS_BUDGET(budget) && acc, gnc_numeric_zero());
667 return recurrenceGetAccountPeriodValue(&GET_PRIVATE(budget)->recurrence,
672 get_perioddata (
const GncBudget *budget,
const Account *account, guint period_num)
676 if (period_num >= priv->num_periods)
677 throw std::out_of_range(
"period_num >= num_periods");
679 auto& vec = priv->acct_map[account];
683 auto budget_kvp { QOF_INSTANCE (budget)->kvp_data };
684 vec.reserve (priv->num_periods);
686 for (guint i = 0; i < priv->num_periods; i++)
688 auto kval1 { budget_kvp->get_slot (make_period_data_path (account, i)) };
689 auto kval2 { budget_kvp->get_slot (make_period_note_path (account, i)) };
691 auto is_set = kval1 && kval1->get_type() == KvpValue::Type::NUMERIC;
692 auto num = is_set ? std::make_optional (kval1->get<gnc_numeric>()) :
std::nullopt;
693 auto note = (kval2 && kval2->get_type() == KvpValue::Type::STRING) ?
694 kval2->get<
const char*>() :
"";
696 vec.emplace_back (note, num);
700 return vec.at(period_num);
704 gnc_budget_lookup (
const GncGUID *guid,
const QofBook *book)
708 g_return_val_if_fail(guid,
nullptr);
709 g_return_val_if_fail(book,
nullptr);
714 static void just_get_one(
QofInstance *ent, gpointer data)
716 GncBudget **bgt = (GncBudget**)data;
717 if (bgt && !*bgt) *bgt = GNC_BUDGET(ent);
721 gnc_budget_get_default (QofBook *book)
724 GncBudget *bgt =
nullptr;
725 GncGUID *default_budget_guid =
nullptr;
727 g_return_val_if_fail(book,
nullptr);
730 "default-budget", &default_budget_guid,
732 if (default_budget_guid)
736 default_budget_guid);
741 if ( bgt ==
nullptr )
746 qof_collection_foreach(col, just_get_one, &bgt);
750 guid_free (default_budget_guid);
755 destroy_budget_on_book_close(
QofInstance *ent, gpointer data)
757 GncBudget* bgt = GNC_BUDGET(ent);
767 gnc_budget_book_end(QofBook* book)
772 qof_collection_foreach(col, destroy_budget_on_book_close,
nullptr);
784 static QofObject budget_object_def =
787 DI(.e_type = ) GNC_ID_BUDGET,
788 DI(.type_label = ) "Budget",
790 DI(.book_begin = )
nullptr,
791 DI(.book_end = ) gnc_budget_book_end,
794 DI(.foreach = ) qof_collection_foreach,
795 DI(.printable = ) (const
char * (*)(gpointer)) gnc_budget_get_name,
801 static PeriodType gnc_budget_get_rec_pt(const GncBudget *bgt)
803 return recurrenceGetPeriodType(&(GET_PRIVATE(bgt)->recurrence));
805 static guint gnc_budget_get_rec_mult(
const GncBudget *bgt)
807 return recurrenceGetMultiplier(&(GET_PRIVATE(bgt)->recurrence));
809 static time64 gnc_budget_get_rec_time(
const GncBudget *bgt)
811 return recurrenceGetTime(&(GET_PRIVATE(bgt)->recurrence));
815 gboolean gnc_budget_register (
void)
817 static QofParam params[] =
820 "name", QOF_TYPE_STRING,
825 "description", QOF_TYPE_STRING,
830 "recurrence_period_type", QOF_TYPE_INT32,
835 "recurrence_multiplier", QOF_TYPE_INT32,
842 "recurrence_date", QOF_TYPE_DATE,
847 "num_periods", QOF_TYPE_INT32,
856 QOF_PARAM_GUID, QOF_TYPE_GUID,
void gnc_budget_set_num_periods(GncBudget *budget, guint num_periods)
Set/Get the number of periods in the Budget.
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.
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.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
GncBudget * gnc_budget_new(QofBook *book)
Creates and initializes a Budget.
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.
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
#define ENTER(format, args...)
Print a function entry debugging message.
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Data structure for containing info while cloning budget values.
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
#define PWARN(format, args...)
Log a warning.
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)
Account handling public routines.
time64 gnc_budget_get_period_start_date(const GncBudget *budget, guint period_num)
Get the starting date of the Budget period.
GncBudget * gnc_budget_clone(const GncBudget *old_b)
Clones a budget creating a copy.
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.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
void gnc_budget_set_name(GncBudget *budget, const gchar *name)
Set/Get the name of the Budget.
void gnc_budget_set_description(GncBudget *budget, const gchar *description)
Set/Get the description of the Budget.
#define LEAVE(format, args...)
Print a function exit debugging message.
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.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
guint qof_collection_count(const QofCollection *col)
return the number of entities in the collection.
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
time64 gnc_budget_get_period_end_date(const GncBudget *budget, guint period_num)
Get the ending date of the Budget period.
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
The type used to store guids in C.
Commodity handling public routines.
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).