GnuCash 2.4.99
Data Structures | Defines | Typedefs | Enumerations | Functions
gnc-sx-instance-model.h File Reference
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include "gnc-numeric.h"
#include "SchedXaction.h"

Go to the source code of this file.

Data Structures

struct  _GncSxInstanceModel
struct  _GncSxInstanceModelClass
struct  _GncSxInstances
struct  _GncSxVariable
struct  _GncSxInstance
struct  _GncSxVariableNeeded
struct  _GncSxSummary

Defines

#define GNC_TYPE_SX_INSTANCE_MODEL   (gnc_sx_instance_model_get_type ())
#define GNC_SX_INSTANCE_MODEL(obj)   (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModel))
#define GNC_SX_INSTANCE_MODEL_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModelClass))
#define GNC_IS_SX_INSTANCE_MODEL(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_SX_INSTANCE_MODEL))
#define GNC_IS_SX_INSTANCE_MODEL_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_SX_INSTANCE_MODEL))
#define GNC_SX_INSTANCE_MODEL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_SX_INSTANCE_MODEL, GncSxInstanceModelClass))

Typedefs

typedef struct _GncSxInstanceModel GncSxInstanceModel
typedef struct
_GncSxInstanceModelClass 
GncSxInstanceModelClass
typedef struct _GncSxInstances GncSxInstances
typedef struct _GncSxVariable GncSxVariable
typedef struct _GncSxInstance GncSxInstance
typedef struct _GncSxVariableNeeded GncSxVariableNeeded
typedef struct _GncSxSummary GncSxSummary

Enumerations

enum  GncSxInstanceState {
  SX_INSTANCE_STATE_IGNORED, SX_INSTANCE_STATE_POSTPONED, SX_INSTANCE_STATE_TO_CREATE, SX_INSTANCE_STATE_REMINDER,
  SX_INSTANCE_STATE_CREATED, SX_INSTANCE_STATE_MAX_STATE
}

Functions

GType gnc_sx_instance_model_get_type (void)
GncSxInstanceModelgnc_sx_get_current_instances (void)
GncSxInstanceModelgnc_sx_get_instances (const GDate *range_end, gboolean include_disabled)
void gnc_sx_instance_model_update_sx_instances (GncSxInstanceModel *model, SchedXaction *sx)
void gnc_sx_instance_model_remove_sx_instances (GncSxInstanceModel *model, SchedXaction *sx)
GList * gnc_sx_instance_get_variables (GncSxInstance *inst)
Accountgnc_sx_get_template_transaction_account (const SchedXaction *sx)
GHashTable * gnc_sx_instance_get_variables_for_parser (GHashTable *instance_var_hash)
GncSxVariablegnc_sx_variable_new_full (gchar *name, gnc_numeric value, gboolean editable)
void gnc_sx_variable_free (GncSxVariable *var)
void gnc_sx_instance_model_change_instance_state (GncSxInstanceModel *model, GncSxInstance *instance, GncSxInstanceState new_state)
void gnc_sx_instance_model_set_variable (GncSxInstanceModel *model, GncSxInstance *instance, GncSxVariable *variable, gnc_numeric *new_value)
GList * gnc_sx_instance_model_check_variables (GncSxInstanceModel *model)
void gnc_sx_instance_model_effect_change (GncSxInstanceModel *model, gboolean auto_create_only, GList **created_transaction_guids, GList **creation_errors)
void gnc_sx_instance_model_summarize (GncSxInstanceModel *model, GncSxSummary *summary)
void gnc_sx_summary_print (const GncSxSummary *summary)
void gnc_sx_get_variables (SchedXaction *sx, GHashTable *var_hash)
int gnc_sx_parse_vars_from_formula (const char *formula, GHashTable *var_hash, gnc_numeric *result)
void gnc_sx_randomize_variables (GHashTable *vars)
GHashTable * gnc_g_hash_new_guid_numeric (void)
void gnc_sx_all_instantiate_cashflow (GList *all_sxes, const GDate *range_start, const GDate *range_end, GHashTable *map, GList **creation_errors)
GHashTable * gnc_sx_all_instantiate_cashflow_all (GDate range_start, GDate range_end)

Detailed Description

Definition in file gnc-sx-instance-model.h.


