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" 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__)); \ 68 static GObjectClass *parent_class = NULL;
70 typedef struct _SxTxnCreationData
73 GList **created_txn_guids;
74 GList **creation_errors;
78 static void gnc_sx_instance_model_init(GTypeInstance *instance, gpointer klass);
81 static GncSxInstance* gnc_sx_instance_new(
GncSxInstances *parent, GncSxInstanceState state, GDate *date,
void *temporal_state, gint sequence_num);
83 static gint _get_vars_helper(Transaction *txn,
void *var_hash_data);
87 static void _gnc_sx_instance_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data);
88 static gnc_commodity* get_transaction_currency(
SxTxnCreationData *creation_data, SchedXaction *sx, Transaction *template_txn);
98 scrub_sx_split_numeric (Split* split, gboolean is_credit, GList **changes)
100 const char *formula = is_credit ?
"sx-credit-formula" :
"sx-debit-formula";
101 const char *
numeric = is_credit ?
"sx-credit-numeric" :
"sx-debit-numeric";
103 gnc_numeric *numval = NULL;
104 GHashTable *parser_vars = g_hash_table_new_full
105 (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_free);
107 gnc_numeric amount = gnc_numeric_zero ();
108 gboolean parse_result = FALSE;
115 parse_result = gnc_exp_parser_parse_separate_vars (formval, &amount,
116 &error_loc, parser_vars);
118 if (!parse_result || g_hash_table_size (parser_vars) != 0)
119 amount = gnc_numeric_zero ();
125 change->amount = amount;
126 *changes = g_list_prepend (*changes, change);
129 g_hash_table_destroy (parser_vars);
140 Split *split = GNC_SPLIT (psplit);
142 GList *changes = NULL;
143 scrub_sx_split_numeric (split, TRUE, &changes);
144 scrub_sx_split_numeric (split, FALSE, &changes);
149 for (GList *n = changes; n; n = n->next)
153 change->name, &change->amount,
157 g_list_free_full (changes, g_free);
161 _sx_var_to_raw_numeric(gchar *name,
GncSxVariable *var, GHashTable *parser_var_hash)
163 g_hash_table_insert(parser_var_hash, g_strdup(name), &var->
value);
167 _var_numeric_to_sx_var(gchar *name, gnc_numeric *num, GHashTable *sx_var_hash)
170 if (!g_hash_table_lookup_extended(sx_var_hash, name, NULL, &p_var))
172 p_var = (gpointer)gnc_sx_variable_new(name);
173 g_hash_table_insert(sx_var_hash, g_strdup(name), p_var);
179 _wipe_parsed_sx_var(gchar *key,
GncSxVariable *var, gpointer unused_user_data)
185 split_is_marker(Split *split)
187 gchar *credit_formula = NULL;
188 gchar *debit_formula = NULL;
189 gboolean split_is_marker = TRUE;
192 "sx-credit-formula", &credit_formula,
193 "sx-debit-formula", &debit_formula,
196 if ((credit_formula && *credit_formula) ||
197 (debit_formula && *debit_formula))
198 split_is_marker = FALSE;
200 g_free(credit_formula);
201 g_free(debit_formula);
202 return split_is_marker;
211 GHashTable *parser_vars;
213 parser_vars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
214 g_hash_table_foreach(instance_var_hash, (GHFunc)_sx_var_to_raw_numeric, parser_vars);
219 gnc_sx_parse_vars_from_formula(
const char *formula,
220 GHashTable *var_hash,
226 GHashTable *parser_vars;
231 num = gnc_numeric_zero();
232 if (!gnc_exp_parser_parse_separate_vars(formula, &num, &errLoc, parser_vars))
238 g_hash_table_foreach(parser_vars, (GHFunc)_var_numeric_to_sx_var, var_hash);
239 g_hash_table_destroy(parser_vars);
250 gnc_sx_variable_new(gchar *name)
253 var->name = g_strdup(name);
255 var->editable = TRUE;
260 gnc_sx_variable_new_full(gchar *name, gnc_numeric value, gboolean editable)
264 var->editable = editable;
273 var->editable = to_copy->editable;
285 var_name_from_commodities(gnc_commodity* split_c, gnc_commodity* txn_c)
289 gchar* var_name = g_strdup_printf (
"%s -> %s",
290 split_m ? split_m :
"(null)",
291 txn_m ? txn_m :
"(null)");
293 DEBUG(
"var_name is %s", var_name);
298 _get_vars_helper(Transaction *txn,
void *var_hash_data)
300 GHashTable *var_hash = (GHashTable*)var_hash_data;
303 gchar *credit_formula = NULL;
304 gchar *debit_formula = NULL;
305 gnc_commodity *txn_cmdty = get_transaction_currency(NULL, NULL, txn);
308 if (split_list == NULL)
313 for ( ; split_list; split_list = split_list->next)
315 gnc_commodity *split_cmdty = NULL;
318 gboolean split_is_marker = TRUE;
320 s = (Split*)split_list->data;
323 "sx-account", &acct_guid,
324 "sx-credit-formula", &credit_formula,
325 "sx-debit-formula", &debit_formula,
328 guid_free (acct_guid);
331 if (credit_formula && strlen(credit_formula) != 0)
333 gnc_sx_parse_vars_from_formula(credit_formula, var_hash, NULL);
334 split_is_marker = FALSE;
336 if (debit_formula && strlen(debit_formula) != 0)
338 gnc_sx_parse_vars_from_formula(debit_formula, var_hash, NULL);
339 split_is_marker = FALSE;
341 g_free (credit_formula);
342 g_free (debit_formula);
352 var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
353 var = gnc_sx_variable_new(var_name);
354 g_hash_table_insert(var_hash, g_strdup(var->name), var);
362 gnc_sx_get_template_transaction_account(
const SchedXaction *sx)
364 Account *template_root, *sx_template_acct;
370 return sx_template_acct;
374 gnc_sx_get_variables(SchedXaction *sx, GHashTable *var_hash)
376 Account *sx_template_acct = gnc_sx_get_template_transaction_account(sx);
381 _set_var_to_random_value(gchar *key,
GncSxVariable *var, gpointer unused_user_data)
389 var->
value = gnc_numeric_create(g_random_int_range(1, 1000), 1);
393 gnc_sx_randomize_variables(GHashTable *vars)
395 g_hash_table_foreach(vars, (GHFunc)_set_var_to_random_value, NULL);
399 _clone_sx_var_hash_entry(gpointer key, gpointer value, gpointer user_data)
401 GHashTable *to = (GHashTable*)user_data;
404 g_hash_table_insert(to, g_strdup(key), var);
408 gnc_sx_instance_new(
GncSxInstances *parent, GncSxInstanceState state, GDate *date,
void *temporal_state, gint sequence_num)
414 g_date_clear(&rtn->
date, 1);
418 if (! parent->variable_names_parsed)
420 parent->variable_names = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free);
421 gnc_sx_get_variables(parent->sx, parent->variable_names);
422 g_hash_table_foreach(parent->variable_names, (GHFunc)_wipe_parsed_sx_var, NULL);
423 parent->variable_names_parsed = TRUE;
426 rtn->
variable_bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free);
427 g_hash_table_foreach(parent->variable_names, _clone_sx_var_hash_entry, rtn->
variable_bindings);
430 int instance_i_value;
435 i_num = gnc_numeric_create(instance_i_value, 1);
436 as_var = gnc_sx_variable_new_full(
"i", i_num, FALSE);
445 _compare_GncSxVariables(gconstpointer a, gconstpointer b)
451 _build_list_from_hash_elts(gpointer key, gpointer value, gpointer user_data)
453 GList **list = (GList**)user_data;
454 *list = g_list_prepend (*list, value);
461 g_hash_table_foreach(inst->
variable_bindings, _build_list_from_hash_elts, &vars);
462 return g_list_sort (vars, _compare_GncSxVariables);
466 _gnc_sx_gen_instances(gpointer *data, gpointer user_data)
469 GList *instlist = NULL;
470 SchedXaction *sx = (SchedXaction*)data;
471 const GDate *range_end = (
const GDate*)user_data;
472 GDate creation_end, remind_end;
478 creation_end = *range_end;
479 g_date_add_days(&creation_end, xaccSchedXactionGetAdvanceCreation(sx));
480 remind_end = creation_end;
481 g_date_add_days(&remind_end, xaccSchedXactionGetAdvanceReminder(sx));
486 for ( ; postponed != NULL; postponed = postponed->next)
492 g_date_clear(&inst_date, 1);
495 inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_POSTPONED,
496 &inst_date, postponed->data, seq_num);
497 instlist = g_list_prepend (instlist, inst);
505 g_date_clear(&cur_date, 1);
507 instances->next_instance_date = cur_date;
508 while (g_date_valid(&cur_date) && g_date_compare(&cur_date, &creation_end) <= 0)
513 inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_TO_CREATE,
514 &cur_date, temporal_state, seq_num);
515 instlist = g_list_prepend (instlist, inst);
521 while (g_date_valid(&cur_date) &&
522 g_date_compare(&cur_date, &remind_end) <= 0)
527 inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_REMINDER,
528 &cur_date, temporal_state, seq_num);
529 instlist = g_list_prepend (instlist, inst);
545 g_date_clear(&now, 1);
553 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
556 g_assert(range_end != NULL);
557 g_assert(g_date_valid(range_end));
559 instances = gnc_sx_instance_model_new();
560 instances->include_disabled = include_disabled;
561 instances->range_end = *range_end;
563 if (include_disabled)
565 instances->sx_instance_list =
gnc_g_list_map(all_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
569 GList *sx_iter = g_list_first(all_sxes);
570 GList *enabled_sxes = NULL;
572 for (; sx_iter != NULL; sx_iter = sx_iter->next)
574 SchedXaction *sx = (SchedXaction*)sx_iter->data;
575 if (xaccSchedXactionGetEnabled(sx))
577 enabled_sxes = g_list_prepend (enabled_sxes, sx);
580 enabled_sxes = g_list_reverse (enabled_sxes);
581 instances->sx_instance_list =
gnc_g_list_map(enabled_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
582 g_list_free(enabled_sxes);
588 gnc_sx_instance_model_new(
void)
590 return GNC_SX_INSTANCE_MODEL(g_object_new(GNC_TYPE_SX_INSTANCE_MODEL, NULL));
594 gnc_sx_instance_model_get_type(
void)
596 static GType type = 0;
599 static const GTypeInfo info =
604 (GClassInitFunc)gnc_sx_instance_model_class_init,
609 (GInstanceInitFunc)gnc_sx_instance_model_init
611 type = g_type_register_static (G_TYPE_OBJECT,
612 "GncSxInstanceModelType",
619 gnc_sx_instance_model_dispose(GObject *
object)
622 g_return_if_fail(
object != NULL);
623 model = GNC_SX_INSTANCE_MODEL(
object);
625 g_return_if_fail(!model->disposed);
626 model->disposed = TRUE;
630 G_OBJECT_CLASS(parent_class)->dispose(
object);
650 GList *instance_iter;
658 instances->sx = NULL;
660 for (instance_iter = instances->
instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
663 gnc_sx_instance_free(inst);
672 gnc_sx_instance_model_finalize (GObject *
object)
677 g_return_if_fail(
object != NULL);
679 model = GNC_SX_INSTANCE_MODEL(
object);
680 for (sx_list_iter = model->sx_instance_list; sx_list_iter != NULL; sx_list_iter = sx_list_iter->next)
683 gnc_sx_instances_free(instances);
685 g_list_free(model->sx_instance_list);
686 model->sx_instance_list = NULL;
688 G_OBJECT_CLASS(parent_class)->finalize(
object);
694 GObjectClass *object_class = G_OBJECT_CLASS(klass);
696 parent_class = g_type_class_peek_parent(klass);
698 object_class->dispose = gnc_sx_instance_model_dispose;
699 object_class->finalize = gnc_sx_instance_model_finalize;
701 klass->removing_signal_id =
702 g_signal_new(
"removing",
703 GNC_TYPE_SX_INSTANCE_MODEL,
708 g_cclosure_marshal_VOID__POINTER,
713 klass->updated_signal_id =
714 g_signal_new(
"updated",
715 GNC_TYPE_SX_INSTANCE_MODEL,
720 g_cclosure_marshal_VOID__POINTER,
725 klass->added_signal_id =
726 g_signal_new(
"added",
727 GNC_TYPE_SX_INSTANCE_MODEL,
732 g_cclosure_marshal_VOID__POINTER,
739 gnc_sx_instance_model_init(GTypeInstance *instance, gpointer klass)
743 g_date_clear(&inst->range_end, 1);
744 inst->sx_instance_list = NULL;
749 _gnc_sx_instance_find_by_sx(
GncSxInstances *in_list_instances, SchedXaction *sx_to_find)
751 if (in_list_instances->sx == sx_to_find)
757 _gnc_sx_instance_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data)
766 if (!(GNC_IS_SX(ent) || GNC_IS_SXES(ent)))
772 gboolean sx_is_in_model = FALSE;
776 sx_is_in_model = (g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx) != NULL);
777 if (event_type & QOF_EVENT_MODIFY)
781 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
783 g_signal_emit_by_name(instances,
"updated", (gpointer)sx);
788 g_signal_emit_by_name(instances,
"removing", (gpointer)sx);
794 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
795 if (g_list_find(all_sxes, sx) && (!instances->include_disabled && xaccSchedXactionGetEnabled(sx)))
798 instances->sx_instance_list
799 = g_list_append(instances->sx_instance_list,
800 _gnc_sx_gen_instances((gpointer)sx, (gpointer) & instances->range_end));
801 g_signal_emit_by_name(instances,
"added", (gpointer)sx);
807 else if (GNC_IS_SXES(ent))
809 SchedXaction *sx = GNC_SX(evt_data);
811 if (event_type & GNC_EVENT_ITEM_REMOVED)
813 GList *instances_link;
814 instances_link = g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
815 if (instances_link != NULL)
817 g_signal_emit_by_name(instances,
"removing", (gpointer)sx);
819 else if (instances->include_disabled)
821 g_warning(
"could not remove instances that do not exist in the model");
826 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
829 instances->sx_instance_list
830 = g_list_append(instances->sx_instance_list,
831 _gnc_sx_gen_instances((gpointer)sx, (gpointer) & instances->range_end));
832 g_signal_emit_by_name(instances,
"added", (gpointer)sx);
839 typedef struct _HashListPair
846 _find_unreferenced_vars(gchar *key,
850 if (cb_pair->hash == NULL ||
851 !g_hash_table_lookup_extended(cb_pair->hash, key, NULL, NULL))
853 DEBUG(
"variable [%s] not found", key);
854 cb_pair->list = g_list_prepend (cb_pair->list, key);
864 link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
867 g_critical(
"couldn't find sx [%p]\n", sx);
873 new_instances = _gnc_sx_gen_instances((gpointer)sx, &model->range_end);
874 existing->sx = new_instances->sx;
875 existing->next_instance_date = new_instances->next_instance_date;
877 GList *existing_iter, *new_iter;
878 gboolean existing_remain, new_remain;
885 for (; existing_iter != NULL && new_iter != NULL; existing_iter = existing_iter->next, new_iter = new_iter->next)
888 gboolean same_instance_date;
892 same_instance_date = g_date_compare(&existing_inst->
date, &new_inst->
date) == 0;
893 if (!same_instance_date)
897 existing_remain = (existing_iter != NULL);
898 new_remain = (new_iter != NULL);
904 g_list_foreach(existing_iter, (GFunc)gnc_sx_instance_free, NULL);
910 GList *new_iter_iter;
913 for (new_iter_iter = new_iter; new_iter_iter != NULL; new_iter_iter = new_iter_iter->next)
919 g_list_free(new_iter);
925 GList *removed_var_names = NULL, *added_var_names = NULL;
926 GList *inst_iter = NULL;
932 removed_cb_data.list = NULL;
933 g_hash_table_foreach(existing->
variable_names, (GHFunc)_find_unreferenced_vars, &removed_cb_data);
934 removed_var_names = g_list_reverse (removed_cb_data.list);
936 DEBUG(
"%d removed variables", g_list_length(removed_var_names));
942 added_cb_data.list = NULL;
943 g_hash_table_foreach(new_instances->
variable_names, (GHFunc)_find_unreferenced_vars, &added_cb_data);
944 added_var_names = g_list_reverse (added_cb_data.list);
946 DEBUG(
"%d added variables", g_list_length(added_var_names));
955 for (inst_iter = existing->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
960 for (var_iter = removed_var_names; var_iter != NULL; var_iter = var_iter->next)
962 gchar *to_remove_key = (gchar*)var_iter->data;
966 for (var_iter = added_var_names; var_iter != NULL; var_iter = var_iter->next)
968 gchar *to_add_key = (gchar*)var_iter->data;
969 if (!g_hash_table_lookup_extended(
976 g_assert(parent_var != NULL);
977 var_copy = gnc_sx_variable_new_copy(parent_var);
983 gnc_sx_instances_free(new_instances);
987 gnc_sx_instance_model_remove_sx_instances(
GncSxInstanceModel *model, SchedXaction *sx)
989 GList *instance_link = NULL;
991 instance_link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
992 if (instance_link == NULL)
994 g_warning(
"instance not found!\n");
998 model->sx_instance_list = g_list_remove_link(model->sx_instance_list, instance_link);
1003 increment_sx_state(
GncSxInstance *inst, GDate **last_occur_date,
int *instance_count,
int *remain_occur_count)
1005 if (!g_date_valid(*last_occur_date)
1006 || (g_date_valid(*last_occur_date)
1007 && g_date_compare(*last_occur_date, &inst->
date) <= 0))
1009 *last_occur_date = &inst->
date;
1014 if (*remain_occur_count > 0)
1016 *remain_occur_count -= 1;
1021 _get_template_split_account(
const SchedXaction* sx,
1022 const Split *template_split,
1024 GList **creation_errors)
1026 gboolean success = TRUE;
1029 "sx-account", &acct_guid,
1032 if (!*split_acct && sx && creation_errors)
1037 gchar* err = N_(
"Unknown account for guid [%s], cancelling SX [%s] creation.");
1039 REPORT_ERROR(creation_errors, err, guid_str, xaccSchedXactionGetName(sx));
1043 guid_free (acct_guid);
1048 _get_sx_formula_value(
const SchedXaction* sx,
1049 const Split *template_split,
1051 GList **creation_errors,
1052 const char *formula_key,
1053 const char* numeric_key,
1054 GHashTable *variable_bindings)
1057 char *formula_str = NULL, *parseErrorLoc = NULL;
1058 gnc_numeric *numeric_val = NULL;
1060 formula_key, &formula_str,
1061 numeric_key, &numeric_val,
1064 if ((variable_bindings == NULL ||
1065 g_hash_table_size (variable_bindings) == 0) &&
1066 numeric_val != NULL &&
1073 numeric->num = numeric_val->num;
1074 numeric->denom = numeric_val->denom;
1075 g_free (formula_str);
1076 g_free (numeric_val);
1080 if (formula_str != NULL && strlen(formula_str) != 0)
1082 GHashTable *parser_vars = NULL;
1083 if (variable_bindings)
1087 if (!gnc_exp_parser_parse_separate_vars(formula_str,
1092 gchar *err = N_(
"Error parsing SX [%s] key [%s]=formula [%s] at [%s]: %s.");
1093 REPORT_ERROR(creation_errors, err,
1094 xaccSchedXactionGetName(sx),
1098 gnc_exp_parser_error_string());
1101 if (parser_vars != NULL)
1103 g_hash_table_destroy(parser_vars);
1106 g_free (formula_str);
1107 g_free (numeric_val);
1112 const Split *template_split, gnc_numeric *credit_num,
1113 GList **creation_errors)
1115 _get_sx_formula_value(instance->
parent->sx, template_split, credit_num,
1116 creation_errors,
"sx-credit-formula",
1121 _get_debit_formula_value(
GncSxInstance *instance,
const Split *template_split,
1122 gnc_numeric *debit_num, GList **creation_errors)
1124 _get_sx_formula_value(instance->
parent->sx, template_split, debit_num,
1125 creation_errors,
"sx-debit-formula",
1132 gnc_numeric credit_num = gnc_numeric_zero();
1133 gnc_numeric debit_num = gnc_numeric_zero();
1136 SchedXaction *sx = creation_data->instance->
parent->sx;
1138 _get_credit_formula_value(creation_data->instance, split, &credit_num,
1139 creation_data->creation_errors);
1140 _get_debit_formula_value(creation_data->instance, split, &debit_num,
1141 creation_data->creation_errors);
1143 final = gnc_numeric_sub_fixed(debit_num, credit_num);
1148 gchar *err = N_(
"Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1149 REPORT_ERROR(creation_data->creation_errors, err,
1150 gncn_error, xaccSchedXactionGetName(sx));
1151 final = gnc_numeric_zero();
1157 split_apply_exchange_rate (Split *split, GHashTable *bindings,
1158 gnc_commodity *txn_cmdty,
1159 gnc_commodity *split_cmdty, gnc_numeric *
final)
1161 gchar *exchange_rate_var_name;
1164 gnc_numeric exchange_rate = gnc_numeric_create (1, 1);
1166 exchange_rate_var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
1169 exchange_rate_var_name);
1171 if (exchange_rate_var != NULL)
1173 exchange_rate = exchange_rate_var->
value;
1176 g_free (exchange_rate_var_name);
1205 static gnc_commodity*
1207 SchedXaction *sx, Transaction *template_txn)
1209 gnc_commodity *first_currency = NULL, *first_cmdty = NULL,
1210 *fallback_cmdty = NULL;
1211 gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
1214 GList** creation_errors =
1215 creation_data ? creation_data->creation_errors : NULL;
1218 DEBUG(
"Template txn currency is %s.",
1221 DEBUG(
"No template txn currency.");
1223 for (;txn_splits; txn_splits = txn_splits->next)
1225 Split* t_split = (Split*)txn_splits->data;
1226 Account* split_account = NULL;
1227 gnc_commodity *split_cmdty = NULL;
1229 if (!_get_template_split_account(sx, t_split, &split_account,
1238 if (!fallback_cmdty)
1241 if (split_is_marker(t_split))
1246 txn_cmdty = split_cmdty;
1248 first_cmdty = split_cmdty;
1250 txn_cmdty_in_splits = TRUE;
1252 first_currency = split_cmdty;
1256 g_critical(
"Error in SX transaction [%s], split missing account: " 1257 "Creation aborted.", xaccSchedXactionGetName(sx));
1260 if (first_currency &&
1262 return first_currency;
1263 if (!txn_cmdty_in_splits && first_cmdty)
1267 return fallback_cmdty;
1271 create_each_transaction_helper(Transaction *template_txn,
void *user_data)
1273 Transaction *new_txn;
1274 GList *txn_splits, *template_splits, *node;
1275 Split *copying_split;
1277 SchedXaction *sx = creation_data->instance->
parent->sx;
1278 gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
1282 if (txn_cmdty == NULL)
1292 DEBUG(
"creating template txn desc [%s] for sx [%s]",
1294 xaccSchedXactionGetName(sx));
1302 g_date_get_day(&creation_data->instance->
date),
1303 g_date_get_month(&creation_data->instance->
date),
1304 g_date_get_year(&creation_data->instance->
date));
1309 if ((template_splits == NULL) || (txn_splits == NULL))
1311 g_critical(
"transaction w/o splits for sx [%s]",
1312 xaccSchedXactionGetName(sx));
1318 if (txn_cmdty == NULL)
1327 txn_splits && template_splits;
1328 txn_splits = txn_splits->next, template_splits = template_splits->next)
1330 const Split *template_split;
1332 gnc_commodity *split_cmdty = NULL;
1337 template_split = (Split*)template_splits->data;
1338 copying_split = (Split*)txn_splits->data;
1340 _get_template_split_account(sx, template_split, &split_acct,
1341 creation_data->creation_errors);
1344 xaccSplitSetAccount(copying_split, split_acct);
1347 gnc_numeric
final = split_apply_formulas(template_split,
1350 DEBUG(
"value is %s for memo split '%s'",
1355 split_apply_exchange_rate(copying_split,
1357 txn_cmdty, split_cmdty, &
final);
1367 "from-sched-xaction",
1374 if (creation_data->created_txn_guids != NULL)
1376 *creation_data->created_txn_guids
1377 = g_list_append(*(creation_data->created_txn_guids),
1385 create_transactions_for_instance(
GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
1390 sx_template_account = gnc_sx_get_template_transaction_account(instance->
parent->sx);
1392 creation_data.instance = instance;
1393 creation_data.created_txn_guids = created_txn_guids;
1394 creation_data.creation_errors = creation_errors;
1400 create_each_transaction_helper,
1407 gboolean auto_create_only,
1408 GList **created_transaction_guids,
1409 GList **creation_errors)
1419 for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
1421 GList *instance_iter;
1423 GDate *last_occur_date;
1424 gint instance_count = 0;
1425 gint remain_occur_count = 0;
1433 last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
1435 remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
1437 for (instance_iter = instances->
instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
1440 gboolean sx_is_auto_create;
1441 GList *instance_errors = NULL;
1443 xaccSchedXactionGetAutoCreate(inst->
parent->sx, &sx_is_auto_create, NULL);
1444 if (auto_create_only && !sx_is_auto_create)
1446 if (inst->
state != SX_INSTANCE_STATE_TO_CREATE)
1453 if (inst->
orig_state == SX_INSTANCE_STATE_POSTPONED
1454 && inst->
state != SX_INSTANCE_STATE_POSTPONED)
1462 switch (inst->
state)
1464 case SX_INSTANCE_STATE_CREATED:
1467 case SX_INSTANCE_STATE_IGNORED:
1468 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1470 case SX_INSTANCE_STATE_POSTPONED:
1471 if (inst->
orig_state != SX_INSTANCE_STATE_POSTPONED)
1476 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1478 case SX_INSTANCE_STATE_TO_CREATE:
1479 create_transactions_for_instance (inst,
1480 created_transaction_guids,
1482 if (instance_errors == NULL)
1484 increment_sx_state (inst, &last_occur_date,
1486 &remain_occur_count);
1488 (model, inst, SX_INSTANCE_STATE_CREATED);
1491 *creation_errors = g_list_concat (*creation_errors,
1494 case SX_INSTANCE_STATE_REMINDER:
1499 g_assert_not_reached();
1504 xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
1506 xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
1513 GncSxInstanceState new_state)
1515 if (instance->
state == new_state)
1518 instance->
state = new_state;
1524 g_assert(inst_iter != NULL);
1525 if (instance->
state != SX_INSTANCE_STATE_REMINDER)
1528 for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
1531 if (prev_inst->
state != SX_INSTANCE_STATE_REMINDER)
1533 prev_inst->
state = SX_INSTANCE_STATE_POSTPONED;
1539 for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
1542 if (next_inst->
state == SX_INSTANCE_STATE_REMINDER)
1544 next_inst->
state = SX_INSTANCE_STATE_REMINDER;
1549 g_signal_emit_by_name(model,
"updated", (gpointer)instance->
parent->sx);
1556 gnc_numeric *new_value)
1561 variable->
value = *new_value;
1562 g_signal_emit_by_name(model,
"updated", (gpointer)instance->
parent->sx);
1566 _list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
1568 *result_list = g_list_prepend (*result_list, value);
1575 GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
1577 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1580 for (inst_iter = instances->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1584 if (inst->
state != SX_INSTANCE_STATE_TO_CREATE)
1587 g_hash_table_foreach(inst->
variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
1588 for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
1594 need->instance = inst;
1595 need->variable = var;
1596 rtn = g_list_prepend (rtn, need);
1599 g_list_free(var_list);
1609 GList *sx_iter, *inst_iter;
1611 g_return_if_fail(model != NULL);
1612 g_return_if_fail(summary != NULL);
1620 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1623 gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
1624 xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
1625 for (inst_iter = instances->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1630 if (inst->
state == SX_INSTANCE_STATE_TO_CREATE)
1632 if (sx_is_auto_create)
1668 static void gnc_numeric_free(gpointer data)
1670 gnc_numeric *p = (gnc_numeric*) data;
1677 NULL, gnc_numeric_free);
1683 GList **creation_errors;
1684 const SchedXaction *sx;
1688 static void add_to_hash_amount(GHashTable* hash,
const GncGUID* guid,
const gnc_numeric* amount)
1693 gnc_numeric* elem = g_hash_table_lookup(hash, guid);
1698 elem = g_new0(gnc_numeric, 1);
1699 *elem = gnc_numeric_zero();
1700 g_hash_table_insert(hash, (gpointer) guid, elem);
1706 g_critical(
"Oops, the given amount [%s] has the error code %d, at guid [%s].",
1714 g_critical(
"Oops, the account's amount [%s] has the error code %d, at guid [%s].",
1732 g_critical(
"Oops, after addition at guid [%s] the resulting amount [%s] has the error code %d; added amount = [%s].",
1741 DEBUG(
"Adding to guid [%s] the value [%s]. Value now [%s].",
1748 create_cashflow_helper(Transaction *template_txn,
void *user_data)
1751 GList *template_splits;
1752 const gnc_commodity *first_cmdty = NULL;
1754 DEBUG(
"Evaluating txn desc [%s] for sx [%s]",
1756 xaccSchedXactionGetName(creation_data->sx));
1760 if (template_splits == NULL)
1762 g_critical(
"transaction w/o splits for sx [%s]",
1763 xaccSchedXactionGetName(creation_data->sx));
1769 template_splits = template_splits->next)
1772 const gnc_commodity *split_cmdty = NULL;
1773 const Split *template_split = (
const Split*) template_splits->data;
1776 if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors))
1778 DEBUG(
"Could not find account for split");
1784 if (first_cmdty == NULL)
1786 first_cmdty = split_cmdty;
1791 gnc_numeric credit_num = gnc_numeric_zero();
1792 gnc_numeric debit_num = gnc_numeric_zero();
1793 gnc_numeric final_once,
final;
1797 _get_sx_formula_value(creation_data->sx, template_split,
1798 &credit_num, creation_data->creation_errors,
1799 "sx-credit-formula",
"sx-credit-numeric",
1802 _get_sx_formula_value(creation_data->sx, template_split,
1803 &debit_num, creation_data->creation_errors,
1804 "sx-debit-formula",
"sx-debit-numeric", NULL);
1808 final_once = gnc_numeric_sub_fixed( debit_num, credit_num );
1811 gnc_numeric_denom(final_once),
1817 gchar* err = N_(
"Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1818 REPORT_ERROR(creation_data->creation_errors, err,
1819 gncn_error, xaccSchedXactionGetName(creation_data->sx));
1820 final = gnc_numeric_zero();
1826 gchar *err = N_(
"No exchange rate available in SX [%s] for %s -> %s, value is zero.");
1827 REPORT_ERROR(creation_data->creation_errors, err,
1828 xaccSchedXactionGetName(creation_data->sx),
1831 final = gnc_numeric_zero();
1843 instantiate_cashflow_internal(
const SchedXaction* sx,
1845 GList **creation_errors, gint count)
1848 Account* sx_template_account = gnc_sx_get_template_transaction_account(sx);
1850 if (!sx_template_account)
1852 g_critical(
"Huh? No template account for the SX %s", xaccSchedXactionGetName(sx));
1856 if (!xaccSchedXactionGetEnabled(sx))
1858 DEBUG(
"Skipping non-enabled SX [%s]",
1859 xaccSchedXactionGetName(sx));
1863 create_cashflow_data.hash = map;
1864 create_cashflow_data.creation_errors = creation_errors;
1865 create_cashflow_data.sx = sx;
1866 create_cashflow_data.count = gnc_numeric_create(count, 1);
1871 create_cashflow_helper,
1872 &create_cashflow_data);
1878 GList **creation_errors;
1879 const GDate *range_start;
1880 const GDate *range_end;
1883 static void instantiate_cashflow_cb(gpointer data, gpointer _user_data)
1885 const SchedXaction* sx = (
const SchedXaction*) data;
1894 userdata->range_end);
1900 instantiate_cashflow_internal(sx,
1902 userdata->creation_errors,
1908 const GDate *range_start,
const GDate *range_end,
1909 GHashTable* map, GList **creation_errors)
1912 userdata.hash = map;
1913 userdata.creation_errors = creation_errors;
1914 userdata.range_start = range_start;
1915 userdata.range_end = range_end;
1918 g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
1925 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
1927 &range_start, &range_end,
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
GHashTable * variable_bindings
variable bindings.
gint xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, void *data)
The xaccAccountForEachTransaction() routine will traverse all of the transactions in account and call...
GList * gnc_g_list_map(GList *list, GncGMapFunc fn, gpointer user_data)
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
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 va...
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
SXTmpStateData * temporal_state
the sx creation temporal state.
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...
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
GHashTable * variable_names
<name:char*,GncSxVariable*>
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
Date and Time handling routines.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
void gnc_sx_scrub_split_numerics(gpointer psplit, gpointer user)
Fix up numerics where they've gotten out-of-sync with the formulas.
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.
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 curre...
void gnc_sx_destroy_temporal_state(SXTmpStateData *tsd)
Frees the given stateDate object.
utility functions for the GnuCash UI
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Print an informational note.
gint num_auto_create_no_notify_instances
The number of automatically-created instances that do no request notification.
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
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...
#define DEBUG(format, args...)
Print a debugging message.
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.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
GncSxInstanceState orig_state
the original state at generation time.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
GHashTable * gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash)
void gnc_sx_instance_model_summarize(GncSxInstanceModel *model, GncSxSummary *summary)
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
void gnc_sx_instance_model_update_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
Regenerates and updates the GncSxInstances* for the given SX.
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
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...
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
API for Transactions and Splits (journal entries)
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...
Just the variable temporal bits from the SX structure.
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
gint num_to_create_instances
The number of (not-auto-create) to-create instances.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
gint num_instances
The number of total instances (in any state).
GDate xaccSchedXactionGetNextInstance(const SchedXaction *sx, SXTmpStateData *tsd)
Returns the next occurrence of a scheduled transaction.
void gnc_sx_summary_print(const GncSxSummary *summary)
Debug output to trace file.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
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...
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
#define xaccAccountGetGUID(X)
convert single-entry accounts to clean double-entry
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
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.
Account handling public routines.
GncSxInstances * parent
the parent instances collection.
gint QofEventId
Define the type of events allowed.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
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...
void xaccSplitScrub(Split *split)
The xaccSplitScrub method ensures that if this split has the same commodity and currency, then it will have the same amount and value.
void gnc_sx_add_defer_instance(SchedXaction *sx, void *deferStateData)
Adds an instance to the deferred list of the SX.
Anchor Scheduled Transaction info in a book.
Transaction * xaccTransCloneNoKvp(const Transaction *from)
The xaccTransCloneNoKvp() method will create a complete copy of an existing transaction except that ...
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
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)...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Argument is not a valid number.
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...
SXTmpStateData * gnc_sx_clone_temporal_state(SXTmpStateData *tsd)
Allocates and returns a one-by-one copy of the given temporal state.
void xaccTransSetDate(Transaction *trans, int day, int mon, int year)
The xaccTransSetDate() method does the same thing as xaccTransSetDate[Posted]Secs(), but takes a convenient day-month-year format.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
Additional event handling code.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gint num_auto_create_instances
The total number of auto-create instances.
GDate date
the instance date.
#define xaccSchedXactionGetGUID(X)
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
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 ...
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<G...
GList * gnc_sx_instance_get_variables(GncSxInstance *inst)
#define xaccTransGetGUID(X)
Never round at all, and signal an error if there is a fractional result in a computation.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
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 ...
void qof_event_suspend(void)
Suspend all engine events.
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
gnc_numeric value
only numeric values are supported.
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
void qof_event_resume(void)
Resume engine event generation.
GncSxInstanceState state
the current state of the instance (during editing)
GncSxInstanceModel * gnc_sx_get_current_instances(void)
Shorthand for get_instances(now, FALSE);.
void gnc_sx_remove_defer_instance(SchedXaction *sx, void *deferStateData)
Removes an instance from the deferred list.
time64 gnc_time(time64 *tbuf)
get the current local time
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
GList * instance_list
GList<GncSxInstance*>
Scheduled Transactions public handling routines.
#define GNC_EVENT_ITEM_ADDED
These events are used when a split is added to an account.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
API for Transactions and Splits (journal entries)
gboolean need_dialog
If the dialog needs to be displayed.
The type used to store guids in C.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
Commodity handling public routines.
gint guid_g_hash_table_equal(gconstpointer guid_a, gconstpointer guid_b)
Equality function for two GUIDs in a GHashTable.
GList * gnc_sx_instance_model_check_variables(GncSxInstanceModel *model)
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
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.