[patch 3/8] [budget-engine.diff] Core Budget implementation
c.shoemaker at cox.net
c.shoemaker at cox.net
Sat Oct 15 00:18:30 EDT 2005
* src/engine/Makefile.am:
src/engine/gnc-budget.[ch]:
- budget functionality in the engine
* src/engine/gnc-engine.[ch]
- add the budget logging module
* src/engine/gw-engine-spec.scm
- g-wrap bindings for budget functions needed by guile
src/engine/Makefile.am | 16 -
src/engine/gnc-budget.c | 642 +++++++++++++++++-------------------------
src/engine/gnc-budget.h | 211 +++++--------
src/engine/gnc-engine.c | 2
src/engine/gnc-engine.h | 19 +
src/engine/gw-engine-spec.scm | 83 +++++
6 files changed, 444 insertions(+), 529 deletions(-)
Index: gnucash/src/engine/Makefile.am
===================================================================
--- gnucash.orig/src/engine/Makefile.am
+++ gnucash/src/engine/Makefile.am
@@ -8,6 +8,7 @@ AM_CFLAGS = \
-I${top_srcdir}/src \
-I${top_srcdir}/src/gnc-module \
-I${top_srcdir}/src/business/business-core/ \
+ -I${top_srcdir}/src/core-utils \
${GNUCASH_ENGINE_CFLAGS}
libgncmod_engine_la_SOURCES = \
@@ -28,10 +29,6 @@ libgncmod_engine_la_SOURCES = \
cap-gains.c \
cashobjects.c \
gnc-associate-account.c \
- gnc-budget-book.c \
- gnc-budget-cat.c \
- gnc-budget-period-value.c \
- gnc-budget-period.c \
gnc-budget.c \
gnc-commodity.c \
gnc-date.c \
@@ -62,7 +59,7 @@ libgncmod_engine_la_SOURCES = \
qofobject.c \
qofquery.c \
qofquerycore.c \
- qofsession.c
+ qofsession.c
EXTRA_libgncmod_engine_la_SOURCES = iso-4217-currencies.c
@@ -92,10 +89,6 @@ gncinclude_HEADERS = \
glib-helpers.h \
gnc-associate-account.h \
gnc-book.h \
- gnc-budget-book.h \
- gnc-budget-cat.h \
- gnc-budget-period-value.h \
- gnc-budget-period.h \
gnc-budget.h \
gnc-commodity.h \
gnc-date.h \
@@ -142,11 +135,6 @@ noinst_HEADERS = \
SX-book.h \
SX-ttinfo.h \
TransactionP.h \
- gnc-budget-book-p.h \
- gnc-budget-cat-p.h \
- gnc-budget-period-value-p.h \
- gnc-budget-period-p.h \
- gnc-budget-p.h \
gnc-event-p.h \
gnc-hooks-scm.h \
gnc-lot.h \
Index: gnucash/src/engine/gnc-budget.c
===================================================================
--- gnucash.orig/src/engine/gnc-budget.c
+++ gnucash/src/engine/gnc-budget.c
@@ -1,6 +1,7 @@
/********************************************************************\
- * gnc-budget.c -- Implementation of the top level Budgeting API's. *
+ * gnc-budget.c -- Implementation of the top level Budgeting API's. *
* Copyright (C) 04 sep 2003 Darin Willits <darin at willits.ca> *
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -21,492 +22,375 @@
* *
\********************************************************************/
-/** @file gnc-budget.c
- * @brief Implementation of the top level budgeting API's.
- * @author Created by Darin Willits 04 sep 2003
- * @author Copyright (c) 2003 Darin Willits <darin at willits.ca>
- *
- *
- */
-
-// Includes
-#include "config.h"
-#include <stdio.h>
-
-#include "gnc-budget-p.h"
-#include "qofbook.h"
-#include "qofbook-p.h"
-#include "qofid-p.h"
-#include "gnc-engine.h"
-#include "gnc-engine-util.h"
-#include "gnc-event-p.h"
-#include "Group.h"
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "qof.h"
+
#include "Account.h"
-#include "gnc-budget-period.h"
+#include "Group.h"
+#include "gnc-budget.h"
+#include "gnc-event-p.h"
+#include "gnc-commodity.h"
+#include "gnc-trace.h"
+#include "gnc-gdate-utils.h"
+
+static QofLogModule log_module = GNC_MOD_ENGINE;
-static void budget_fill_categories(GncBudget* budget);
+struct gnc_budget_private{
+ QofInstance inst;
+
+ gchar* name;
+ gchar* description;
+ Recurrence recurrence;
+ guint num_periods;
+};
-GncBudget* gnc_budget_new(QofBook *book)
+GncBudget*
+gnc_budget_new(QofBook *book)
{
GncBudget* budget;
- GDate date;
- GString* freqStr;
g_return_val_if_fail(book, NULL);
+ ENTER(" ");
budget = g_new0(GncBudget, 1);
-
-
qof_instance_init (&budget->inst, GNC_ID_BUDGET, book);
- //budget->entity_table = qof_book_get_entity_table (book);
- //qof_entity_guid_new (budget->entity_table, &budget->guid);
- //qof_entity_store( budget->entity_table, budget,
- // &budget->guid, GNC_ID_BUDGET );
-
- budget->book = book;
-
- /* Create the default period frequency.*/
- budget->period_freq = xaccFreqSpecMalloc(book);
-
- g_date_clear(&date, 1);
- g_date_set_time(&date, time(NULL));
- xaccFreqSpecSetMonthly(budget->period_freq, &date, 1);
- xaccFreqSpecSetUIType(budget->period_freq, UIFREQ_MONTHLY);
-
- freqStr = g_string_sized_new(16);
- xaccFreqSpecGetFreqStr(budget->period_freq, freqStr);
- //printf("Category Freq: %s\n", freqStr->str);
-
-
-
- /* fill the categories based on the current account hierarchy.
- * FIXME: This should probably not be here but rather be a separate
- * operation with a public interface. For now it is convienent.*/
- budget_fill_categories(budget);
+ recurrenceSet(&budget->recurrence, 1, PERIOD_MONTH, NULL);
+
+ gnc_budget_set_name(budget, "Unnamed Budget");
+ gnc_budget_set_description(budget, "");
+ gnc_budget_set_num_periods(budget, 12);
gnc_engine_gen_event( &budget->inst.entity, GNC_EVENT_CREATE );
+ LEAVE(" ");
return budget;
}
-void gnc_budget_delete(GncBudget* budget)
+static void
+remove_bgt_line_items(QofEntity *act, gpointer bgt)
+{
+ KvpFrame *frame;
+ const GUID *guid;
+ gchar guidbuf[GUID_ENCODING_LENGTH+1];
+
+ frame = qof_instance_get_slots(QOF_INSTANCE(bgt));
+ guid = qof_entity_get_guid(QOF_ENTITY(act));
+ guid_to_string_buff(guid, guidbuf);
+ kvp_frame_delete(kvp_frame_get_frame(frame, guidbuf));
+}
+
+static void
+gnc_budget_remove_all_line_items(GncBudget *budget)
{
- if(budget == NULL){
+ QofBook *book;
+ QofCollection *col;
+
+ g_return_if_fail(GNC_IS_BUDGET(budget));
+
+ book = qof_instance_get_book(QOF_INSTANCE(budget));
+ col = qof_book_get_collection(book, GNC_ID_ACCOUNT);
+ qof_collection_foreach(col, remove_bgt_line_items, (gpointer) budget);
+}
+
+void
+gnc_budget_free(GncBudget* budget)
+{
+ if (budget == NULL)
return;
- }
-
+
+ g_return_if_fail(GNC_IS_BUDGET(budget));
+ gnc_budget_remove_all_line_items(budget);
+
/* We first send the message that this object is about to be
* destroyed so that any GUI elements can remove it before it is
- * actually gone.
- */
+ * actually gone. */
gnc_engine_gen_event( &budget->inst.entity, GNC_EVENT_DESTROY);
-
- //qof_entity_remove(budget->entity_table, &budget->guid);
- if(budget->name){
+ if (budget->name)
g_free(budget->name);
- }
-
- if(budget->description){
+
+ if (budget->description)
g_free(budget->description);
- }
qof_instance_release (&budget->inst);
g_free(budget);
}
-void gnc_budget_set_name(GncBudget* budget, const gchar* name)
+void
+gnc_budget_set_name(GncBudget* budget, const gchar* name)
{
- g_return_if_fail( name != NULL );
- if ( budget->name != NULL ) {
- g_free( budget->name );
- budget->name = NULL;
- }
- budget->name = g_strdup( name );
+ g_return_if_fail(GNC_IS_BUDGET(budget));
+ g_return_if_fail(name);
+
+ if (budget->name)
+ g_free(budget->name);
+ budget->name = g_strdup(name);
+ gnc_engine_gen_event( &budget->inst.entity, GNC_EVENT_MODIFY);
}
-gchar* gnc_budget_get_name(GncBudget* budget)
+const gchar*
+gnc_budget_get_name(GncBudget* budget)
{
- if(budget == NULL){
- return NULL;
- }
+ g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
return budget->name;
}
-void gnc_budget_set_description(GncBudget* budget, const gchar* description)
+void
+gnc_budget_set_description(GncBudget* budget, const gchar* description)
{
- g_return_if_fail( description != NULL );
- if ( budget->description != NULL ) {
+ g_return_if_fail(GNC_IS_BUDGET(budget));
+ g_return_if_fail(description);
+
+ if (budget->description)
g_free( budget->description);
- budget->description = NULL;
- }
budget->description = g_strdup(description);
+ gnc_engine_gen_event( &budget->inst.entity, GNC_EVENT_MODIFY);
}
-gchar* gnc_budget_get_description(GncBudget* budget)
+const gchar*
+gnc_budget_get_description(GncBudget* budget)
{
- if(budget == NULL){
- return NULL;
- }
+ g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
return budget->description;
}
-
-
-
-void gnc_budget_set_period_frequency(GncBudget* budget, FreqSpec* period)
+void
+gnc_budget_set_recurrence(GncBudget *budget, const Recurrence *r)
{
- if(budget == NULL){
- return;
- }
-
- if((budget->period_freq == period) || (period == NULL)){
- return;
- }
-
- if(budget->period_freq != NULL){
- /* Delete the existing object before setting the new one. */
- xaccFreqSpecFree(budget->period_freq);
- }
-
- budget->period_freq = period;
+ g_return_if_fail(budget && r);
+ budget->recurrence = *r;
+ gnc_engine_gen_event(&budget->inst.entity, GNC_EVENT_MODIFY);
}
-FreqSpec* gnc_budget_get_period_frequency(GncBudget* budget)
+const Recurrence *
+gnc_budget_get_recurrence(GncBudget *budget)
{
- if(budget == NULL){
- return NULL;
- }
-
- return budget->period_freq;
+ g_return_val_if_fail(budget, NULL);
+ return (&budget->recurrence);
}
-
-
-
-void gnc_budget_set_start_date(GncBudget* budget, GDate* date)
+const GUID*
+gnc_budget_get_guid(GncBudget* budget)
{
- if(budget == NULL){
- return;
- }
- budget->start_date = *date;
+ g_return_val_if_fail(budget, NULL);
+ g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
+ return qof_entity_get_guid(QOF_ENTITY(budget));
}
-GDate* gnc_budget_get_start_date(GncBudget* budget)
+void
+gnc_budget_set_num_periods(GncBudget* budget, guint num_periods)
{
- if(budget == NULL){
- return NULL;
- }
- return &budget->start_date;
+ g_return_if_fail(GNC_IS_BUDGET(budget));
+ budget->num_periods = num_periods;
+ gnc_engine_gen_event( &budget->inst.entity, GNC_EVENT_MODIFY);
}
-
-
-
-
-
-void gnc_budget_set_length_months(GncBudget* budget, gint months)
+guint
+gnc_budget_get_num_periods(GncBudget* budget)
{
- if(budget == NULL){
- return;
- }
-
- budget->length = months;
+ g_return_val_if_fail(GNC_IS_BUDGET(budget), 0);
+ return budget->num_periods;
}
-void gnc_budget_set_length_years(GncBudget* budget, gint years)
-{
- if(budget == NULL){
- return;
- }
-
- budget->length = years * 12;
-}
+#define BUF_SIZE (10 + GUID_ENCODING_LENGTH + \
+ GNC_BUDGET_MAX_NUM_PERIODS_DIGITS)
-gint gnc_budget_get_length_months(GncBudget* budget)
+/* period_num is zero-based */
+/* What happens when account is deleted, after we have an entry for it? */
+void
+gnc_budget_set_account_period_value(GncBudget *budget, Account *account,
+ guint period_num, gnc_numeric val)
{
- if(budget == NULL){
- return 0;
- }
+ const GUID *guid;
+ KvpFrame *frame;
+ gchar path[BUF_SIZE];
+ gchar *bufend;
- return budget->length;
-}
+ frame = qof_instance_get_slots(QOF_INSTANCE(budget));
+ guid = xaccAccountGetGUID(account);
+ bufend = guid_to_string_buff(guid, path);
+ g_sprintf(bufend, "/%d", period_num);
-gint gnc_budget_get_length_years(GncBudget* budget)
-{
- if(budget == NULL){
- return 0;
- }
+ kvp_frame_set_numeric(frame, path, val);
+ gnc_engine_gen_event( &budget->inst.entity, GNC_EVENT_MODIFY);
- return budget->length / 12;
}
+/* We don't need these here, but maybe they're useful somewhere else?
+ Maybe this should move to Account.h */
+#if 0
+static gpointer
+is_same_commodity(Account *a, gpointer data)
+{
+ gnc_commodity *acct_comm;
+ gnc_commodity *comm;
+ g_return_val_if_fail(data, NULL);
+ // What? No type-checking macro?
+ comm = (gnc_commodity *) data;
+ acct_comm = xaccAccountGetCommodity(a);
-
-
-
-void gnc_budget_add_inflow_category(GncBudget* budget, GncBudgetCategory* category)
-{
- if(budget == NULL){
- return;
- }
- gnc_budget_category_add_child(budget->inflow_category, category);
+ return gnc_commodity_equal(comm, acct_comm) ? NULL : data;
}
-void gnc_budget_remove_inflow_category(GncBudget* budget, GncBudgetCategory* category)
+static gboolean
+xaccAccountChildrenHaveSameCommodity(Account *account)
{
- if(budget == NULL){
- return;
- }
- gnc_budget_category_remove_child(budget->inflow_category, category);
-}
+ AccountGroup *grp;
+ gpointer different;
+ gnc_commodity *comm;
-void gnc_budget_set_inflow_category(GncBudget* budget, GncBudgetCategory* inflow)
-{
- if(budget == NULL){
- return;
- }
- budget->inflow_category = inflow;
+ comm = xaccAccountGetCommodity(account);
+ grp = xaccAccountGetChildren(account);
+ different = xaccGroupForEachAccount(
+ grp, is_same_commodity, comm, TRUE);
+ return (different == NULL);
}
+#endif
-GncBudgetCategory* gnc_budget_get_inflow_category(GncBudget* budget)
-{
- if(budget == NULL){
- return NULL;
- }
- return budget->inflow_category;
-}
-void gnc_budget_set_outflow_category(GncBudget* budget, GncBudgetCategory* outflow)
+/* In order to distinguish between a value of zero and an unset value,
+ this function can return a gnc_numeric with GNC_ERROR_ARG set.
+ Currently, it only does so for placeholder accounts with
+ mixed-commodity subaccounts. */
+gnc_numeric
+gnc_budget_get_account_period_value(GncBudget *budget, Account *account,
+ guint period_num)
{
- if(budget == NULL){
- return;
- }
- budget->outflow_category = outflow;
-}
+ gnc_numeric numeric;
+ gchar path[BUF_SIZE];
+ gchar *bufend;
+ const GUID *guid;
+ KvpFrame *frame;
-GncBudgetCategory* gnc_budget_get_outflow_category(GncBudget* budget)
-{
- if(budget == NULL){
- return NULL;
- }
- return budget->outflow_category;
-}
+ numeric = gnc_numeric_zero();
+ g_return_val_if_fail(GNC_IS_BUDGET(budget), numeric);
+ g_return_val_if_fail(account, numeric);
-void gnc_budget_add_outflow_category(GncBudget* budget, GncBudgetCategory* category)
-{
- if(budget == NULL){
- return;
- }
- gnc_budget_category_add_child(budget->outflow_category, category);
+ /* FIXME? check for unset? Right now, returns zero on unset. */
+ frame = qof_instance_get_slots(QOF_INSTANCE(budget));
+ guid = xaccAccountGetGUID(account);
+ bufend = guid_to_string_buff(guid, path);
+ g_sprintf(bufend, "/%d", period_num);
+ numeric = kvp_frame_get_numeric(frame, path);
+ return numeric;
}
-void gnc_budget_remove_outflow_category(GncBudget* budget, GncBudgetCategory* category)
+/* If 'end' is true, then get a time just before the beginning of the
+ next period */
+static time_t
+gnc_budget_get_period_time(GncBudget *budget, guint period_num, gboolean end)
{
- if(budget == NULL){
- return;
+ GDate date;
+ recurrenceNthInstance(&(budget->recurrence),
+ period_num + (end ? 1 : 0), &date);
+ if (end) {
+ g_date_subtract_days(&date, 1);
+ return gnc_timet_get_day_end_gdate(&date);
+ } else {
+ return gnc_timet_get_day_start_gdate(&date);
}
- gnc_budget_category_remove_child(budget->inflow_category, category);
-}
-QofBook* gnc_budget_get_book(GncBudget* budget)
-{
- if(budget == NULL){
- return NULL;
- }
- return budget->book;
}
-static void free_budget_period(gpointer data, gpointer user_data)
+Timespec
+gnc_budget_get_period_start_date(GncBudget *budget, guint period_num)
{
- GncBudgetPeriod* period = data;
- g_free(period);
+ Timespec ts;
+ timespecFromTime_t(
+ &ts, gnc_budget_get_period_time(budget, period_num, FALSE));
+ return ts;
}
-void gnc_budget_generate_periods(GncBudget* budget)
+gnc_numeric
+gnc_budget_get_account_period_actual_value(
+ GncBudget *budget, Account *account, guint period_num)
{
- GDate currentPeriod, outDate;
- GDate endDate;
- GDate periodStartDate;
- GncBudgetPeriod* budgetPeriod;
+ gnc_numeric numeric, num1, num2;
+ time_t t1, t2;
- if(budget == NULL){
- return;
- }
-
- /* TODO: Add in some error checking such that we don't regenerate
- * the periods if they are the same as before. We may still need
- * to recalculate the values however.
- */
- if(budget->period_list != NULL){
- g_list_foreach(budget->period_list, free_budget_period, NULL);
- g_list_free(budget->period_list);
- budget->period_list = NULL;
- }
-
- /* Add the starting period. Every budget should have at least
- * one period. */
- g_date_set_dmy(&periodStartDate, g_date_get_day(&budget->start_date),
- g_date_get_month(&budget->start_date),
- g_date_get_year(&budget->start_date));
- budgetPeriod = gnc_budget_period_new();
- gnc_budget_period_set_start_date(budgetPeriod, &periodStartDate);
- budget->period_list = g_list_append(budget->period_list, budgetPeriod);
-
-
- /* Setup the end Date */
- g_date_set_dmy(&endDate, g_date_get_day(&budget->start_date),
- g_date_get_month(&budget->start_date),
- g_date_get_year(&budget->start_date));
- g_date_add_months(&endDate, budget->length);
-
-
- g_date_set_dmy(¤tPeriod, g_date_get_day(&budget->start_date),
- g_date_get_month(&budget->start_date),
- g_date_get_year(&budget->start_date));
- do{
- xaccFreqSpecGetNextInstance(budget->period_freq, ¤tPeriod, &outDate);
- if(g_date_valid(&outDate) == FALSE){
- break;
- }
-
- if(g_date_compare(&outDate, &endDate) >= 0){
- break;
- }
- else{
- /* Set the end date of the previous period. */
- gnc_budget_period_set_end_date(budgetPeriod, &outDate);
-
- /* Create the new period. */
- budgetPeriod = gnc_budget_period_new();
- gnc_budget_period_set_start_date(budgetPeriod, &outDate);
-
- budget->period_list = g_list_append(budget->period_list, budgetPeriod);
- g_date_set_dmy(¤tPeriod, g_date_get_day(&outDate),
- g_date_get_month(&outDate),
- g_date_get_year(&outDate));
- }
- }while(1);
-
- gnc_budget_period_set_end_date(budgetPeriod, &endDate);
-
- /* Now we should generate the list of values for each category.*/
- gnc_budget_category_generate_values(budget->inflow_category);
- gnc_budget_category_generate_values(budget->outflow_category);
-}
+ // FIXME: maybe zero is not best error return val.
+ g_return_val_if_fail(GNC_IS_BUDGET(budget) && account, gnc_numeric_zero());
+ t1 = gnc_budget_get_period_time(budget, period_num, FALSE);
+ t2 = gnc_budget_get_period_time(budget, period_num, TRUE);
+ num1 = xaccAccountGetBalanceAsOfDateInCurrency(
+ account, t1, NULL, TRUE);
+ num2 = xaccAccountGetBalanceAsOfDateInCurrency(
+ account, t2, NULL, TRUE);
-gint gnc_budget_get_num_periods(GncBudget* budget)
-{
- if(budget == NULL){
- return 0;
- }
- return g_list_length(budget->period_list);
+ numeric = gnc_numeric_sub(num2, num1, GNC_DENOM_AUTO,
+ GNC_HOW_DENOM_FIXED);
+ return numeric;
}
-GList* gnc_budget_get_period_list(GncBudget* budget)
+QofBook*
+gnc_budget_get_book(GncBudget* budget)
{
- if(budget == NULL){
- return NULL;
- }
- return budget->period_list;
+ g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL);
+ return qof_instance_get_book(&budget->inst);
}
-
-
-/* ==================================================================== */
-/* Private member functions.
- *
- * ==================================================================== */
-
-static void add_category_from_account(gpointer data, gpointer userdata)
+GList*
+gnc_book_get_budgets(QofBook* book)
{
- GncBudget* budget;
- Account* account;
- AccountList* children;
- GncBudgetCategory* category = NULL;
- gchar* name;
- AccountGroup* group;
- int numChildren;
- GNCAccountType accountType;
-
- account = data;
- budget = userdata;
-
- if(account == NULL){
- return;
- }
-
- //printf("Adding Category from account\n");
- //printf("Account: %s\n", xaccAccountGetName(account));
- group = xaccAccountGetChildren(account);
- children = xaccGroupGetAccountList(group);
- numChildren = xaccGroupGetNumAccounts(group);
- //printf("NumChildren: %d\n", numChildren);
-
- if(numChildren == 0){
- name = xaccAccountGetFullName(account, ':');
- accountType = xaccAccountGetType(account);
-
- category = gnc_budget_category_new(budget->book, budget);
- gnc_budget_category_set_name(category, name);
- gnc_budget_category_add_account(category, account);
-
- if(accountType == INCOME){
- //printf("Inflow Category: %s\n", gnc_budget_category_get_name(category));
- gnc_budget_category_set_type(category, BUDGET_CATEGORY_INFLOW);
- gnc_budget_category_add_child(budget->inflow_category, category);
- }
- else{
- //printf("Outflow Category: %s\n", gnc_budget_category_get_name(category));
- gnc_budget_category_set_type(category, BUDGET_CATEGORY_OUTFLOW);
- gnc_budget_category_add_child(budget->outflow_category, category);
- }
-
- g_free(name);
- }
+ QofCollection *col;
+ g_return_val_if_fail(book, NULL);
+ col = qof_book_get_collection (book, GNC_ID_BUDGET);
+ return qof_collection_get_list(col);
}
-static void budget_fill_categories(GncBudget* budget)
+GncBudget*
+gnc_budget_lookup (const GUID *guid, QofBook *book)
{
- AccountList* children;
- if(budget == NULL){
- return;
- }
-
- //printf("Creating top level budget categories.\n");
-
- budget->inflow_category = gnc_budget_category_new(budget->book, budget);
- gnc_budget_category_set_name(budget->inflow_category, "Inflow");
+ QofCollection *col;
-
- budget->outflow_category = gnc_budget_category_new(budget->book, budget);
- gnc_budget_category_set_name(budget->outflow_category, "Outflow");
+ g_return_val_if_fail(guid, NULL);
+ g_return_val_if_fail(book, NULL);
+ col = qof_book_get_collection (book, GNC_ID_BUDGET);
+ return GNC_BUDGET(qof_collection_lookup_entity (col, guid));
+}
-
- //printf("Filing budget Categories.\n");
- //children = xaccGroupGetAccountList(gnc_book_get_group(budget->book));
- children = xaccGroupGetSubAccounts(gnc_book_get_group(budget->book));
- g_list_foreach(children, add_category_from_account, budget);
+GncBudget*
+gnc_budget_get_default (QofBook *book)
+{
+ GList *list;
+ GncBudget *bgt = NULL;
- //printf("Number of inflow categories: %d\n",
- //gnc_budget_category_get_num_children(budget->inflow_category));
- //printf("Number of outflow categories: %d\n",
- //gnc_budget_category_get_num_children(budget->outflow_category));
-}
+ g_return_val_if_fail(book, NULL);
+ list = gnc_book_get_budgets(book);
+ if (g_list_length(list) > 0) {
+ bgt = GNC_BUDGET(list->data); // Just get the first one.
+ g_list_free(list);
+ }
+ return bgt;
+}
+
+/* Define the QofObject. */
+/* TODO: Eventually, I'm think I'm going to have to check if this struct is
+ complete. Also, do we need one of those QofParam thingys? */
+static QofObject budget_object_def =
+{
+ interface_version: QOF_OBJECT_VERSION,
+ e_type: GNC_ID_BUDGET,
+ type_label: "BUDGET",
+ create: (gpointer (*)(QofBook *)) gnc_budget_new,
+ book_begin: NULL,
+ book_end: NULL,
+ is_dirty: NULL,
+ mark_clean: NULL,
+ foreach: qof_collection_foreach,
+ printable: NULL,
+ version_cmp: (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
+};
-GncBudget* gnc_budget_lookup (const GUID *guid, QofBook *book)
+/* Register ourselves with the engine. */
+gboolean gnc_budget_register (void)
{
- QofCollection *col;
-
- if (!guid || !book){
- return NULL;
- }
-
- col = qof_book_get_collection (book, GNC_ID_BUDGET);
- return (GncBudget*)qof_collection_lookup_entity (col, guid);
+ return qof_object_register (&budget_object_def);
}
Index: gnucash/src/engine/gnc-budget.h
===================================================================
--- gnucash.orig/src/engine/gnc-budget.h
+++ gnucash/src/engine/gnc-budget.h
@@ -1,6 +1,7 @@
/********************************************************************\
- * gnc-budget.h -- Budget public handling routines. *
+ * gnc-budget.h -- Budget public handling routines. *
* Copyright (C) 04 sep 2003 Darin Willits <darin at willits.ca> *
+ * Copyright (C) 2005 Chris Shoemaker <c.shoemaker at cox.net> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -21,39 +22,58 @@
* *
\********************************************************************/
-
-/** @addtogroup Engine
- * @{ */
-/** @addtogroup Budget Budget objects
- * A budget object contains a list of budget_categories each of
- * which define an individual budgetable item. When a budget is first
- * created these catagories will be initialized based on the existing
- * account hierarchy.
- * @{ */
-/** @file gnc-budget.h
- * @brief Public interface for budget objects.
- * @author Created by Darin Willits 04 sep 2003
- * @author Copyright (c) 04 Darin Willits <darin at willits.ca>
- *
- * This file contains the public methods to interact with a budgeting
- * object.
- * This is pre-alpha code right now. User beware.
+/* Design decisions:
+ *
+ * - The budget values that the user enters (and that are stored) for
+ * each account are inclusive of any sub-accounts.
+ *
+ * - Reason: This allows the user to budget an amount for a
+ * "parent" account, while tracking (e.g.) expenses with more
+ * detail in sub-accounts.
+ *
+ * - Implication: when reporting budgeted values for parent
+ * accounts, just show the stored value, not a sum.
+ *
+ * - Budget values are always in the account's commodity.
+ *
+ *
+ *
+ * Accounts with sub-accounts can have a value budgeted. For those
+ * accounts,
+ *
+ * Option 1: when setting or getting budgeted values, the value is
+ * *always* exclusive of sub-account values. Pro: consistent
+ * values in all contexts. Con: no summary behavior.
+ *
+ * Option 2: make setting only for account proper, but always
+ * report summaries. Con: value can change as soon as it is
+ * entered; forces entry from bottom-up.
+ *
+ * * Option 3: value is always *inclusive* of sub-accounts, although
+ * potentially in a different commodity. Pro: allows top-down
+ * entry; no auto value update. Con: ? [ This option was selected. ]
+ *
*/
-
#ifndef __GNC_BUDGET_H__
#define __GNC_BUDGET_H__
#include <glib.h>
/** The budget data.*/
-typedef struct gncp_budget GncBudget;
+typedef struct gnc_budget_private GncBudget;
#include "guid.h"
#include "qofbook.h"
-#include "gnc-budget-cat.h"
-#include "FreqSpec.h"
+#include "Account.h"
+#include "Recurrence.h"
+
+#define GNC_IS_BUDGET(obj) (QOF_CHECK_TYPE((obj), GNC_ID_BUDGET))
+#define GNC_BUDGET(obj) (QOF_CHECK_CAST((obj), GNC_ID_BUDGET, GncBudget))
+#define GNC_BUDGET_MAX_NUM_PERIODS_DIGITS 3 // max num periods == 999
+
+gboolean gnc_budget_register(void);
/**
* Creates and initializes a Budget.
@@ -61,130 +81,53 @@ typedef struct gncp_budget GncBudget;
GncBudget *gnc_budget_new(QofBook *book);
/** Deletes the given budget object.*/
-void gnc_budget_delete(GncBudget* budget);
-
+void gnc_budget_free(GncBudget* budget);
+const GUID* gnc_budget_get_guid(GncBudget* budget);
+#define gnc_budget_return_guid(X) \
+ (X ? *(qof_entity_get_guid(QOF_ENTITY(X))) : *(guid_null()))
-/** Set the Name of the Budget.
- */
+/** Set/Get the name of the Budget */
void gnc_budget_set_name(GncBudget* budget, const gchar* name);
+const gchar* gnc_budget_get_name(GncBudget* budget);
-/** Retieve the Name of the Budget.
- */
-gchar* gnc_budget_get_name(GncBudget* budget);
-
-
-
-/** Set the Description of the Budget.
- */
+/** Set/Get the description of the Budget */
void gnc_budget_set_description(GncBudget* budget, const gchar* description);
+const gchar* gnc_budget_get_description(GncBudget* budget);
-/** Retieve the Description of the Budget.
- */
-gchar* gnc_budget_get_description(GncBudget* budget);
-
-
-
-/** Set the period frequency.
- */
-void gnc_budget_set_period_frequency(GncBudget* budget, FreqSpec* period);
-
-/** Get the period frequency.
- */
-FreqSpec* gnc_budget_get_period_frequency(GncBudget* budget);
-
-
-
-/** Set the start date*/
-void gnc_budget_set_start_date(GncBudget* budget, GDate* date);
-
-/** Return the start date */
-GDate* gnc_budget_get_start_date(GncBudget* budget);
-
-
-/** Set the Length for this budget in terms of a number of months.
- */
-void gnc_budget_set_length_months(GncBudget* budget, gint months);
+/** Set/Get the number of periods in the Budget */
+void gnc_budget_set_num_periods(GncBudget* budget, guint num_periods);
+guint gnc_budget_get_num_periods(GncBudget* budget);
+
+void gnc_budget_set_recurrence(GncBudget *budget, const Recurrence *r);
+const Recurrence * gnc_budget_get_recurrence(GncBudget *budget);
+
+/** Set/Get the starting date of the Budget */
+void gnc_budget_set_start_date(GncBudget* budget, Timespec date);
+Timespec gnc_budget_get_start_date(GncBudget* budget);
+Timespec gnc_budget_get_period_start_date(GncBudget* budget, guint period_num);
+
+/* Period indices are zero-based. */
+void gnc_budget_set_account_period_value(
+ GncBudget* budget, Account* account, guint period_num, gnc_numeric val);
+
+gnc_numeric gnc_budget_get_account_period_value(
+ GncBudget *budget, Account *account, guint period_num);
+gnc_numeric gnc_budget_get_account_period_actual_value(
+ GncBudget *budget, Account *account, guint period_num);
-/** Set the Length for this budget in terms of a number of months.
- */
-void gnc_budget_set_length_years(GncBudget* budget, gint years);
-
-/** Retrieve the Length for this budget in number of months.
- */
-gint gnc_budget_get_length_months(GncBudget* budget);
-
-/** Retrieve the Length for this budget in number of years.
- */
-gint gnc_budget_get_length_years(GncBudget* budget);
-
-
-
-/* Add the inflow category for this budget.
- * This is a wrapper function to make it easier to add a category
- * as a child of the inflow category. It's here cause I'm a lazy ass.
- */
-void gnc_budget_add_inflow_category(GncBudget* budget, GncBudgetCategory* inflow);
-
-/* Remove the inflow category for this budget.
- */
-void gnc_budget_remove_inflow_category(GncBudget* budget, GncBudgetCategory* category);
-
-/* Set the inflow category for this budget.
- */
-void gnc_budget_set_inflow_category(GncBudget* budget, GncBudgetCategory* inflow);
-
-/** Retrieve the inflow category for this budget.
- */
-GncBudgetCategory* gnc_budget_get_inflow_category(GncBudget* budget);
-
-
-
-
-/* Add the outflow category for this budget.
- */
-void gnc_budget_add_outflow_category(GncBudget* budget, GncBudgetCategory* outflow);
-
-/* Remove the outflow category for this budget.
- */
-void gnc_budget_remove_outflow_category(GncBudget* budget, GncBudgetCategory* inflow);
-
-/* Set the outflow category for this budget.
- */
-void gnc_budget_set_outflow_category(GncBudget* budget, GncBudgetCategory* category);
-
-/** Retrieve the outflow category for this budget.
- */
-GncBudgetCategory* gnc_budget_get_outflow_category(GncBudget* budget);
-
-
-
-/** Retrieve the book that this budget is associated with.*/
+/** Get the book that this budget is associated with. */
QofBook* gnc_budget_get_book(GncBudget* budget);
+/* Caller owns the list of all budgets in the book. */
+GList* gnc_book_get_budgets(QofBook* book);
-/** Generate the list of periods.
- * This function will use the start date and budget length to generate a
- * list of budget periods. The periods are represented by a list of start
- * dates for each period.
- * As well this function regenerates the list of values for each
- * category.
- * */
-void gnc_budget_generate_periods(GncBudget* budget);
-
-/** Get the number of periods.
- */
-gint gnc_budget_get_num_periods(GncBudget* budget);
-
-/* Return the list of periods.
- */
-GList* gnc_budget_get_period_list(GncBudget* budget);
-
+/* Returns some budget in the book, or NULL. */
+GncBudget* gnc_budget_get_default(QofBook *book);
-/** Retrieve the budget object associated with the given GUID from
- * the given book.
- */
+/* Get the budget associated with the given GUID from the given book. */
GncBudget* gnc_budget_lookup (const GUID *guid, QofBook *book);
+#define gnc_budget_lookup_direct(g,b) gnc_budget_lookup(&(g),(b))
#endif // __BUDGET_H__
Index: gnucash/src/engine/gnc-engine.c
===================================================================
--- gnucash.orig/src/engine/gnc-engine.c
+++ gnucash/src/engine/gnc-engine.c
@@ -31,7 +31,7 @@
#include "AccountP.h"
#include "GroupP.h"
#include "SX-book-p.h"
-#include "gnc-budget-book-p.h"
+#include "gnc-budget.h"
#include "TransactionP.h"
#include "gnc-commodity.h"
#include "gnc-lot-p.h"
Index: gnucash/src/engine/gnc-engine.h
===================================================================
--- gnucash.orig/src/engine/gnc-engine.h
+++ gnucash/src/engine/gnc-engine.h
@@ -62,6 +62,7 @@
#define GNC_MOD_IMPORT "gnucash-import-export"
#define GNC_MOD_DRUID "gnucash-druids"
#define GNC_MOD_TEST "gnucash-tests"
+#define GNC_MOD_BUDGET "gnucash-budget"
//@}
/** @brief IDENTIFIERS
@@ -99,13 +100,29 @@
#define GNC_ID_SPLIT "Split"
#define GNC_ID_SCHEDXACTION "SchedXaction"
#define GNC_ID_BUDGET "Budget"
-#define GNC_ID_BUDGET_CATEGORY "BudgetCategory"
#define GNC_ID_SXTG "SXTGroup"
#define GNC_ID_SXTT "SXTTrans"
#define GNC_ID_TRANS "Trans"
/* TYPES **********************************************************/
+/* CAS: ISTM, it would make more sense to put the typedefs in their
+ corresponding header files, (e.g. Account.h), and to #include all
+ the engine API header files right here. After all, when I jump to
+ the definition "Account", I want to end up in Account.h, not this
+ file, like I do now.
+
+ Also, as it is now, if I want to use the engine api, I need to
+ include this header, plus all the other engine headers for the
+ types whose functions I call, so this header is providing almost no
+ benefit of aggregation. But, if it included all the headers I
+ could just include this file. Or would that cause a massive
+ recompile everytime one engine header changed?
+ Even if including all the headers here doesn't make sense, I think
+ distributing the stuff in the "Types" section does.
+*/
+
+
/** @brief Account in Gnucash.
* This is the typename for an account. The actual structure is
* defined in the private header AccountP.h, but no one outside the
Index: gnucash/src/engine/gw-engine-spec.scm
===================================================================
--- gnucash.orig/src/engine/gw-engine-spec.scm
+++ gnucash/src/engine/gw-engine-spec.scm
@@ -26,6 +26,7 @@
"#include <guid.h>\n"
"#include <Group.h>\n"
"#include <Query.h>\n"
+ "#include <gnc-budget.h>\n"
"#include <gnc-commodity.h>\n"
"#include <gnc-date.h>\n"
"#include <gnc-engine.h>\n"
@@ -260,6 +261,7 @@
;
(gw:wrap-value ws 'gnc:id-account '<gnc:id-type> "GNC_ID_ACCOUNT")
(gw:wrap-value ws 'gnc:id-book '<gnc:id-type> "GNC_ID_BOOK")
+(gw:wrap-value ws 'gnc:id-budget '<gnc:id-type> "GNC_ID_BUDGET")
(gw:wrap-value ws 'gnc:id-lot '<gnc:id-type> "GNC_ID_LOT")
(gw:wrap-value ws 'gnc:id-price '<gnc:id-type> "GNC_ID_PRICE")
(gw:wrap-value ws 'gnc:id-split '<gnc:id-type> "GNC_ID_SPLIT")
@@ -2491,6 +2493,87 @@ the timepair representing midday on that
'(((gw:glist-of (<gw:mchars> callee-owned) callee-owned) choices))
"Takes a list of installed Finance::Quote souces and records it internally.")
+
+;; Budget functions
+
+(gw:wrap-as-wct ws '<gnc:Budget*> "GncBudget *" "const GncBudget *")
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-guid
+ '<gnc:guid-scm>
+ "gnc_budget_return_guid"
+ '((<gnc:Budget*> budget))
+ "Gets the guid of the budget")
+
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-lookup
+ '<gnc:Budget*>
+ "gnc_budget_lookup_direct"
+ '((<gnc:guid-scm> guid)
+ (<gnc:Book*> book))
+ "Lookup a budget from its GUID.")
+
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-default
+ '<gnc:Budget*>
+ "gnc_budget_get_default"
+ '((<gnc:Book*> book))
+ "Get the default budget for the book.")
+
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-name
+ '(<gw:mchars> callee-owned const)
+ "gnc_budget_get_name"
+ '((<gnc:Budget*> budget))
+ "Get the brief name for the budget.")
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-num-periods
+ '<gw:unsigned-int>
+ "gnc_budget_get_num_periods"
+ '((<gnc:Budget*> budget))
+ "Get the number of periods in a budget.")
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-account-period-value
+ '<gnc:numeric>
+ "gnc_budget_get_account_period_value"
+ '((<gnc:Budget*> budget)
+ (<gnc:Account*> acct)
+ (<gw:unsigned-int> period_num)
+ )
+ "Get the budgeted value for the given account and budget period.")
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-account-period-actual-value
+ '<gnc:numeric>
+ "gnc_budget_get_account_period_actual_value"
+ '((<gnc:Budget*> budget)
+ (<gnc:Account*> acct)
+ (<gw:unsigned-int> period_num)
+ )
+ "Get the actual account value for the given account and budget period.")
+
+(gw:wrap-function
+ ws
+ 'gnc:budget-get-period-start-date
+ '<gnc:time-pair>
+ "gnc_budget_get_period_start_date"
+ '((<gnc:Budget*> budget)
+ (<gw:unsigned-int> period_num)
+ )
+ "Get the date that the given period begins.")
+
;;
;; gnc-hooks-scm.h
;; (and gnc-hooks.h)
--
More information about the gnucash-patches
mailing list