Function Documentation

GHashTable* gnc_g_hash_new_guid_numeric ( void  )

Returns a GHashTable<GUID*, gnc_numeric*> with no destructor for the key, but a destructor for the value set.

The returned value must be free'd with g_hash_table_destroy or g_hash_table_unref.

Definition at line 1479 of file gnc-sx-instance-model.c.

{
    return g_hash_table_new_full (guid_hash_to_guint, guid_g_hash_table_equal,
                                  NULL, gnc_numeric_free);
}
void gnc_sx_all_instantiate_cashflow ( GList *  all_sxes,
const GDate *  range_start,
const GDate *  range_end,
GHashTable *  map,
GList **  creation_errors 
)

Instantiates the cash flow of all given SXs (in the given GList<SchedXAction*>) into the GHashTable<GUID*, gnc_numeric*> for the given date range. Each SX is counted with multiplicity as it has occurrences in the given date range.

The creation_errors list, if non-NULL, receive any errors that occurred during creation, similar as in gnc_sx_instance_model_effect_change().

Definition at line 1720 of file gnc-sx-instance-model.c.

{
    SxAllCashflow userdata;
    userdata.hash = map;
    userdata.creation_errors = creation_errors;
    userdata.range_start = range_start;
    userdata.range_end = range_end;

    /* The work is done in the callback for each SX */
    g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
}
GHashTable* gnc_sx_all_instantiate_cashflow_all ( GDate  range_start,
GDate  range_end 
)

Simplified wrapper around gnc_sx_all_instantiate_cashflow(): Run that function on all SX of the current book for the given date range. Ignore any potential error messages. Returns a newly allocated GHashTable with the result, which is a GHashTable<GUID*, gnc_numeric*>, identical to what gnc_g_hash_new_guid_numeric() would return. The returned value must be free'd with g_hash_table_destroy.

Definition at line 1735 of file gnc-sx-instance-model.c.

{
    GHashTable *result_map = gnc_g_hash_new_guid_numeric();
    GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
    gnc_sx_all_instantiate_cashflow(all_sxes,
                                    &range_start, &range_end,
                                    result_map, NULL);
    return result_map;
}
GncSxInstanceModel* gnc_sx_get_current_instances ( void  )

Shorthand for get_instances(now, FALSE);

Definition at line 425 of file gnc-sx-instance-model.c.

{
    GDate now;
    g_date_clear(&now, 1);
    g_date_set_time_t(&now, time(NULL));
    return gnc_sx_get_instances(&now, FALSE);
}
GncSxInstanceModel* gnc_sx_get_instances ( const GDate *  range_end,
gboolean  include_disabled 
)

Allocates a new SxInstanceModel and fills it with generated instances for all scheduled transactions up to the given range_end date.

The caller must unref the returned object by g_object_unref(G_OBJECT(inst_model)); when no longer in use.

Definition at line 434 of file gnc-sx-instance-model.c.

