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 typedef 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;
87 static GncSxInstanceModel* gnc_sx_instance_model_new(
void);
89 static GncSxInstance* gnc_sx_instance_new(
GncSxInstances *parent, GncSxInstanceState state, GDate *date,
void *temporal_state, gint sequence_num);
91 static gint _get_vars_helper(Transaction *txn,
void *var_hash_data);
95 static void _gnc_sx_instance_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data);
96 static gnc_commodity* get_transaction_currency(
SxTxnCreationData *creation_data, SchedXaction *sx, Transaction *template_txn);
107 REMOVING, UPDATED, ADDED,
111 static guint signals[LAST_SIGNAL] = { 0 };
114 scrub_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);
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)
201 split_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;
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);
235 gnc_sx_parse_vars_from_formula(
const char *formula,
236 GHashTable *var_hash,
242 GHashTable *parser_vars;
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);
266 gnc_sx_variable_new(gchar *name)
269 var->name = g_strdup(name);
271 var->editable = TRUE;
276 gnc_sx_variable_new_full(gchar *name, gnc_numeric value, gboolean editable)
280 var->editable = editable;
289 var->editable = to_copy->editable;
301 var_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);
379 gnc_sx_get_template_transaction_account(
const SchedXaction *sx)
381 Account *template_root, *sx_template_acct;
387 return sx_template_acct;
391 gnc_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);
410 gnc_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);
425 gnc_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);
562 g_date_clear(&now, 1);
570 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
571 GncSxInstanceModel *instances;
573 g_assert(range_end != NULL);
574 g_assert(g_date_valid(range_end));
576 instances = gnc_sx_instance_model_new();
577 instances->include_disabled = include_disabled;
578 instances->range_end = *range_end;
580 if (include_disabled)
582 instances->sx_instance_list =
gnc_g_list_map(all_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
586 GList *sx_iter = g_list_first(all_sxes);
587 GList *enabled_sxes = NULL;
589 for (; sx_iter != NULL; sx_iter = sx_iter->next)
591 SchedXaction *sx = (SchedXaction*)sx_iter->data;
592 if (xaccSchedXactionGetEnabled(sx))
594 enabled_sxes = g_list_prepend (enabled_sxes, sx);
597 enabled_sxes = g_list_reverse (enabled_sxes);
598 instances->sx_instance_list =
gnc_g_list_map(enabled_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
599 g_list_free(enabled_sxes);
605 G_DEFINE_TYPE (GncSxInstanceModel, gnc_sx_instance_model, G_TYPE_OBJECT)
607 static GncSxInstanceModel*
608 gnc_sx_instance_model_new(
void)
610 return GNC_SX_INSTANCE_MODEL(g_object_new(GNC_TYPE_SX_INSTANCE_MODEL, NULL));
614 gnc_sx_instance_model_dispose(GObject *
object)
616 GncSxInstanceModel *model;
617 g_return_if_fail(
object != NULL);
618 model = GNC_SX_INSTANCE_MODEL(
object);
620 g_return_if_fail(!model->disposed);
621 model->disposed = TRUE;
625 G_OBJECT_CLASS(gnc_sx_instance_model_parent_class)->dispose(
object);
645 GList *instance_iter;
653 instances->sx = NULL;
655 for (instance_iter = instances->
instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
658 gnc_sx_instance_free(inst);
667 gnc_sx_instance_model_finalize (GObject *
object)
669 GncSxInstanceModel *model;
672 g_return_if_fail(
object != NULL);
674 model = GNC_SX_INSTANCE_MODEL(
object);
675 for (sx_list_iter = model->sx_instance_list; sx_list_iter != NULL; sx_list_iter = sx_list_iter->next)
678 gnc_sx_instances_free(instances);
680 g_list_free(model->sx_instance_list);
681 model->sx_instance_list = NULL;
683 G_OBJECT_CLASS(gnc_sx_instance_model_parent_class)->finalize(
object);
687 gnc_sx_instance_model_class_init (GncSxInstanceModelClass *klass)
689 GObjectClass *object_class = G_OBJECT_CLASS(klass);
691 object_class->dispose = gnc_sx_instance_model_dispose;
692 object_class->finalize = gnc_sx_instance_model_finalize;
695 g_signal_new(
"removing",
696 GNC_TYPE_SX_INSTANCE_MODEL,
701 g_cclosure_marshal_VOID__POINTER,
707 g_signal_new(
"updated",
708 GNC_TYPE_SX_INSTANCE_MODEL,
713 g_cclosure_marshal_VOID__POINTER,
719 g_signal_new(
"added",
720 GNC_TYPE_SX_INSTANCE_MODEL,
725 g_cclosure_marshal_VOID__POINTER,
732 gnc_sx_instance_model_init(GncSxInstanceModel *inst)
734 g_date_clear(&inst->range_end, 1);
735 inst->sx_instance_list = NULL;
740 _gnc_sx_instance_find_by_sx(
GncSxInstances *in_list_instances, SchedXaction *sx_to_find)
742 if (in_list_instances->sx == sx_to_find)
748 _gnc_sx_instance_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data)
750 GncSxInstanceModel *instances = GNC_SX_INSTANCE_MODEL(user_data);
757 if (!(GNC_IS_SX(ent) || GNC_IS_SXES(ent)))
763 gboolean sx_is_in_model = FALSE;
767 sx_is_in_model = (g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx) != NULL);
768 if (event_type & QOF_EVENT_MODIFY)
772 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
774 g_signal_emit_by_name(instances,
"updated", (gpointer)sx);
779 g_signal_emit_by_name(instances,
"removing", (gpointer)sx);
785 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
786 if (g_list_find(all_sxes, sx) && (!instances->include_disabled && xaccSchedXactionGetEnabled(sx)))
789 instances->sx_instance_list
790 = g_list_append(instances->sx_instance_list,
791 _gnc_sx_gen_instances((gpointer)sx, (gpointer) & instances->range_end));
792 g_signal_emit_by_name(instances,
"added", (gpointer)sx);
798 else if (GNC_IS_SXES(ent))
800 SchedXaction *sx = GNC_SX(evt_data);
802 if (event_type & GNC_EVENT_ITEM_REMOVED)
804 GList *instances_link;
805 instances_link = g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
806 if (instances_link != NULL)
808 g_signal_emit_by_name(instances,
"removing", (gpointer)sx);
810 else if (instances->include_disabled)
812 g_warning(
"could not remove instances that do not exist in the model");
817 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
820 instances->sx_instance_list
821 = g_list_append(instances->sx_instance_list,
822 _gnc_sx_gen_instances((gpointer)sx, (gpointer) & instances->range_end));
823 g_signal_emit_by_name(instances,
"added", (gpointer)sx);
830 typedef struct _HashListPair
837 _find_unreferenced_vars(gchar *key,
841 if (cb_pair->hash == NULL ||
842 !g_hash_table_lookup_extended(cb_pair->hash, key, NULL, NULL))
844 DEBUG(
"variable [%s] not found", key);
845 cb_pair->list = g_list_prepend (cb_pair->list, key);
855 link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
858 g_critical(
"couldn't find sx [%p]\n", sx);
864 new_instances = _gnc_sx_gen_instances((gpointer)sx, &model->range_end);
865 existing->sx = new_instances->sx;
866 existing->next_instance_date = new_instances->next_instance_date;
868 GList *existing_iter, *new_iter;
869 gboolean existing_remain, new_remain;
876 for (; existing_iter != NULL && new_iter != NULL; existing_iter = existing_iter->next, new_iter = new_iter->next)
879 gboolean same_instance_date;
883 same_instance_date = g_date_compare(&existing_inst->
date, &new_inst->
date) == 0;
884 if (!same_instance_date)
888 existing_remain = (existing_iter != NULL);
889 new_remain = (new_iter != NULL);
895 g_list_foreach(existing_iter, (GFunc)gnc_sx_instance_free, NULL);
901 GList *new_iter_iter;
904 for (new_iter_iter = new_iter; new_iter_iter != NULL; new_iter_iter = new_iter_iter->next)
910 g_list_free(new_iter);
916 GList *removed_var_names = NULL, *added_var_names = NULL;
917 GList *inst_iter = NULL;
923 removed_cb_data.list = NULL;
924 g_hash_table_foreach(existing->
variable_names, (GHFunc)_find_unreferenced_vars, &removed_cb_data);
925 removed_var_names = g_list_reverse (removed_cb_data.list);
927 DEBUG(
"%d removed variables", g_list_length(removed_var_names));
933 added_cb_data.list = NULL;
934 g_hash_table_foreach(new_instances->
variable_names, (GHFunc)_find_unreferenced_vars, &added_cb_data);
935 added_var_names = g_list_reverse (added_cb_data.list);
937 DEBUG(
"%d added variables", g_list_length(added_var_names));
946 for (inst_iter = existing->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
951 for (var_iter = removed_var_names; var_iter != NULL; var_iter = var_iter->next)
953 gchar *to_remove_key = (gchar*)var_iter->data;
957 for (var_iter = added_var_names; var_iter != NULL; var_iter = var_iter->next)
959 gchar *to_add_key = (gchar*)var_iter->data;
960 if (!g_hash_table_lookup_extended(
967 g_assert(parent_var != NULL);
968 var_copy = gnc_sx_variable_new_copy(parent_var);
974 gnc_sx_instances_free(new_instances);
978 gnc_sx_instance_model_remove_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
980 GList *instance_link = NULL;
982 instance_link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
983 if (instance_link == NULL)
985 g_warning(
"instance not found!\n");
989 model->sx_instance_list = g_list_remove_link(model->sx_instance_list, instance_link);
994 increment_sx_state(
GncSxInstance *inst, GDate **last_occur_date,
int *instance_count,
int *remain_occur_count)
996 if (!g_date_valid(*last_occur_date)
997 || (g_date_valid(*last_occur_date)
998 && g_date_compare(*last_occur_date, &inst->
date) <= 0))
1000 *last_occur_date = &inst->
date;
1005 if (*remain_occur_count > 0)
1007 *remain_occur_count -= 1;
1012 _get_template_split_account(
const SchedXaction* sx,
1013 const Split *template_split,
1015 GList **creation_errors)
1017 gboolean success = TRUE;
1020 "sx-account", &acct_guid,
1023 if (!*split_acct && sx && creation_errors)
1028 gchar* err = N_(
"Unknown account for guid [%s], cancelling SX [%s] creation.");
1030 REPORT_ERROR(creation_errors, err, guid_str, xaccSchedXactionGetName(sx));
1034 guid_free (acct_guid);
1039 _get_sx_formula_value(
const SchedXaction* sx,
1040 const Split *template_split,
1042 GList **creation_errors,
1043 const char *formula_key,
1044 const char* numeric_key,
1045 GHashTable *variable_bindings)
1048 char *formula_str = NULL, *parseErrorLoc = NULL;
1049 gnc_numeric *numeric_val = NULL;
1051 formula_key, &formula_str,
1052 numeric_key, &numeric_val,
1055 if ((variable_bindings == NULL ||
1056 g_hash_table_size (variable_bindings) == 0) &&
1057 numeric_val != NULL &&
1064 numeric->num = numeric_val->num;
1065 numeric->denom = numeric_val->denom;
1066 g_free (formula_str);
1067 g_free (numeric_val);
1071 if (formula_str != NULL && strlen(formula_str) != 0)
1073 GHashTable *parser_vars = NULL;
1074 if (variable_bindings)
1078 if (!gnc_exp_parser_parse_separate_vars(formula_str,
1083 gchar *err = N_(
"Error parsing SX [%s] key [%s]=formula [%s] at [%s]: %s.");
1084 REPORT_ERROR(creation_errors, err,
1085 xaccSchedXactionGetName(sx),
1089 gnc_exp_parser_error_string());
1092 if (parser_vars != NULL)
1094 g_hash_table_destroy(parser_vars);
1097 g_free (formula_str);
1098 g_free (numeric_val);
1103 const Split *template_split, gnc_numeric *credit_num,
1104 GList **creation_errors)
1106 _get_sx_formula_value(instance->
parent->sx, template_split, credit_num,
1107 creation_errors,
"sx-credit-formula",
1112 _get_debit_formula_value(
GncSxInstance *instance,
const Split *template_split,
1113 gnc_numeric *debit_num, GList **creation_errors)
1115 _get_sx_formula_value(instance->
parent->sx, template_split, debit_num,
1116 creation_errors,
"sx-debit-formula",
1123 gnc_numeric credit_num = gnc_numeric_zero();
1124 gnc_numeric debit_num = gnc_numeric_zero();
1127 SchedXaction *sx = creation_data->instance->
parent->sx;
1129 _get_credit_formula_value(creation_data->instance, split, &credit_num,
1130 creation_data->creation_errors);
1131 _get_debit_formula_value(creation_data->instance, split, &debit_num,
1132 creation_data->creation_errors);
1134 final = gnc_numeric_sub_fixed(debit_num, credit_num);
1139 gchar *err = N_(
"Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1140 REPORT_ERROR(creation_data->creation_errors, err,
1141 gncn_error, xaccSchedXactionGetName(sx));
1142 final = gnc_numeric_zero();
1148 split_apply_exchange_rate (Split *split, GHashTable *bindings,
1149 gnc_commodity *txn_cmdty,
1150 gnc_commodity *split_cmdty, gnc_numeric *
final)
1152 gchar *exchange_rate_var_name;
1155 gnc_numeric exchange_rate = gnc_numeric_create (1, 1);
1157 exchange_rate_var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
1160 exchange_rate_var_name);
1162 if (exchange_rate_var != NULL)
1164 exchange_rate = exchange_rate_var->
value;
1167 g_free (exchange_rate_var_name);
1196 static gnc_commodity*
1198 SchedXaction *sx, Transaction *template_txn)
1200 gnc_commodity *first_currency = NULL, *first_cmdty = NULL,
1201 *fallback_cmdty = NULL;
1202 gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
1205 GList** creation_errors =
1206 creation_data ? creation_data->creation_errors : NULL;
1209 DEBUG(
"Template txn currency is %s.",
1212 DEBUG(
"No template txn currency.");
1214 for (;txn_splits; txn_splits = txn_splits->next)
1216 Split* t_split = (Split*)txn_splits->data;
1217 Account* split_account = NULL;
1218 gnc_commodity *split_cmdty = NULL;
1220 if (!_get_template_split_account(sx, t_split, &split_account,
1229 if (!fallback_cmdty)
1232 if (split_is_marker(t_split))
1237 txn_cmdty = split_cmdty;
1239 first_cmdty = split_cmdty;
1241 txn_cmdty_in_splits = TRUE;
1243 first_currency = split_cmdty;
1247 g_critical(
"Error in SX transaction [%s], split missing account: " 1248 "Creation aborted.", xaccSchedXactionGetName(sx));
1251 if (first_currency &&
1253 return first_currency;
1254 if (!txn_cmdty_in_splits && first_cmdty)
1258 return fallback_cmdty;
1262 create_each_transaction_helper(Transaction *template_txn,
void *user_data)
1264 Transaction *new_txn;
1265 GList *txn_splits, *template_splits;
1266 Split *copying_split;
1268 SchedXaction *sx = creation_data->instance->
parent->sx;
1269 gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
1273 if (txn_cmdty == NULL)
1283 DEBUG(
"creating template txn desc [%s] for sx [%s]",
1285 xaccSchedXactionGetName(sx));
1293 g_date_get_day(&creation_data->instance->
date),
1294 g_date_get_month(&creation_data->instance->
date),
1295 g_date_get_year(&creation_data->instance->
date));
1300 if ((template_splits == NULL) || (txn_splits == NULL))
1302 g_critical(
"transaction w/o splits for sx [%s]",
1303 xaccSchedXactionGetName(sx));
1309 if (txn_cmdty == NULL)
1318 txn_splits && template_splits;
1319 txn_splits = txn_splits->next, template_splits = template_splits->next)
1321 const Split *template_split;
1323 gnc_commodity *split_cmdty = NULL;
1328 template_split = (Split*)template_splits->data;
1329 copying_split = (Split*)txn_splits->data;
1331 _get_template_split_account(sx, template_split, &split_acct,
1332 creation_data->creation_errors);
1335 xaccSplitSetAccount(copying_split, split_acct);
1338 gnc_numeric
final = split_apply_formulas(template_split,
1341 DEBUG(
"value is %s for memo split '%s'",
1346 split_apply_exchange_rate(copying_split,
1348 txn_cmdty, split_cmdty, &
final);
1358 "from-sched-xaction",
1365 if (creation_data->created_txn_guids != NULL)
1367 *creation_data->created_txn_guids
1368 = g_list_append(*(creation_data->created_txn_guids),
1376 create_transactions_for_instance(
GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
1381 sx_template_account = gnc_sx_get_template_transaction_account(instance->
parent->sx);
1383 creation_data.instance = instance;
1384 creation_data.created_txn_guids = created_txn_guids;
1385 creation_data.creation_errors = creation_errors;
1391 create_each_transaction_helper,
1398 gboolean auto_create_only,
1399 GList **created_transaction_guids,
1400 GList **creation_errors)
1410 for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
1412 GList *instance_iter;
1414 GDate *last_occur_date;
1415 gint instance_count = 0;
1416 gint remain_occur_count = 0;
1424 last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
1426 remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
1428 for (instance_iter = instances->
instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
1431 gboolean sx_is_auto_create;
1432 GList *instance_errors = NULL;
1434 xaccSchedXactionGetAutoCreate(inst->
parent->sx, &sx_is_auto_create, NULL);
1435 if (auto_create_only && !sx_is_auto_create)
1437 if (inst->
state != SX_INSTANCE_STATE_TO_CREATE)
1444 if (inst->
orig_state == SX_INSTANCE_STATE_POSTPONED
1445 && inst->
state != SX_INSTANCE_STATE_POSTPONED)
1453 switch (inst->
state)
1455 case SX_INSTANCE_STATE_CREATED:
1458 case SX_INSTANCE_STATE_IGNORED:
1459 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1461 case SX_INSTANCE_STATE_POSTPONED:
1462 if (inst->
orig_state != SX_INSTANCE_STATE_POSTPONED)
1467 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1469 case SX_INSTANCE_STATE_TO_CREATE:
1470 create_transactions_for_instance (inst,
1471 created_transaction_guids,
1473 if (instance_errors == NULL)
1475 increment_sx_state (inst, &last_occur_date,
1477 &remain_occur_count);
1479 (model, inst, SX_INSTANCE_STATE_CREATED);
1481 else if (creation_errors)
1483 *creation_errors = g_list_concat (*creation_errors,
1485 instance_errors = NULL;
1488 case SX_INSTANCE_STATE_REMINDER:
1493 g_assert_not_reached();
1497 if (instance_errors)
1498 g_list_free_full (instance_errors, g_free);
1501 xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
1503 xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
1510 GncSxInstanceState new_state)
1512 if (instance->
state == new_state)
1515 instance->
state = new_state;
1521 g_assert(inst_iter != NULL);
1522 if (instance->
state != SX_INSTANCE_STATE_REMINDER)
1525 for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
1528 if (prev_inst->
state != SX_INSTANCE_STATE_REMINDER)
1530 prev_inst->
state = SX_INSTANCE_STATE_POSTPONED;
1536 for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
1539 if (next_inst->
state == SX_INSTANCE_STATE_REMINDER)
1541 next_inst->
state = SX_INSTANCE_STATE_REMINDER;
1546 g_signal_emit_by_name(model,
"updated", (gpointer)instance->
parent->sx);
1550 gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
1553 gnc_numeric *new_value)
1558 variable->
value = *new_value;
1559 g_signal_emit_by_name(model,
"updated", (gpointer)instance->
parent->sx);
1563 _list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
1565 *result_list = g_list_prepend (*result_list, value);
1572 GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
1574 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1577 for (inst_iter = instances->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1581 if (inst->
state != SX_INSTANCE_STATE_TO_CREATE)
1584 g_hash_table_foreach(inst->
variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
1585 for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
1591 need->instance = inst;
1592 need->variable = var;
1593 rtn = g_list_prepend (rtn, need);
1596 g_list_free(var_list);
1606 GList *sx_iter, *inst_iter;
1608 g_return_if_fail(model != NULL);
1609 g_return_if_fail(summary != NULL);
1617 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1620 gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
1621 xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
1622 for (inst_iter = instances->
instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1627 if (inst->
state == SX_INSTANCE_STATE_TO_CREATE)
1629 if (sx_is_auto_create)
1665 static void gnc_numeric_free(gpointer data)
1667 gnc_numeric *p = (gnc_numeric*) data;
1674 NULL, gnc_numeric_free);
1680 GList **creation_errors;
1681 const SchedXaction *sx;
1685 static void add_to_hash_amount(GHashTable* hash,
const GncGUID* guid,
const gnc_numeric* amount)
1690 gnc_numeric* elem = g_hash_table_lookup(hash, guid);
1695 elem = g_new0(gnc_numeric, 1);
1696 *elem = gnc_numeric_zero();
1697 g_hash_table_insert(hash, (gpointer) guid, elem);
1703 g_critical(
"Oops, the given amount [%s] has the error code %d, at guid [%s].",
1711 g_critical(
"Oops, the account's amount [%s] has the error code %d, at guid [%s].",
1729 g_critical(
"Oops, after addition at guid [%s] the resulting amount [%s] has the error code %d; added amount = [%s].",
1738 DEBUG(
"Adding to guid [%s] the value [%s]. Value now [%s].",
1745 create_cashflow_helper(Transaction *template_txn,
void *user_data)
1748 GList *template_splits;
1749 const gnc_commodity *first_cmdty = NULL;
1751 DEBUG(
"Evaluating txn desc [%s] for sx [%s]",
1753 xaccSchedXactionGetName(creation_data->sx));
1757 if (template_splits == NULL)
1759 g_critical(
"transaction w/o splits for sx [%s]",
1760 xaccSchedXactionGetName(creation_data->sx));
1766 template_splits = template_splits->next)
1769 const gnc_commodity *split_cmdty = NULL;
1770 const Split *template_split = (
const Split*) template_splits->data;
1773 if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors))
1775 DEBUG(
"Could not find account for split");
1781 if (first_cmdty == NULL)
1783 first_cmdty = split_cmdty;
1788 gnc_numeric credit_num = gnc_numeric_zero();
1789 gnc_numeric debit_num = gnc_numeric_zero();
1790 gnc_numeric final_once,
final;
1794 _get_sx_formula_value(creation_data->sx, template_split,
1795 &credit_num, creation_data->creation_errors,
1796 "sx-credit-formula",
"sx-credit-numeric",
1799 _get_sx_formula_value(creation_data->sx, template_split,
1800 &debit_num, creation_data->creation_errors,
1801 "sx-debit-formula",
"sx-debit-numeric", NULL);
1805 final_once = gnc_numeric_sub_fixed( debit_num, credit_num );
1808 gnc_numeric_denom(final_once),
1814 gchar* err = N_(
"Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1815 REPORT_ERROR(creation_data->creation_errors, err,
1816 gncn_error, xaccSchedXactionGetName(creation_data->sx));
1817 final = gnc_numeric_zero();
1823 gchar *err = N_(
"No exchange rate available in SX [%s] for %s -> %s, value is zero.");
1824 REPORT_ERROR(creation_data->creation_errors, err,
1825 xaccSchedXactionGetName(creation_data->sx),
1828 final = gnc_numeric_zero();
1840 instantiate_cashflow_internal(
const SchedXaction* sx,
1842 GList **creation_errors, gint count)
1845 Account* sx_template_account = gnc_sx_get_template_transaction_account(sx);
1847 if (!sx_template_account)
1849 g_critical(
"Huh? No template account for the SX %s", xaccSchedXactionGetName(sx));
1853 if (!xaccSchedXactionGetEnabled(sx))
1855 DEBUG(
"Skipping non-enabled SX [%s]",
1856 xaccSchedXactionGetName(sx));
1860 create_cashflow_data.hash = map;
1861 create_cashflow_data.creation_errors = creation_errors;
1862 create_cashflow_data.sx = sx;
1863 create_cashflow_data.count = gnc_numeric_create(count, 1);
1868 create_cashflow_helper,
1869 &create_cashflow_data);
1875 GList **creation_errors;
1876 const GDate *range_start;
1877 const GDate *range_end;
1880 static void instantiate_cashflow_cb(gpointer data, gpointer _user_data)
1882 const SchedXaction* sx = (
const SchedXaction*) data;
1891 userdata->range_end);
1897 instantiate_cashflow_internal(sx,
1899 userdata->creation_errors,
1905 const GDate *range_start,
const GDate *range_end,
1906 GHashTable* map, GList **creation_errors)
1909 userdata.hash = map;
1910 userdata.creation_errors = creation_errors;
1911 userdata.range_start = range_start;
1912 userdata.range_end = range_end;
1915 g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
1922 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
1924 &range_start, &range_end,
1931 return model->sx_instance_list;
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
Never round at all, and signal an error if there is a fractional result in a computation.
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.
gint num_to_create_instances
The number of (not-auto-create) to-create instances.
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.
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
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)
GList * gnc_sx_instance_model_get_sx_instances_list(GncSxInstanceModel *model)
Returns the list of GncSxInstances in the model (Each element in the list has type GncSxInstances) ...
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.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
time64 gnc_time(time64 *tbuf)
get the current 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.