35#include <glib/gi18n.h>
36#include <glib-object.h>
48#include "gnc-exp-parser.h"
55#define G_LOG_DOMAIN "gnc.app-utils.sx"
56static QofLogModule log_module = G_LOG_DOMAIN;
62#define REPORT_ERROR(list, format, ...) do { \
63 g_critical(format, __VA_ARGS__); \
65 *list = g_list_append(*list, g_strdup_printf(_(format), __VA_ARGS__)); \
68typedef struct _SxTxnCreationData
71 GList **created_txn_guids;
72 GList **creation_errors;
80 gint qof_event_handler_id;
83 gboolean include_disabled;
84 GList *sx_instance_list;
87static GncSxInstanceModel* gnc_sx_instance_model_new(
void);
89static GncSxInstance* gnc_sx_instance_new(
GncSxInstances *parent, GncSxInstanceState state, GDate *date,
void *temporal_state, gint sequence_num);
91static gint _get_vars_helper(Transaction *txn,
void *var_hash_data);
95static void _gnc_sx_instance_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data);
96static gnc_commodity* get_transaction_currency(
SxTxnCreationData *creation_data, SchedXaction *sx, Transaction *template_txn);
107 REMOVING, UPDATED, ADDED,
111static guint signals[LAST_SIGNAL] = { 0 };
114scrub_sx_split_numeric (Split* split, gboolean is_credit, GList **changes)
116 const char *formula = is_credit ?
"sx-credit-formula" :
"sx-debit-formula";
117 const char *
numeric = is_credit ?
"sx-credit-numeric" :
"sx-debit-numeric";
119 gnc_numeric *numval = NULL;
120 GHashTable *parser_vars = g_hash_table_new_full
121 (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_free);
123 gnc_numeric amount = gnc_numeric_zero ();
124 gboolean parse_result = FALSE;
131 parse_result = gnc_exp_parser_parse_separate_vars (formval, &amount,
132 &error_loc, parser_vars);
134 if (!parse_result || g_hash_table_size (parser_vars) != 0)
135 amount = gnc_numeric_zero ();
141 change->amount = amount;
142 *changes = g_list_prepend (*changes, change);
145 g_hash_table_destroy (parser_vars);
154gnc_sx_scrub_split_numerics (gpointer psplit, gpointer puser)
156 Split *split = GNC_SPLIT (psplit);
158 GList *changes = NULL;
159 scrub_sx_split_numeric (split, TRUE, &changes);
160 scrub_sx_split_numeric (split, FALSE, &changes);
165 for (GList *n = changes; n; n = n->next)
169 change->name, &change->amount,
173 g_list_free_full (changes, g_free);
177_sx_var_to_raw_numeric(gchar *name,
GncSxVariable *var, GHashTable *parser_var_hash)
179 g_hash_table_insert(parser_var_hash, g_strdup(name), &var->
value);
183_var_numeric_to_sx_var(gchar *name, gnc_numeric *num, GHashTable *sx_var_hash)
186 if (!g_hash_table_lookup_extended(sx_var_hash, name, NULL, &p_var))
188 p_var = (gpointer)gnc_sx_variable_new(name);
189 g_hash_table_insert(sx_var_hash, g_strdup(name), p_var);
195_wipe_parsed_sx_var(gchar *key,
GncSxVariable *var, gpointer unused_user_data)
201split_is_marker(Split *split)
203 gchar *credit_formula = NULL;
204 gchar *debit_formula = NULL;
205 gboolean split_is_marker = TRUE;
208 "sx-credit-formula", &credit_formula,
209 "sx-debit-formula", &debit_formula,
212 if ((credit_formula && *credit_formula) ||
213 (debit_formula && *debit_formula))
214 split_is_marker = FALSE;
216 g_free(credit_formula);
217 g_free(debit_formula);
218 return split_is_marker;
225gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash)
227 GHashTable *parser_vars;
229 parser_vars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
230 g_hash_table_foreach(instance_var_hash, (GHFunc)_sx_var_to_raw_numeric, parser_vars);
235gnc_sx_parse_vars_from_formula(
const char *formula,
236 GHashTable *var_hash,
242 GHashTable *parser_vars;
245 parser_vars = gnc_sx_instance_get_variables_for_parser(var_hash);
247 num = gnc_numeric_zero();
248 if (!gnc_exp_parser_parse_separate_vars(formula, &num, &errLoc, parser_vars))
254 g_hash_table_foreach(parser_vars, (GHFunc)_var_numeric_to_sx_var, var_hash);
255 g_hash_table_destroy(parser_vars);
266gnc_sx_variable_new(gchar *name)
269 var->name = g_strdup(name);
271 var->editable = TRUE;
276gnc_sx_variable_new_full(gchar *name, gnc_numeric value, gboolean editable)
280 var->editable = editable;
289 var->editable = to_copy->editable;
301var_name_from_commodities(gnc_commodity* split_c, gnc_commodity* txn_c)
305 gchar* var_name = g_strdup_printf (
"%s -> %s",
306 split_m ? split_m :
"(null)",
307 txn_m ? txn_m :
"(null)");
309 DEBUG(
"var_name is %s", var_name);
314_get_vars_helper(Transaction *txn,
void *var_hash_data)
316 GHashTable *var_hash = (GHashTable*)var_hash_data;
319 gchar *credit_formula = NULL;
320 gchar *debit_formula = NULL;
321 gnc_commodity *txn_cmdty = get_transaction_currency(NULL, NULL, txn);
324 if (split_list == NULL)
329 for ( ; split_list; split_list = split_list->next)
331 gnc_commodity *split_cmdty = NULL;
334 gboolean split_is_marker = TRUE;
336 s = (Split*)split_list->data;
339 "sx-account", &acct_guid,
340 "sx-credit-formula", &credit_formula,
341 "sx-debit-formula", &debit_formula,
344 guid_free (acct_guid);
347 if (credit_formula && strlen(credit_formula) != 0)
349 gnc_sx_parse_vars_from_formula(credit_formula, var_hash, NULL);
350 split_is_marker = FALSE;
352 if (debit_formula && strlen(debit_formula) != 0)
354 gnc_sx_parse_vars_from_formula(debit_formula, var_hash, NULL);
355 split_is_marker = FALSE;
357 g_free (credit_formula);
358 g_free (debit_formula);
368 var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
369 var = gnc_sx_variable_new(var_name);
370 g_hash_table_insert(var_hash, g_strdup(var->name), var);
379gnc_sx_get_template_transaction_account(
const SchedXaction *sx)
381 Account *template_root, *sx_template_acct;
387 return sx_template_acct;
391gnc_sx_get_variables(SchedXaction *sx, GHashTable *var_hash)
393 Account *sx_template_acct = gnc_sx_get_template_transaction_account(sx);
398_set_var_to_random_value(gchar *key,
GncSxVariable *var, gpointer unused_user_data)
406 var->
value = gnc_numeric_create(g_random_int_range(1, 1000), 1);
410gnc_sx_randomize_variables(GHashTable *vars)
412 g_hash_table_foreach(vars, (GHFunc)_set_var_to_random_value, NULL);
416_clone_sx_var_hash_entry(gpointer key, gpointer value, gpointer user_data)
418 GHashTable *to = (GHashTable*)user_data;
421 g_hash_table_insert(to, g_strdup(key), var);
425gnc_sx_instance_new(
GncSxInstances *parent, GncSxInstanceState state, GDate *date,
void *temporal_state, gint sequence_num)
431 g_date_clear(&rtn->
date, 1);
435 if (! parent->variable_names_parsed)
437 parent->variable_names = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free);
438 gnc_sx_get_variables(parent->sx, parent->variable_names);
439 g_hash_table_foreach(parent->variable_names, (GHFunc)_wipe_parsed_sx_var, NULL);
440 parent->variable_names_parsed = TRUE;
443 rtn->
variable_bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free);
444 g_hash_table_foreach(parent->variable_names, _clone_sx_var_hash_entry, rtn->
variable_bindings);
447 int instance_i_value;
452 i_num = gnc_numeric_create(instance_i_value, 1);
453 as_var = gnc_sx_variable_new_full(
"i", i_num, FALSE);
462_compare_GncSxVariables(gconstpointer a, gconstpointer b)
468_build_list_from_hash_elts(gpointer key, gpointer value, gpointer user_data)
470 GList **list = (GList**)user_data;
471 *list = g_list_prepend (*list, value);
478 g_hash_table_foreach(inst->
variable_bindings, _build_list_from_hash_elts, &vars);
479 return g_list_sort (vars, _compare_GncSxVariables);
483_gnc_sx_gen_instances(gpointer *data, gpointer user_data)
486 GList *instlist = NULL;
487 SchedXaction *sx = (SchedXaction*)data;
488 const GDate *range_end = (
const GDate*)user_data;
489 GDate creation_end, remind_end;
495 creation_end = *range_end;
496 g_date_add_days(&creation_end, xaccSchedXactionGetAdvanceCreation(sx));
497 remind_end = creation_end;
498 g_date_add_days(&remind_end, xaccSchedXactionGetAdvanceReminder(sx));
503 for ( ; postponed != NULL; postponed = postponed->next)
509 g_date_clear(&inst_date, 1);
512 inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_POSTPONED,
513 &inst_date, postponed->data, seq_num);
514 instlist = g_list_prepend (instlist, inst);
522 g_date_clear(&cur_date, 1);
524 instances->next_instance_date = cur_date;
525 while (g_date_valid(&cur_date) && g_date_compare(&cur_date, &creation_end) <= 0)
530 inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_TO_CREATE,
531 &cur_date, temporal_state, seq_num);
532 instlist = g_list_prepend (instlist, inst);
538 while (g_date_valid(&cur_date) &&
539 g_date_compare(&cur_date, &remind_end) <= 0)
544 inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_REMINDER,
545 &cur_date, temporal_state, seq_num);
546 instlist = g_list_prepend (instlist, inst);
559sx_gen_select_instances(gpointer *data, gpointer user_data)
561 GncSxInstances *instances = _gnc_sx_gen_instances(data, user_data);
564 if (instlist == NULL)
566 SchedXaction *sx = instances->sx;
570 if (g_date_valid(&cur_date))
573 GncSxInstance *inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_REMINDER,
574 &cur_date, temporal_state, seq_num);
586sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
588 return model->include_disabled ?
589 sx_gen_select_instances((gpointer)sx, (gpointer)&model->range_end) :
590 _gnc_sx_gen_instances((gpointer)sx, (gpointer)&model->range_end);
594gnc_sx_get_current_instances(
void)
597 g_date_clear(&now, 1);
599 return gnc_sx_get_instances(&now, FALSE);
603gnc_sx_get_instances(
const GDate *range_end, gboolean include_disabled)
605 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
606 GncSxInstanceModel *instances;
608 g_assert(range_end != NULL);
609 g_assert(g_date_valid(range_end));
611 instances = gnc_sx_instance_model_new();
612 instances->include_disabled = include_disabled;
613 instances->range_end = *range_end;
615 if (include_disabled)
617 instances->sx_instance_list =
gnc_g_list_map(all_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
621 GList *sx_iter = g_list_first(all_sxes);
622 GList *enabled_sxes = NULL;
624 for (; sx_iter != NULL; sx_iter = sx_iter->next)
626 SchedXaction *sx = (SchedXaction*)sx_iter->data;
627 if (xaccSchedXactionGetEnabled(sx))
629 enabled_sxes = g_list_prepend (enabled_sxes, sx);
632 enabled_sxes = g_list_reverse (enabled_sxes);
633 instances->sx_instance_list =
gnc_g_list_map(enabled_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
634 g_list_free(enabled_sxes);
641gnc_sx_get_select_instances(GList *sel_sxes)
643 GncSxInstanceModel *instances;
646 g_date_clear(&now, 1);
649 instances = gnc_sx_instance_model_new();
650 instances->include_disabled = TRUE;
651 instances->range_end = now;
653 instances->sx_instance_list =
gnc_g_list_map(sel_sxes, (GncGMapFunc)sx_gen_select_instances, (gpointer)&now);
658G_DEFINE_TYPE (GncSxInstanceModel, gnc_sx_instance_model, G_TYPE_OBJECT)
660static GncSxInstanceModel*
661gnc_sx_instance_model_new(
void)
663 return GNC_SX_INSTANCE_MODEL(g_object_new(GNC_TYPE_SX_INSTANCE_MODEL, NULL));
667gnc_sx_instance_model_dispose(GObject *
object)
669 GncSxInstanceModel *model;
670 g_return_if_fail(
object != NULL);
671 model = GNC_SX_INSTANCE_MODEL(
object);
673 g_return_if_fail(!model->disposed);
674 model->disposed = TRUE;
678 G_OBJECT_CLASS(gnc_sx_instance_model_parent_class)->dispose(
object);
698 GList *instance_iter;
706 instances->sx = NULL;
708 for (instance_iter = instances->
instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
711 gnc_sx_instance_free(inst);
720gnc_sx_instance_model_finalize (GObject *
object)
722 GncSxInstanceModel *model;
725 g_return_if_fail(
object != NULL);
727 model = GNC_SX_INSTANCE_MODEL(
object);
728 for (sx_list_iter = model->sx_instance_list; sx_list_iter != NULL; sx_list_iter = sx_list_iter->next)
731 gnc_sx_instances_free(instances);
733 g_list_free(model->sx_instance_list);
734 model->sx_instance_list = NULL;
736 G_OBJECT_CLASS(gnc_sx_instance_model_parent_class)->finalize(
object);
740gnc_sx_instance_model_class_init (GncSxInstanceModelClass *klass)
742 GObjectClass *object_class = G_OBJECT_CLASS(klass);
744 object_class->dispose = gnc_sx_instance_model_dispose;
745 object_class->finalize = gnc_sx_instance_model_finalize;
748 g_signal_new(
"removing",
749 GNC_TYPE_SX_INSTANCE_MODEL,
754 g_cclosure_marshal_VOID__POINTER,
760 g_signal_new(
"updated",
761 GNC_TYPE_SX_INSTANCE_MODEL,
766 g_cclosure_marshal_VOID__POINTER,
772 g_signal_new(
"added",
773 GNC_TYPE_SX_INSTANCE_MODEL,
778 g_cclosure_marshal_VOID__POINTER,
785gnc_sx_instance_model_init(GncSxInstanceModel *inst)
787 g_date_clear(&inst->range_end, 1);
788 inst->sx_instance_list = NULL;
793_gnc_sx_instance_find_by_sx(
GncSxInstances *in_list_instances, SchedXaction *sx_to_find)
795 if (in_list_instances->sx == sx_to_find)
801_gnc_sx_instance_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data)
803 GncSxInstanceModel *instances = GNC_SX_INSTANCE_MODEL(user_data);
810 if (!(GNC_IS_SX(ent) || GNC_IS_SXES(ent)))
816 gboolean sx_is_in_model = FALSE;
820 sx_is_in_model = (g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx) != NULL);
821 if (event_type & QOF_EVENT_MODIFY)
825 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
827 g_signal_emit_by_name(instances,
"updated", (gpointer)sx);
832 g_signal_emit_by_name(instances,
"removing", (gpointer)sx);
838 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
839 if (g_list_find(all_sxes, sx) && (!instances->include_disabled && xaccSchedXactionGetEnabled(sx)))
842 instances->sx_instance_list = g_list_append(instances->sx_instance_list, sx_instances(instances, sx));
843 g_signal_emit_by_name(instances,
"added", (gpointer)sx);
849 else if (GNC_IS_SXES(ent))
851 SchedXaction *sx = GNC_SX(evt_data);
853 if (event_type & GNC_EVENT_ITEM_REMOVED)
855 GList *instances_link;
856 instances_link = g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
857 if (instances_link != NULL)
859 g_signal_emit_by_name(instances,
"removing", (gpointer)sx);
861 else if (instances->include_disabled)
863 g_warning(
"could not remove instances that do not exist in the model");
868 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
871 instances->sx_instance_list = g_list_append(instances->sx_instance_list, sx_instances(instances, sx));
872 g_signal_emit_by_name(instances,
"added", (gpointer)sx);
879typedef struct _HashListPair
886_find_unreferenced_vars(gchar *key,
890 if (cb_pair->hash == NULL ||
891 !g_hash_table_lookup_extended(cb_pair->hash, key, NULL, NULL))
893 DEBUG(
"variable [%s] not found", key);
894 cb_pair->list = g_list_prepend (cb_pair->list, key);
899gnc_sx_instance_model_update_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
904 link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
907 g_critical(
"couldn't find sx [%p]\n", sx);
913 new_instances = sx_instances(model, sx);
914 existing->sx = new_instances->sx;
915 existing->next_instance_date = new_instances->next_instance_date;
917 GList *existing_iter, *new_iter;
918 gboolean existing_remain, new_remain;
925 for (; existing_iter != NULL && new_iter != NULL; existing_iter = existing_iter->next, new_iter = new_iter->next)
928 gboolean same_instance_date;
932 same_instance_date = g_date_compare(&existing_inst->
date, &new_inst->
date) == 0;
933 if (!same_instance_date)
937 existing_remain = (existing_iter != NULL);
938 new_remain = (new_iter != NULL);
944 g_list_foreach(existing_iter, (GFunc)gnc_sx_instance_free, NULL);
950 GList *new_iter_iter;
953 for (new_iter_iter = new_iter; new_iter_iter != NULL; new_iter_iter = new_iter_iter->next)
959 g_list_free(new_iter);
965 GList *removed_var_names = NULL, *added_var_names = NULL;
966 GList *inst_iter = NULL;
972 removed_cb_data.list = NULL;
973 g_hash_table_foreach(existing->
variable_names, (GHFunc)_find_unreferenced_vars, &removed_cb_data);
974 removed_var_names = g_list_reverse (removed_cb_data.list);
976 DEBUG(
"%d removed variables", g_list_length(removed_var_names));
982 added_cb_data.list = NULL;
983 g_hash_table_foreach(new_instances->
variable_names, (GHFunc)_find_unreferenced_vars, &added_cb_data);
984 added_var_names = g_list_reverse (added_cb_data.list);
986 DEBUG(
"%d added variables", g_list_length(added_var_names));
995 for (inst_iter = existing->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1000 for (var_iter = removed_var_names; var_iter != NULL; var_iter = var_iter->next)
1002 gchar *to_remove_key = (gchar*)var_iter->data;
1006 for (var_iter = added_var_names; var_iter != NULL; var_iter = var_iter->next)
1008 gchar *to_add_key = (gchar*)var_iter->data;
1009 if (!g_hash_table_lookup_extended(
1016 g_assert(parent_var != NULL);
1017 var_copy = gnc_sx_variable_new_copy(parent_var);
1023 gnc_sx_instances_free(new_instances);
1027gnc_sx_instance_model_remove_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
1029 GList *instance_link = NULL;
1031 instance_link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
1032 if (instance_link == NULL)
1034 g_warning(
"instance not found!\n");
1038 model->sx_instance_list = g_list_remove_link(model->sx_instance_list, instance_link);
1043increment_sx_state(
GncSxInstance *inst, GDate **last_occur_date,
int *instance_count,
int *remain_occur_count)
1045 if (!g_date_valid(*last_occur_date)
1046 || (g_date_valid(*last_occur_date)
1047 && g_date_compare(*last_occur_date, &inst->
date) <= 0))
1049 *last_occur_date = &inst->
date;
1054 if (*remain_occur_count > 0)
1056 *remain_occur_count -= 1;
1061_get_template_split_account(
const SchedXaction* sx,
1062 const Split *template_split,
1064 GList **creation_errors)
1066 gboolean success = TRUE;
1069 "sx-account", &acct_guid,
1072 if (!*split_acct && sx && creation_errors)
1077 gchar* err = N_(
"Unknown account for guid [%s], cancelling SX [%s] creation.");
1079 REPORT_ERROR(creation_errors, err, guid_str, xaccSchedXactionGetName(sx));
1083 guid_free (acct_guid);
1088_get_sx_formula_value(
const SchedXaction* sx,
1089 const Split *template_split,
1091 GList **creation_errors,
1092 const char *formula_key,
1093 const char* numeric_key,
1094 GHashTable *variable_bindings)
1097 char *formula_str = NULL, *parseErrorLoc = NULL;
1098 gnc_numeric *numeric_val = NULL;
1100 formula_key, &formula_str,
1101 numeric_key, &numeric_val,
1104 if ((variable_bindings == NULL ||
1105 g_hash_table_size (variable_bindings) == 0) &&
1106 numeric_val != NULL &&
1113 numeric->num = numeric_val->num;
1114 numeric->denom = numeric_val->denom;
1115 g_free (formula_str);
1116 g_free (numeric_val);
1120 if (formula_str != NULL && strlen(formula_str) != 0)
1122 GHashTable *parser_vars = NULL;
1123 if (variable_bindings)
1125 parser_vars = gnc_sx_instance_get_variables_for_parser(variable_bindings);
1127 if (!gnc_exp_parser_parse_separate_vars(formula_str,
1132 gchar *err = N_(
"Error parsing SX [%s] key [%s]=formula [%s] at [%s]: %s.");
1133 REPORT_ERROR(creation_errors, err,
1134 xaccSchedXactionGetName(sx),
1138 gnc_exp_parser_error_string());
1141 if (parser_vars != NULL)
1143 g_hash_table_destroy(parser_vars);
1146 g_free (formula_str);
1147 g_free (numeric_val);
1152 const Split *template_split, gnc_numeric *credit_num,
1153 GList **creation_errors)
1155 _get_sx_formula_value(instance->
parent->sx, template_split, credit_num,
1156 creation_errors,
"sx-credit-formula",
1161_get_debit_formula_value(
GncSxInstance *instance,
const Split *template_split,
1162 gnc_numeric *debit_num, GList **creation_errors)
1164 _get_sx_formula_value(instance->
parent->sx, template_split, debit_num,
1165 creation_errors,
"sx-debit-formula",
1172 gnc_numeric credit_num = gnc_numeric_zero();
1173 gnc_numeric debit_num = gnc_numeric_zero();
1176 SchedXaction *sx = creation_data->instance->
parent->sx;
1178 _get_credit_formula_value(creation_data->instance, split, &credit_num,
1179 creation_data->creation_errors);
1180 _get_debit_formula_value(creation_data->instance, split, &debit_num,
1181 creation_data->creation_errors);
1183 final = gnc_numeric_sub_fixed(debit_num, credit_num);
1188 gchar *err = N_(
"Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1189 REPORT_ERROR(creation_data->creation_errors, err,
1190 gncn_error, xaccSchedXactionGetName(sx));
1191 final = gnc_numeric_zero();
1197split_apply_exchange_rate (Split *split, GHashTable *bindings,
1198 gnc_commodity *txn_cmdty,
1199 gnc_commodity *split_cmdty, gnc_numeric *
final)
1201 gchar *exchange_rate_var_name;
1204 gnc_numeric exchange_rate = gnc_numeric_create (1, 1);
1206 exchange_rate_var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
1209 exchange_rate_var_name);
1211 if (exchange_rate_var != NULL)
1213 exchange_rate = exchange_rate_var->
value;
1216 g_free (exchange_rate_var_name);
1221 GNC_HOW_RND_ROUND_HALF_UP);
1224 GNC_HOW_RND_ROUND_HALF_UP);
1245static gnc_commodity*
1247 SchedXaction *sx, Transaction *template_txn)
1249 gnc_commodity *first_currency = NULL, *first_cmdty = NULL,
1250 *fallback_cmdty = NULL;
1251 gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
1254 GList** creation_errors =
1255 creation_data ? creation_data->creation_errors : NULL;
1258 DEBUG(
"Template txn currency is %s.",
1261 DEBUG(
"No template txn currency.");
1263 for (;txn_splits; txn_splits = txn_splits->next)
1265 Split* t_split = (Split*)txn_splits->data;
1266 Account* split_account = NULL;
1267 gnc_commodity *split_cmdty = NULL;
1269 if (!_get_template_split_account(sx, t_split, &split_account,
1278 if (!fallback_cmdty)
1281 if (split_is_marker(t_split))
1286 txn_cmdty = split_cmdty;
1288 first_cmdty = split_cmdty;
1290 txn_cmdty_in_splits = TRUE;
1292 first_currency = split_cmdty;
1296 g_critical(
"Error in SX transaction [%s], split missing account: "
1297 "Creation aborted.", xaccSchedXactionGetName(sx));
1300 if (first_currency &&
1302 return first_currency;
1303 if (!txn_cmdty_in_splits && first_cmdty)
1307 return fallback_cmdty;
1311create_each_transaction_helper(Transaction *template_txn,
void *user_data)
1313 Transaction *new_txn;
1314 GList *txn_splits, *template_splits;
1315 Split *copying_split;
1317 SchedXaction *sx = creation_data->instance->
parent->sx;
1318 gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
1322 if (txn_cmdty == NULL)
1332 DEBUG(
"creating template txn desc [%s] for sx [%s]",
1334 xaccSchedXactionGetName(sx));
1342 g_date_get_day(&creation_data->instance->
date),
1343 g_date_get_month(&creation_data->instance->
date),
1344 g_date_get_year(&creation_data->instance->
date));
1349 if ((template_splits == NULL) || (txn_splits == NULL))
1351 g_critical(
"transaction w/o splits for sx [%s]",
1352 xaccSchedXactionGetName(sx));
1358 if (txn_cmdty == NULL)
1367 txn_splits && template_splits;
1368 txn_splits = txn_splits->next, template_splits = template_splits->next)
1370 const Split *template_split;
1372 gnc_commodity *split_cmdty = NULL;
1377 template_split = (Split*)template_splits->data;
1378 copying_split = (Split*)txn_splits->data;
1380 _get_template_split_account(sx, template_split, &split_acct,
1381 creation_data->creation_errors);
1384 xaccSplitSetAccount(copying_split, split_acct);
1387 gnc_numeric
final = split_apply_formulas(template_split,
1390 DEBUG(
"value is %s for memo split '%s'",
1395 split_apply_exchange_rate(copying_split,
1397 txn_cmdty, split_cmdty, &
final);
1407 "from-sched-xaction",
1414 if (creation_data->created_txn_guids != NULL)
1416 *creation_data->created_txn_guids
1417 = g_list_append(*(creation_data->created_txn_guids),
1425create_transactions_for_instance(
GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
1430 sx_template_account = gnc_sx_get_template_transaction_account(instance->
parent->sx);
1432 creation_data.instance = instance;
1433 creation_data.created_txn_guids = created_txn_guids;
1434 creation_data.creation_errors = creation_errors;
1440 create_each_transaction_helper,
1446gnc_sx_instance_model_effect_change(GncSxInstanceModel *model,
1447 gboolean auto_create_only,
1448 GList **created_transaction_guids,
1449 GList **creation_errors)
1459 for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
1461 GList *instance_iter;
1463 GDate *last_occur_date;
1464 gint instance_count = 0;
1465 gint remain_occur_count = 0;
1473 last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
1475 remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
1477 for (instance_iter = instances->
instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
1480 gboolean sx_is_auto_create;
1481 GList *instance_errors = NULL;
1483 xaccSchedXactionGetAutoCreate(inst->
parent->sx, &sx_is_auto_create, NULL);
1484 if (auto_create_only && !sx_is_auto_create)
1486 if (inst->
state != SX_INSTANCE_STATE_TO_CREATE)
1493 if (inst->
orig_state == SX_INSTANCE_STATE_POSTPONED
1494 && inst->
state != SX_INSTANCE_STATE_POSTPONED)
1502 switch (inst->
state)
1504 case SX_INSTANCE_STATE_CREATED:
1507 case SX_INSTANCE_STATE_IGNORED:
1508 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1510 case SX_INSTANCE_STATE_POSTPONED:
1511 if (inst->
orig_state != SX_INSTANCE_STATE_POSTPONED)
1516 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1518 case SX_INSTANCE_STATE_TO_CREATE:
1519 create_transactions_for_instance (inst,
1520 created_transaction_guids,
1522 if (instance_errors == NULL)
1524 increment_sx_state (inst, &last_occur_date,
1526 &remain_occur_count);
1527 gnc_sx_instance_model_change_instance_state
1528 (model, inst, SX_INSTANCE_STATE_CREATED);
1530 else if (creation_errors)
1532 *creation_errors = g_list_concat (*creation_errors,
1534 instance_errors = NULL;
1537 case SX_INSTANCE_STATE_REMINDER:
1542 g_assert_not_reached();
1546 if (instance_errors)
1547 g_list_free_full (instance_errors, g_free);
1550 xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
1552 xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
1557gnc_sx_instance_model_change_instance_state(GncSxInstanceModel *model,
1559 GncSxInstanceState new_state)
1561 if (instance->
state == new_state)
1564 instance->
state = new_state;
1570 g_assert(inst_iter != NULL);
1571 if (instance->
state != SX_INSTANCE_STATE_REMINDER)
1574 for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
1577 if (prev_inst->
state != SX_INSTANCE_STATE_REMINDER)
1579 prev_inst->
state = SX_INSTANCE_STATE_POSTPONED;
1585 for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
1588 if (next_inst->
state == SX_INSTANCE_STATE_REMINDER)
1590 next_inst->
state = SX_INSTANCE_STATE_REMINDER;
1595 g_signal_emit_by_name(model,
"updated", (gpointer)instance->
parent->sx);
1599gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
1602 gnc_numeric *new_value)
1607 variable->
value = *new_value;
1608 g_signal_emit_by_name(model,
"updated", (gpointer)instance->
parent->sx);
1612_list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
1614 *result_list = g_list_prepend (*result_list, value);
1618gnc_sx_instance_model_check_variables(GncSxInstanceModel *model)
1621 GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
1623 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1626 for (inst_iter = instances->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1630 if (inst->
state != SX_INSTANCE_STATE_TO_CREATE)
1633 g_hash_table_foreach(inst->
variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
1634 for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
1640 need->instance = inst;
1641 need->variable = var;
1642 rtn = g_list_prepend (rtn, need);
1645 g_list_free(var_list);
1653gnc_sx_instance_model_summarize(GncSxInstanceModel *model,
GncSxSummary *summary)
1655 GList *sx_iter, *inst_iter;
1657 g_return_if_fail(model != NULL);
1658 g_return_if_fail(summary != NULL);
1666 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1669 gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
1670 xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
1671 for (inst_iter = instances->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1676 if (inst->
state == SX_INSTANCE_STATE_TO_CREATE)
1678 if (sx_is_auto_create)
1714static void gnc_numeric_free(gpointer data)
1716 gnc_numeric *p = (gnc_numeric*) data;
1720GHashTable* gnc_g_hash_new_guid_numeric()
1723 NULL, gnc_numeric_free);
1729 GList **creation_errors;
1730 const SchedXaction *sx;
1734static void add_to_hash_amount(GHashTable* hash,
const GncGUID* guid,
const gnc_numeric* amount)
1739 gnc_numeric* elem = g_hash_table_lookup(hash, guid);
1744 elem = g_new0(gnc_numeric, 1);
1745 *elem = gnc_numeric_zero();
1746 g_hash_table_insert(hash, (gpointer) guid, elem);
1752 g_critical(
"Oops, the given amount [%s] has the error code %d, at guid [%s].",
1760 g_critical(
"Oops, the account's amount [%s] has the error code %d, at guid [%s].",
1773 GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_NEVER);
1778 g_critical(
"Oops, after addition at guid [%s] the resulting amount [%s] has the error code %d; added amount = [%s].",
1787 DEBUG(
"Adding to guid [%s] the value [%s]. Value now [%s].",
1794create_cashflow_helper(Transaction *template_txn,
void *user_data)
1797 GList *template_splits;
1798 const gnc_commodity *first_cmdty = NULL;
1800 DEBUG(
"Evaluating txn desc [%s] for sx [%s]",
1802 xaccSchedXactionGetName(creation_data->sx));
1806 if (template_splits == NULL)
1808 g_critical(
"transaction w/o splits for sx [%s]",
1809 xaccSchedXactionGetName(creation_data->sx));
1815 template_splits = template_splits->next)
1818 const gnc_commodity *split_cmdty = NULL;
1819 const Split *template_split = (
const Split*) template_splits->data;
1822 if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors))
1824 DEBUG(
"Could not find account for split");
1830 if (first_cmdty == NULL)
1832 first_cmdty = split_cmdty;
1837 gnc_numeric credit_num = gnc_numeric_zero();
1838 gnc_numeric debit_num = gnc_numeric_zero();
1839 gnc_numeric final_once,
final;
1843 _get_sx_formula_value(creation_data->sx, template_split,
1844 &credit_num, creation_data->creation_errors,
1845 "sx-credit-formula",
"sx-credit-numeric",
1848 _get_sx_formula_value(creation_data->sx, template_split,
1849 &debit_num, creation_data->creation_errors,
1850 "sx-debit-formula",
"sx-debit-numeric", NULL);
1854 final_once = gnc_numeric_sub_fixed( debit_num, credit_num );
1857 gnc_numeric_denom(final_once),
1858 GNC_HOW_RND_ROUND_HALF_UP);
1863 gchar* err = N_(
"Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1864 REPORT_ERROR(creation_data->creation_errors, err,
1865 gncn_error, xaccSchedXactionGetName(creation_data->sx));
1866 final = gnc_numeric_zero();
1872 gchar *err = N_(
"No exchange rate available in SX [%s] for %s -> %s, value is zero.");
1873 REPORT_ERROR(creation_data->creation_errors, err,
1874 xaccSchedXactionGetName(creation_data->sx),
1877 final = gnc_numeric_zero();
1889instantiate_cashflow_internal(
const SchedXaction* sx,
1891 GList **creation_errors, gint count)
1894 Account* sx_template_account = gnc_sx_get_template_transaction_account(sx);
1896 if (!sx_template_account)
1898 g_critical(
"Huh? No template account for the SX %s", xaccSchedXactionGetName(sx));
1902 if (!xaccSchedXactionGetEnabled(sx))
1904 DEBUG(
"Skipping non-enabled SX [%s]",
1905 xaccSchedXactionGetName(sx));
1909 create_cashflow_data.hash = map;
1910 create_cashflow_data.creation_errors = creation_errors;
1911 create_cashflow_data.sx = sx;
1912 create_cashflow_data.count = gnc_numeric_create(count, 1);
1917 create_cashflow_helper,
1918 &create_cashflow_data);
1924 GList **creation_errors;
1925 const GDate *range_start;
1926 const GDate *range_end;
1929static void instantiate_cashflow_cb(gpointer data, gpointer _user_data)
1931 const SchedXaction* sx = (
const SchedXaction*) data;
1940 userdata->range_end);
1946 instantiate_cashflow_internal(sx,
1948 userdata->creation_errors,
1953void gnc_sx_all_instantiate_cashflow(GList *all_sxes,
1954 const GDate *range_start,
const GDate *range_end,
1955 GHashTable* map, GList **creation_errors)
1958 userdata.hash = map;
1959 userdata.creation_errors = creation_errors;
1960 userdata.range_start = range_start;
1961 userdata.range_end = range_end;
1964 g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
1968GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_end)
1970 GHashTable *result_map = gnc_g_hash_new_guid_numeric();
1971 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
1972 gnc_sx_all_instantiate_cashflow(all_sxes,
1973 &range_start, &range_end,
1978GList *gnc_sx_instance_model_get_sx_instances_list (GncSxInstanceModel *model)
1980 return model->sx_instance_list;
Account handling public routines.
Anchor Scheduled Transaction info in a book.
Scheduled Transactions public handling routines.
convert single-entry accounts to clean double-entry
API for Transactions and Splits (journal entries)
API for Transactions and Splits (journal entries)
Commodity handling public routines.
Date and Time handling routines.
Additional event handling code.
utility functions for the GnuCash UI
#define xaccAccountGetGUID(X)
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency.
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
time64 gnc_time(time64 *tbuf)
get the current time
void xaccTransSetDate(Transaction *trans, int day, int mon, int year)
The xaccTransSetDate() method does the same thing as xaccTransSetDate[Posted]Secs(),...
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
The gnc_account_lookup_by_name() subroutine fetches the account by name from the descendants of the s...
#define xaccTransGetGUID(X)
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
Transaction * xaccTransCloneNoKvp(const Transaction *from)
The xaccTransCloneNoKvp() method will create a complete copy of an existing transaction except that t...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
gint xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, void *data)
The xaccAccountForEachTransaction() routine will traverse all of the transactions in account and call...
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
void qof_event_resume(void)
Resume engine event generation.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
void qof_event_suspend(void)
Suspend all engine events.
gint QofEventId
Define the type of events allowed.
#define GNC_EVENT_ITEM_ADDED
These events are used when a split is added to an account.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
gint guid_g_hash_table_equal(gconstpointer guid_a, gconstpointer guid_b)
Equality function for two GUIDs in a GHashTable.
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...
void gnc_g_list_cut(GList **list, GList *cut_point)
Cut a GList into two parts; the cut_point is the beginning of the new list; list may need to be modif...
GList * gnc_g_list_map(GList *list, GncGMapFunc fn, gpointer user_data)
void qof_instance_set(QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
#define PINFO(format, args...)
Print an informational note.
#define DEBUG(format, args...)
Print a debugging message.
gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and ...
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_ERROR_ARG
Argument is not a valid number.
#define xaccSchedXactionGetGUID(X)
SXTmpStateData * gnc_sx_clone_temporal_state(SXTmpStateData *tsd)
Allocates and returns a one-by-one copy of the given temporal state.
GList * gnc_sx_get_defer_instances(SchedXaction *sx)
Returns the defer list from the SX; this is a (date-)sorted temporal-state-data instance list.
gint gnc_sx_get_instance_count(const SchedXaction *sx, SXTmpStateData *stateData)
Get the instance count.
GDate xaccSchedXactionGetNextInstance(const SchedXaction *sx, SXTmpStateData *tsd)
Returns the next occurrence of a scheduled transaction.
gint gnc_sx_get_num_occur_daterange(const SchedXaction *sx, const GDate *start_date, const GDate *end_date)
Calculates and returns the number of occurrences of the given SX in the given date range (inclusive).
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
void gnc_sx_remove_defer_instance(SchedXaction *sx, void *deferStateData)
Removes an instance from the deferred list.
void gnc_sx_add_defer_instance(SchedXaction *sx, void *deferStateData)
Adds an instance to the deferred list of the SX.
SXTmpStateData * gnc_sx_create_temporal_state(const SchedXaction *sx)
Allocates a new SXTmpStateData object and fills it with the current state of the given sx.
void gnc_sx_destroy_temporal_state(SXTmpStateData *tsd)
Frees the given stateDate object.
void gnc_sx_incr_temporal_state(const SchedXaction *sx, SXTmpStateData *tsd)
Calculates the next occurrence of the given SX and stores that occurrence in the remporalStateDate.
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
void xaccSplitScrub(Split *split)
The xaccSplitScrub method ensures that if this split has the same commodity and currency,...
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
The type used to store guids in C.
GncSxInstanceState orig_state
the original state at generation time.
SXTmpStateData * temporal_state
the sx creation temporal state.
GHashTable * variable_bindings
variable bindings.
GDate date
the instance date.
GncSxInstanceState state
the current state of the instance (during editing)
GncSxInstances * parent
the parent instances collection.
GList * instance_list
GList<GncSxInstance*>
GHashTable * variable_names
<name:char*,GncSxVariable*>
gint num_auto_create_no_notify_instances
The number of automatically-created instances that do no request notification.
gint num_to_create_instances
The number of (not-auto-create) to-create instances.
gboolean need_dialog
If the dialog needs to be displayed.
gint num_instances
The number of total instances (in any state).
gint num_auto_create_instances
The total number of auto-create instances.
gnc_numeric value
only numeric values are supported.
Just the variable temporal bits from the SX structure.