{
    GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
    GncSxInstanceModel *instances;

    g_assert(range_end != NULL);
    g_assert(g_date_valid(range_end));

    instances = gnc_sx_instance_model_new();
    instances->include_disabled = include_disabled;
    instances->range_end = *range_end;

    if (include_disabled)
    {
        instances->sx_instance_list = gnc_g_list_map(all_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
    }
    else
    {
        GList *sx_iter = g_list_first(all_sxes);
        GList *enabled_sxes = NULL;

        for (; sx_iter != NULL; sx_iter = sx_iter->next)
        {
            SchedXaction *sx = (SchedXaction*)sx_iter->data;
            if (xaccSchedXactionGetEnabled(sx))
            {
                enabled_sxes = g_list_append(enabled_sxes, sx);
            }
        }
        instances->sx_instance_list = gnc_g_list_map(enabled_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
        g_list_free(enabled_sxes);
    }

    return instances;
}
GList* gnc_sx_instance_get_variables ( GncSxInstance inst)
Returns:
GList<GncSxVariable*>. Caller owns the list, but not the items.

Definition at line 352 of file gnc-sx-instance-model.c.

{
    GList *vars = NULL;
    g_hash_table_foreach(inst->variable_bindings, _build_list_from_hash_elts, &vars);
    return vars;
}
GHashTable* gnc_sx_instance_get_variables_for_parser ( GHashTable *  instance_var_hash)
Returns:
caller-owned data struct.
caller-owned.

Definition at line 99 of file gnc-sx-instance-model.c.

{
    GHashTable *parser_vars;
    parser_vars = g_hash_table_new(g_str_hash, g_str_equal);
    g_hash_table_foreach(instance_var_hash, (GHFunc)_sx_var_to_raw_numeric, parser_vars);
    return parser_vars;
}
void gnc_sx_instance_model_change_instance_state ( GncSxInstanceModel model,
GncSxInstance instance,
GncSxInstanceState  new_state 
)

There is a constraint around a sequence of upcoming instance states. In short: the last-created state and a list of postponed instances are modeled, but upcoming reminders are not. As such, a reminder can never be before any other (modeled) instance type. For instance, the following sequences are disallowed:

[...] remind <- will be lost/skipped over; must be converted to `postponed`. to-create <- this will be the last-recorded state. [...]

[...] remind <- same as previous; will be lost/skipped; must be `postponed`. postponed [...]

remind <- same... ignore [...]

As such, the SinceLastRun model will enforce that there are no previous `remind` instances at every state change. They will be silently converted to `postponed`-state transactions.

Definition at line 1316 of file gnc-sx-instance-model.c.

{
    if (instance->state == new_state)
        return;

    instance->state = new_state;

    // ensure 'remind' constraints are met:
    {
        GList *inst_iter;
        inst_iter = g_list_find(instance->parent->instance_list, instance);
        g_assert(inst_iter != NULL);
        if (instance->state != SX_INSTANCE_STATE_REMINDER)
        {
            // iterate backwards, making sure reminders are changed to 'postponed'
            for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
            {
                GncSxInstance *prev_inst = (GncSxInstance*)inst_iter->data;
                if (prev_inst->state != SX_INSTANCE_STATE_REMINDER)
                    continue;
                prev_inst->state = SX_INSTANCE_STATE_POSTPONED;
            }
        }
        else
        {
            // iterate forward, make sure transactions are set to 'remind'
            for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
            {
                GncSxInstance *next_inst = (GncSxInstance*)inst_iter->data;
                if (next_inst->state == SX_INSTANCE_STATE_REMINDER)
                    continue;
                next_inst->state = SX_INSTANCE_STATE_REMINDER;
            }
        }
    }

    g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
}
GList* gnc_sx_instance_model_check_variables ( GncSxInstanceModel model)
Returns:
List<GncSxVariableNeeded> of unbound {instance,variable} pairs; the caller owns the list and the items.

Definition at line 1377 of file gnc-sx-instance-model.c.

{
    GList *rtn = NULL;
    GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;

    for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
    {
        GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
        for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
        {
            GncSxInstance *inst = (GncSxInstance*)inst_iter->data;

            if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
                continue;

            g_hash_table_foreach(inst->variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
            for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
            {
                GncSxVariable *var = (GncSxVariable*)var_iter->data;
                if (gnc_numeric_check(var->value) != GNC_ERROR_OK)
                {
                    GncSxVariableNeeded *need = g_new0(GncSxVariableNeeded, 1);
                    need->instance = inst;
                    need->variable = var;
                    rtn = g_list_append(rtn, need);
                }
            }
            g_list_free(var_list);
            var_list = NULL;
        }
    }
    return rtn;
}
void gnc_sx_instance_model_effect_change ( GncSxInstanceModel model,
gboolean  auto_create_only,
GList **  created_transaction_guids,
GList **  creation_errors 
)

Really ("effectively") create the transactions from the SX instances in the given model.

Definition at line 1232 of file gnc-sx-instance-model.c.

{
    GList *iter;
    for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
    {
        GList *instance_iter;
        GncSxInstances *instances = (GncSxInstances*)iter->data;
        GDate *last_occur_date;
        gint instance_count = 0;
        gint remain_occur_count = 0;

        // If there are no instances, then skip; specifically, skip
        // re-setting SchedXaction fields, which will dirty the book
        // spuriously.
        if (g_list_length(instances->instance_list) == 0)
            continue;

        last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
        instance_count = gnc_sx_get_instance_count(instances->sx, NULL);
        remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);

        for (instance_iter = instances->instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
        {
            GncSxInstance *inst = (GncSxInstance*)instance_iter->data;
            gboolean sx_is_auto_create;

            xaccSchedXactionGetAutoCreate(inst->parent->sx, &sx_is_auto_create, NULL);
            if (auto_create_only && !sx_is_auto_create)
            {
                if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
                {
                    break;
                }
                continue;
            }

            if (inst->orig_state == SX_INSTANCE_STATE_POSTPONED
                    && inst->state != SX_INSTANCE_STATE_POSTPONED)
            {
                // remove from postponed list
                g_assert(inst->temporal_state != NULL);
                gnc_sx_remove_defer_instance(inst->parent->sx, inst->temporal_state);
            }

            switch (inst->state)
            {
            case SX_INSTANCE_STATE_CREATED:
                // nop: we've already processed this.
                break;
            case SX_INSTANCE_STATE_IGNORED:
                increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
                break;
            case SX_INSTANCE_STATE_POSTPONED:
                if (inst->orig_state != SX_INSTANCE_STATE_POSTPONED)
                {
                    gnc_sx_add_defer_instance(instances->sx, inst->temporal_state);
                }
                increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
                break;
            case SX_INSTANCE_STATE_TO_CREATE:
                create_transactions_for_instance(inst, created_transaction_guids, creation_errors);
                increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
                gnc_sx_instance_model_change_instance_state(model, inst, SX_INSTANCE_STATE_CREATED);
                break;
            case SX_INSTANCE_STATE_REMINDER:
                // do nothing
                // assert no non-remind instances after this?
                break;
            default:
                g_assert_not_reached();
                break;
            }
        }

        xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
        gnc_sx_set_instance_count(instances->sx, instance_count);
        xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
    }
}
void gnc_sx_instance_model_summarize ( GncSxInstanceModel model,
GncSxSummary summary 
)
Parameters:
summaryCaller-provided, populated with a summarization of the state of the model. Specifically, used to determine if there are SLR SXes that need either auto-creation or user-interaction.

Definition at line 1412 of file gnc-sx-instance-model.c.

{
    GList *sx_iter, *inst_iter;

    g_return_if_fail(model != NULL);
    g_return_if_fail(summary != NULL);

    summary->need_dialog = FALSE;
    summary->num_instances = 0;
    summary->num_to_create_instances = 0;
    summary->num_auto_create_instances = 0;
    summary->num_auto_create_no_notify_instances = 0;

    for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
    {
        GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
        gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
        xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
        for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
        {
            GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
            summary->num_instances++;

            if (inst->state == SX_INSTANCE_STATE_TO_CREATE)
            {
                if (sx_is_auto_create)
                {
                    if (!sx_notify)
                    {
                        summary->num_auto_create_no_notify_instances++;
                    }
                    else
                    {
                        summary->num_auto_create_instances++;
                    }
                }
                else
                {
                    summary->num_to_create_instances++;
                }
            }
        }
    }

    // if all the instances are 'auto-create, no-notify', then we don't need
    // the dialog.
    summary->need_dialog
    = (summary->num_instances != 0
       && summary->num_auto_create_no_notify_instances != summary->num_instances);
}
void gnc_sx_instance_model_update_sx_instances ( GncSxInstanceModel model,
SchedXaction sx 
)

Regenerates and updates the GncSxInstances* for the given SX. Model consumers are probably going to call this in response to seeing the "update" signal, unless they need to be doing something else like finishing an iteration over an existing GncSxInstances*.

Definition at line 742 of file gnc-sx-instance-model.c.

{
    GncSxInstances *existing, *new_instances;
    GList *link;

    link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
    if (link == NULL)
    {
        g_critical("couldn't find sx [%p]\n", sx);
        return;
    }

    // merge the new instance data into the existing structure, mutating as little as possible.
    existing = (GncSxInstances*)link->data;
    new_instances = _gnc_sx_gen_instances((gpointer)sx, &model->range_end);
    existing->sx = new_instances->sx;
    existing->next_instance_date = new_instances->next_instance_date;
    {
        GList *existing_iter, *new_iter;
        gboolean existing_remain, new_remain;

        // step through the lists pairwise, and retain the existing
        // instance if the dates align, as soon as they don't stop and
        // cleanup.
        existing_iter = existing->instance_list;
        new_iter = new_instances->instance_list;
        for (; existing_iter != NULL && new_iter != NULL; existing_iter = existing_iter->next, new_iter = new_iter->next)
        {
            GncSxInstance *existing_inst, *new_inst;
            gboolean same_instance_date;
            existing_inst = (GncSxInstance*)existing_iter->data;
            new_inst = (GncSxInstance*)new_iter->data;

            same_instance_date = g_date_compare(&existing_inst->date, &new_inst->date) == 0;
            if (!same_instance_date)
                break;
        }

        existing_remain = (existing_iter != NULL);
        new_remain = (new_iter != NULL);

        if (existing_remain)
        {
            // delete excess
            gnc_g_list_cut(&existing->instance_list, existing_iter);
            g_list_foreach(existing_iter, (GFunc)gnc_sx_instance_free, NULL);
        }

        if (new_remain)
        {
            // append new
            GList *new_iter_iter;
            gnc_g_list_cut(&new_instances->instance_list, new_iter);

            for (new_iter_iter = new_iter; new_iter_iter != NULL; new_iter_iter = new_iter_iter->next)
            {
                GncSxInstance *inst = (GncSxInstance*)new_iter_iter->data;
                inst->parent = existing;
                existing->instance_list = g_list_append(existing->instance_list, new_iter_iter->data);
            }
            g_list_free(new_iter);
        }
    }

    // handle variables
    {
        HashListPair removed_cb_data, added_cb_data;
        GList *removed_var_names = NULL, *added_var_names = NULL;
        GList *inst_iter = NULL;

        removed_cb_data.hash = new_instances->variable_names;
        removed_cb_data.list = NULL;
        g_hash_table_foreach(existing->variable_names, (GHFunc)_find_unreferenced_vars, &removed_cb_data);
        removed_var_names = removed_cb_data.list;
        g_debug("%d removed variables", g_list_length(removed_var_names));

        added_cb_data.hash = existing->variable_names;
        added_cb_data.list = NULL;
        g_hash_table_foreach(new_instances->variable_names, (GHFunc)_find_unreferenced_vars, &added_cb_data);
        added_var_names = added_cb_data.list;
        g_debug("%d added variables", g_list_length(added_var_names));

        if (existing->variable_names != NULL)
        {
            g_hash_table_destroy(existing->variable_names);
        }
        existing->variable_names = new_instances->variable_names;
        new_instances->variable_names = NULL;

        for (inst_iter = existing->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
        {
            GList *var_iter;
            GncSxInstance *inst = (GncSxInstance*)inst_iter->data;

            for (var_iter = removed_var_names; var_iter != NULL; var_iter = var_iter->next)
            {
                gchar *to_remove_key = (gchar*)var_iter->data;
                g_hash_table_remove(inst->variable_bindings, to_remove_key);
            }

            for (var_iter = added_var_names; var_iter != NULL; var_iter = var_iter->next)
            {
                gchar *to_add_key = (gchar*)var_iter->data;
                if (!g_hash_table_lookup_extended(
                            inst->variable_bindings, to_add_key, NULL, NULL))
                {
                    GncSxVariable *parent_var
                    = g_hash_table_lookup(existing->variable_names, to_add_key);
                    GncSxVariable *var_copy;

                    g_assert(parent_var != NULL);
                    var_copy = gnc_sx_variable_new_copy(parent_var);
                    g_hash_table_insert(inst->variable_bindings, g_strdup(to_add_key), var_copy);
                }
            }
        }
    }
    gnc_sx_instances_free(new_instances);
}
void gnc_sx_summary_print ( const GncSxSummary summary)

Debug output to trace file

Definition at line 1464 of file gnc-sx-instance-model.c.

{
    g_message("num_instances: %d", summary->num_instances);
    g_message("num_to_create: %d", summary->num_to_create_instances);
    g_message("num_auto_create_instances: %d", summary->num_auto_create_instances);
    g_message("num_auto_create_no_notify_instances: %d", summary->num_auto_create_no_notify_instances);
    g_message("need dialog? %s", summary->need_dialog ? "true" : "false");
}
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines