GnuCash  4.11-517-g41de4cefce
gnc-sx-instance-model.c
1 /*
2  * gnc-sx-instance-model.c
3  *
4  * Copyright (C) 2006 Josh Sled <jsled@asynchronous.org>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 and/or version 3 of the GNU General Public
8  * License as published by the Free Software Foundation.
9  *
10  * As a special exception, permission is granted to link the binary module
11  * resultant from this code with the OpenSSL project's "OpenSSL" library (or
12  * modified versions of it that use the same license as the "OpenSSL"
13  * library), and distribute the linked executable. You must obey the GNU
14  * General Public License in all respects for all of the code used other than
15  * "OpenSSL". If you modify this file, you may extend this exception to your
16  * version of the file, but you are not obligated to do so. If you do not
17  * wish to do so, delete this exception statement from your version of this
18  * file.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, contact:
27  *
28  * Free Software Foundation Voice: +1-617-542-5942
29  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
30  * Boston, MA 02110-1301, USA gnu@gnu.org
31  */
32 
33 #include <config.h>
34 #include <glib.h>
35 #include <glib/gi18n.h>
36 #include <glib-object.h>
37 #include <stdlib.h>
38 
39 #include "Account.h"
40 #include "SX-book.h"
41 #include "SchedXaction.h"
42 #include "Scrub.h"
43 #include "Split.h"
44 #include "Transaction.h"
45 #include "gnc-commodity.h"
46 #include "gnc-date.h"
47 #include "gnc-event.h"
48 #include "gnc-exp-parser.h"
49 #include "gnc-glib-utils.h"
50 #include "gnc-sx-instance-model.h"
51 #include "gnc-ui-util.h"
52 #include "qof.h"
53 
54 #undef G_LOG_DOMAIN
55 #define G_LOG_DOMAIN "gnc.app-utils.sx"
56 static QofLogModule log_module = G_LOG_DOMAIN;
57 
62 #define REPORT_ERROR(list, format, ...) do { \
63  g_critical(format, __VA_ARGS__); \
64  if (list != NULL) \
65  *list = g_list_append(*list, g_strdup_printf(_(format), __VA_ARGS__)); \
66 } while (0)
67 
68 static GObjectClass *parent_class = NULL;
69 
70 typedef struct _SxTxnCreationData
71 {
72  GncSxInstance *instance;
73  GList **created_txn_guids;
74  GList **creation_errors;
76 
77 static void gnc_sx_instance_model_class_init (GncSxInstanceModelClass *klass);
78 static void gnc_sx_instance_model_init(GTypeInstance *instance, gpointer klass);
79 static GncSxInstanceModel* gnc_sx_instance_model_new(void);
80 
81 static GncSxInstance* gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceState state, GDate *date, void *temporal_state, gint sequence_num);
82 
83 static gint _get_vars_helper(Transaction *txn, void *var_hash_data);
84 
85 static GncSxVariable* gnc_sx_variable_new(gchar *name);
86 
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);
89 /* ------------------------------------------------------------ */
90 
91 typedef struct
92 {
93  const char *name;
94  gnc_numeric amount;
95 } ScrubItem;
96 
97 static void
98 scrub_sx_split_numeric (Split* split, gboolean is_credit, GList **changes)
99 {
100  const char *formula = is_credit ? "sx-credit-formula" : "sx-debit-formula";
101  const char *numeric = is_credit ? "sx-credit-numeric" : "sx-debit-numeric";
102  char *formval;
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);
106  char *error_loc;
107  gnc_numeric amount = gnc_numeric_zero ();
108  gboolean parse_result = FALSE;
109 
110  qof_instance_get (QOF_INSTANCE (split),
111  formula, &formval,
112  numeric, &numval,
113  NULL);
114 
115  parse_result = gnc_exp_parser_parse_separate_vars (formval, &amount,
116  &error_loc, parser_vars);
117 
118  if (!parse_result || g_hash_table_size (parser_vars) != 0)
119  amount = gnc_numeric_zero ();
120 
121  if (!numval || !gnc_numeric_eq (amount, *numval))
122  {
123  ScrubItem *change = g_new (ScrubItem, 1);
124  change->name = numeric;
125  change->amount = amount;
126  *changes = g_list_prepend (*changes, change);
127  }
128 
129  g_hash_table_destroy (parser_vars);
130  g_free (formval);
131  g_free (numval);
132 }
133 
134 /* Fixes error in pre-2.6.16 where the numeric slot wouldn't get changed if the
135  * formula slot was edited.
136  */
137 void
138 gnc_sx_scrub_split_numerics (gpointer psplit, gpointer puser)
139 {
140  Split *split = GNC_SPLIT (psplit);
141  Transaction *trans = xaccSplitGetParent (split);
142  GList *changes = NULL;
143  scrub_sx_split_numeric (split, TRUE, &changes);
144  scrub_sx_split_numeric (split, FALSE, &changes);
145  if (!changes)
146  return;
147 
148  xaccTransBeginEdit (trans);
149  for (GList *n = changes; n; n = n->next)
150  {
151  ScrubItem *change = n->data;
152  qof_instance_set (QOF_INSTANCE (split),
153  change->name, &change->amount,
154  NULL);
155  }
156  xaccTransCommitEdit (trans);
157  g_list_free_full (changes, g_free);
158 }
159 
160 static void
161 _sx_var_to_raw_numeric(gchar *name, GncSxVariable *var, GHashTable *parser_var_hash)
162 {
163  g_hash_table_insert(parser_var_hash, g_strdup(name), &var->value);
164 }
165 
166 static void
167 _var_numeric_to_sx_var(gchar *name, gnc_numeric *num, GHashTable *sx_var_hash)
168 {
169  gpointer p_var;
170  if (!g_hash_table_lookup_extended(sx_var_hash, name, NULL, &p_var))
171  {
172  p_var = (gpointer)gnc_sx_variable_new(name);
173  g_hash_table_insert(sx_var_hash, g_strdup(name), p_var);
174  }
175  ((GncSxVariable*)p_var)->value = *num;
176 }
177 
178 static void
179 _wipe_parsed_sx_var(gchar *key, GncSxVariable *var, gpointer unused_user_data)
180 {
182 }
183 
184 static gboolean
185 split_is_marker(Split *split)
186 {
187  gchar *credit_formula = NULL;
188  gchar *debit_formula = NULL;
189  gboolean split_is_marker = TRUE;
190 
191  qof_instance_get (QOF_INSTANCE (split),
192  "sx-credit-formula", &credit_formula,
193  "sx-debit-formula", &debit_formula,
194  NULL);
195 
196  if ((credit_formula && *credit_formula) ||
197  (debit_formula && *debit_formula))
198  split_is_marker = FALSE;
199 
200  g_free(credit_formula);
201  g_free(debit_formula);
202  return split_is_marker;
203 }
204 
208 GHashTable*
209 gnc_sx_instance_get_variables_for_parser(GHashTable *instance_var_hash)
210 {
211  GHashTable *parser_vars;
212 
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);
215  return parser_vars;
216 }
217 
218 int
219 gnc_sx_parse_vars_from_formula(const char *formula,
220  GHashTable *var_hash,
221  gnc_numeric *result)
222 {
223  gnc_numeric num;
224  char *errLoc = NULL;
225  int toRet = 0;
226  GHashTable *parser_vars;
227 
228  // convert var_hash -> variables for the parser.
229  parser_vars = gnc_sx_instance_get_variables_for_parser(var_hash);
230 
231  num = gnc_numeric_zero();
232  if (!gnc_exp_parser_parse_separate_vars(formula, &num, &errLoc, parser_vars))
233  {
234  toRet = -1;
235  }
236 
237  // convert back.
238  g_hash_table_foreach(parser_vars, (GHFunc)_var_numeric_to_sx_var, var_hash);
239  g_hash_table_destroy(parser_vars);
240 
241  if (result != NULL)
242  {
243  *result = num;
244  }
245 
246  return toRet;
247 }
248 
249 static GncSxVariable*
250 gnc_sx_variable_new(gchar *name)
251 {
252  GncSxVariable *var = g_new0(GncSxVariable, 1);
253  var->name = g_strdup(name);
255  var->editable = TRUE;
256  return var;
257 }
258 
260 gnc_sx_variable_new_full(gchar *name, gnc_numeric value, gboolean editable)
261 {
262  GncSxVariable *var = gnc_sx_variable_new(name);
263  var->value = value;
264  var->editable = editable;
265  return var;
266 }
267 
268 static GncSxVariable*
269 gnc_sx_variable_new_copy(GncSxVariable *to_copy)
270 {
271  GncSxVariable *var = gnc_sx_variable_new(to_copy->name);
272  var->value = to_copy->value;
273  var->editable = to_copy->editable;
274  return var;
275 }
276 
277 void
278 gnc_sx_variable_free(GncSxVariable *var)
279 {
280  g_free(var->name);
281  g_free(var);
282 }
283 
284 static inline gchar*
285 var_name_from_commodities(gnc_commodity* split_c, gnc_commodity* txn_c)
286 {
287  const gchar* split_m = gnc_commodity_get_mnemonic(split_c);
288  const gchar* txn_m = gnc_commodity_get_mnemonic(txn_c);
289  gchar* var_name = g_strdup_printf ("%s -> %s",
290  split_m ? split_m : "(null)",
291  txn_m ? txn_m : "(null)");
292 
293  DEBUG("var_name is %s", var_name);
294  return var_name;
295 }
296 
297 static gint
298 _get_vars_helper(Transaction *txn, void *var_hash_data)
299 {
300  GHashTable *var_hash = (GHashTable*)var_hash_data;
301  GList *split_list;
302  Split *s;
303  gchar *credit_formula = NULL;
304  gchar *debit_formula = NULL;
305  gnc_commodity *txn_cmdty = get_transaction_currency(NULL, NULL, txn);
306 
307  split_list = xaccTransGetSplitList(txn);
308  if (split_list == NULL)
309  {
310  return 1;
311  }
312 
313  for ( ; split_list; split_list = split_list->next)
314  {
315  gnc_commodity *split_cmdty = NULL;
316  GncGUID *acct_guid = NULL;
317  Account *acct;
318  gboolean split_is_marker = TRUE;
319 
320  s = (Split*)split_list->data;
321 
322  qof_instance_get (QOF_INSTANCE (s),
323  "sx-account", &acct_guid,
324  "sx-credit-formula", &credit_formula,
325  "sx-debit-formula", &debit_formula,
326  NULL);
327  acct = xaccAccountLookup(acct_guid, gnc_get_current_book());
328  guid_free (acct_guid);
329  split_cmdty = xaccAccountGetCommodity(acct);
330  // existing... ------------------------------------------
331  if (credit_formula && strlen(credit_formula) != 0)
332  {
333  gnc_sx_parse_vars_from_formula(credit_formula, var_hash, NULL);
334  split_is_marker = FALSE;
335  }
336  if (debit_formula && strlen(debit_formula) != 0)
337  {
338  gnc_sx_parse_vars_from_formula(debit_formula, var_hash, NULL);
339  split_is_marker = FALSE;
340  }
341  g_free (credit_formula);
342  g_free (debit_formula);
343 
344  if (split_is_marker)
345  continue;
346 
347  if (! gnc_commodity_equal(split_cmdty, txn_cmdty))
348  {
349  GncSxVariable *var;
350  gchar *var_name;
351 
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);
355  }
356  }
357 
358  return 0;
359 }
360 
361 Account*
362 gnc_sx_get_template_transaction_account(const SchedXaction *sx)
363 {
364  Account *template_root, *sx_template_acct;
365  char sx_guid_str[GUID_ENCODING_LENGTH+1];
366 
367  template_root = gnc_book_get_template_root(gnc_get_current_book());
369  sx_template_acct = gnc_account_lookup_by_name(template_root, sx_guid_str);
370  return sx_template_acct;
371 }
372 
373 void
374 gnc_sx_get_variables(SchedXaction *sx, GHashTable *var_hash)
375 {
376  Account *sx_template_acct = gnc_sx_get_template_transaction_account(sx);
377  xaccAccountForEachTransaction(sx_template_acct, _get_vars_helper, var_hash);
378 }
379 
380 static void
381 _set_var_to_random_value(gchar *key, GncSxVariable *var, gpointer unused_user_data)
382 {
383  /* This is used by dialog-sx-editor to plug in values as a simplistic way to
384  * check balances. One possible variable is the number of periods in a
385  * interest or future value calculation where the variable is used as an
386  * exponent, so we want the numbers to be monotonically > 0 and not so large
387  * that they'll cause overflows.
388  */
389  var->value = gnc_numeric_create(g_random_int_range(1, 1000), 1);
390 }
391 
392 void
393 gnc_sx_randomize_variables(GHashTable *vars)
394 {
395  g_hash_table_foreach(vars, (GHFunc)_set_var_to_random_value, NULL);
396 }
397 
398 static void
399 _clone_sx_var_hash_entry(gpointer key, gpointer value, gpointer user_data)
400 {
401  GHashTable *to = (GHashTable*)user_data;
402  GncSxVariable *to_copy = (GncSxVariable*)value;
403  GncSxVariable *var = gnc_sx_variable_new_copy(to_copy);
404  g_hash_table_insert(to, g_strdup(key), var);
405 }
406 
407 static GncSxInstance*
408 gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceState state, GDate *date, void *temporal_state, gint sequence_num)
409 {
410  GncSxInstance *rtn = g_new0(GncSxInstance, 1);
411  rtn->parent = parent;
412  rtn->orig_state = state;
413  rtn->state = state;
414  g_date_clear(&rtn->date, 1);
415  rtn->date = *date;
416  rtn->temporal_state = gnc_sx_clone_temporal_state(temporal_state);
417 
418  if (! parent->variable_names_parsed)
419  {
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;
424  }
425 
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);
428 
429  {
430  int instance_i_value;
431  gnc_numeric i_num;
432  GncSxVariable *as_var;
433 
434  instance_i_value = gnc_sx_get_instance_count(rtn->parent->sx, rtn->temporal_state);
435  i_num = gnc_numeric_create(instance_i_value, 1);
436  as_var = gnc_sx_variable_new_full("i", i_num, FALSE);
437 
438  g_hash_table_insert(rtn->variable_bindings, g_strdup("i"), as_var);
439  }
440 
441  return rtn;
442 }
443 
444 static gint
445 _compare_GncSxVariables(gconstpointer a, gconstpointer b)
446 {
447  return strcmp(((const GncSxVariable*)a)->name, ((const GncSxVariable*)b)->name);
448 }
449 
450 static void
451 _build_list_from_hash_elts(gpointer key, gpointer value, gpointer user_data)
452 {
453  GList **list = (GList**)user_data;
454  *list = g_list_prepend (*list, value);
455 }
456 
457 GList *
459 {
460  GList *vars = NULL;
461  g_hash_table_foreach(inst->variable_bindings, _build_list_from_hash_elts, &vars);
462  return g_list_sort (vars, _compare_GncSxVariables);
463 }
464 
465 static GncSxInstances*
466 _gnc_sx_gen_instances(gpointer *data, gpointer user_data)
467 {
468  GncSxInstances *instances = g_new0(GncSxInstances, 1);
469  GList *instlist = NULL;
470  SchedXaction *sx = (SchedXaction*)data;
471  const GDate *range_end = (const GDate*)user_data;
472  GDate creation_end, remind_end;
473  GDate cur_date;
474  SXTmpStateData *temporal_state = gnc_sx_create_temporal_state(sx);
475 
476  instances->sx = sx;
477 
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));
482 
483  /* postponed */
484  {
485  GList *postponed = gnc_sx_get_defer_instances(sx);
486  for ( ; postponed != NULL; postponed = postponed->next)
487  {
488  GDate inst_date;
489  int seq_num;
490  GncSxInstance *inst;
491 
492  g_date_clear(&inst_date, 1);
493  inst_date = xaccSchedXactionGetNextInstance(sx, postponed->data);
494  seq_num = gnc_sx_get_instance_count(sx, postponed->data);
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);
498  gnc_sx_destroy_temporal_state(temporal_state);
499  temporal_state = gnc_sx_clone_temporal_state(postponed->data);
500  gnc_sx_incr_temporal_state(sx, temporal_state);
501  }
502  }
503 
504  /* to-create */
505  g_date_clear(&cur_date, 1);
506  cur_date = xaccSchedXactionGetNextInstance(sx, temporal_state);
507  instances->next_instance_date = cur_date;
508  while (g_date_valid(&cur_date) && g_date_compare(&cur_date, &creation_end) <= 0)
509  {
510  GncSxInstance *inst;
511  int seq_num;
512  seq_num = gnc_sx_get_instance_count(sx, temporal_state);
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);
516  gnc_sx_incr_temporal_state(sx, temporal_state);
517  cur_date = xaccSchedXactionGetNextInstance(sx, temporal_state);
518  }
519 
520  /* reminders */
521  while (g_date_valid(&cur_date) &&
522  g_date_compare(&cur_date, &remind_end) <= 0)
523  {
524  GncSxInstance *inst;
525  int seq_num;
526  seq_num = gnc_sx_get_instance_count(sx, temporal_state);
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);
530  gnc_sx_incr_temporal_state(sx, temporal_state);
531  cur_date = xaccSchedXactionGetNextInstance(sx, temporal_state);
532  }
533 
534  instances->instance_list = g_list_reverse (instlist);
535 
536  gnc_sx_destroy_temporal_state (temporal_state);
537 
538  return instances;
539 }
540 
543 {
544  GDate now;
545  g_date_clear(&now, 1);
546  gnc_gdate_set_time64 (&now, gnc_time (NULL));
547  return gnc_sx_get_instances(&now, FALSE);
548 }
549 
551 gnc_sx_get_instances(const GDate *range_end, gboolean include_disabled)
552 {
553  GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
554  GncSxInstanceModel *instances;
555 
556  g_assert(range_end != NULL);
557  g_assert(g_date_valid(range_end));
558 
559  instances = gnc_sx_instance_model_new();
560  instances->include_disabled = include_disabled;
561  instances->range_end = *range_end;
562 
563  if (include_disabled)
564  {
565  instances->sx_instance_list = gnc_g_list_map(all_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
566  }
567  else
568  {
569  GList *sx_iter = g_list_first(all_sxes);
570  GList *enabled_sxes = NULL;
571 
572  for (; sx_iter != NULL; sx_iter = sx_iter->next)
573  {
574  SchedXaction *sx = (SchedXaction*)sx_iter->data;
575  if (xaccSchedXactionGetEnabled(sx))
576  {
577  enabled_sxes = g_list_prepend (enabled_sxes, sx);
578  }
579  }
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);
583  }
584 
585  return instances;
586 }
587 static GncSxInstanceModel*
588 gnc_sx_instance_model_new(void)
589 {
590  return GNC_SX_INSTANCE_MODEL(g_object_new(GNC_TYPE_SX_INSTANCE_MODEL, NULL));
591 }
592 
593 GType
594 gnc_sx_instance_model_get_type(void)
595 {
596  static GType type = 0;
597  if (type == 0)
598  {
599  static const GTypeInfo info =
600  {
601  sizeof (GncSxInstanceModelClass),
602  NULL, /* base_init */
603  NULL, /* base_finalize */
604  (GClassInitFunc)gnc_sx_instance_model_class_init, /* class_init */
605  NULL, /* class_finalize */
606  NULL, /* class_data */
607  sizeof (GncSxInstanceModel),
608  0, /* n_preallocs */
609  (GInstanceInitFunc)gnc_sx_instance_model_init /* instance_init */
610  };
611  type = g_type_register_static (G_TYPE_OBJECT,
612  "GncSxInstanceModelType",
613  &info, 0);
614  }
615  return type;
616 }
617 
618 static void
619 gnc_sx_instance_model_dispose(GObject *object)
620 {
621  GncSxInstanceModel *model;
622  g_return_if_fail(object != NULL);
623  model = GNC_SX_INSTANCE_MODEL(object);
624 
625  g_return_if_fail(!model->disposed);
626  model->disposed = TRUE;
627 
628  qof_event_unregister_handler(model->qof_event_handler_id);
629 
630  G_OBJECT_CLASS(parent_class)->dispose(object);
631 }
632 
633 static void
634 gnc_sx_instance_free(GncSxInstance *instance)
635 {
637 
638  if (instance->variable_bindings != NULL)
639  {
640  g_hash_table_destroy(instance->variable_bindings);
641  }
642  instance->variable_bindings = NULL;
643 
644  g_free(instance);
645 }
646 
647 static void
648 gnc_sx_instances_free(GncSxInstances *instances)
649 {
650  GList *instance_iter;
651 
652  if (instances->variable_names != NULL)
653  {
654  g_hash_table_destroy(instances->variable_names);
655  }
656  instances->variable_names = NULL;
657 
658  instances->sx = NULL;
659 
660  for (instance_iter = instances->instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
661  {
662  GncSxInstance *inst = (GncSxInstance*)instance_iter->data;
663  gnc_sx_instance_free(inst);
664  }
665  g_list_free(instances->instance_list);
666  instances->instance_list = NULL;
667 
668  g_free(instances);
669 }
670 
671 static void
672 gnc_sx_instance_model_finalize (GObject *object)
673 {
674  GncSxInstanceModel *model;
675  GList *sx_list_iter;
676 
677  g_return_if_fail(object != NULL);
678 
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)
681  {
682  GncSxInstances *instances = (GncSxInstances*)sx_list_iter->data;
683  gnc_sx_instances_free(instances);
684  }
685  g_list_free(model->sx_instance_list);
686  model->sx_instance_list = NULL;
687 
688  G_OBJECT_CLASS(parent_class)->finalize(object);
689 }
690 
691 static void
692 gnc_sx_instance_model_class_init (GncSxInstanceModelClass *klass)
693 {
694  GObjectClass *object_class = G_OBJECT_CLASS(klass);
695 
696  parent_class = g_type_class_peek_parent(klass);
697 
698  object_class->dispose = gnc_sx_instance_model_dispose;
699  object_class->finalize = gnc_sx_instance_model_finalize;
700 
701  klass->removing_signal_id =
702  g_signal_new("removing",
703  GNC_TYPE_SX_INSTANCE_MODEL,
704  G_SIGNAL_RUN_FIRST,
705  0, /* class offset */
706  NULL, /* accumulator */
707  NULL, /* accum data */
708  g_cclosure_marshal_VOID__POINTER,
709  G_TYPE_NONE,
710  1,
711  G_TYPE_POINTER);
712 
713  klass->updated_signal_id =
714  g_signal_new("updated",
715  GNC_TYPE_SX_INSTANCE_MODEL,
716  G_SIGNAL_RUN_FIRST,
717  0, /* class offset */
718  NULL, /* accumulator */
719  NULL, /* accum data */
720  g_cclosure_marshal_VOID__POINTER,
721  G_TYPE_NONE,
722  1,
723  G_TYPE_POINTER);
724 
725  klass->added_signal_id =
726  g_signal_new("added",
727  GNC_TYPE_SX_INSTANCE_MODEL,
728  G_SIGNAL_RUN_FIRST,
729  0, /* class offset */
730  NULL, /* accumulator */
731  NULL, /* accum data */
732  g_cclosure_marshal_VOID__POINTER,
733  G_TYPE_NONE,
734  1,
735  G_TYPE_POINTER);
736 }
737 
738 static void
739 gnc_sx_instance_model_init(GTypeInstance *instance, gpointer klass)
740 {
741  GncSxInstanceModel *inst = (GncSxInstanceModel*)instance;
742 
743  g_date_clear(&inst->range_end, 1);
744  inst->sx_instance_list = NULL;
745  inst->qof_event_handler_id = qof_event_register_handler(_gnc_sx_instance_event_handler, inst);
746 }
747 
748 static gint
749 _gnc_sx_instance_find_by_sx(GncSxInstances *in_list_instances, SchedXaction *sx_to_find)
750 {
751  if (in_list_instances->sx == sx_to_find)
752  return 0;
753  return -1;
754 }
755 
756 static void
757 _gnc_sx_instance_event_handler(QofInstance *ent, QofEventId event_type, gpointer user_data, gpointer evt_data)
758 {
759  GncSxInstanceModel *instances = GNC_SX_INSTANCE_MODEL(user_data);
760 
761  /* selection rules {
762  // (gnc_collection_get_schedxaction_list(book), GNC_EVENT_ITEM_ADDED)
763  // (gnc_collection_get_schedxaction_list(book), GNC_EVENT_ITEM_REMOVED)
764  // (GNC_IS_SX(ent), QOF_EVENT_MODIFIED)
765  // } */
766  if (!(GNC_IS_SX(ent) || GNC_IS_SXES(ent)))
767  return;
768 
769  if (GNC_IS_SX(ent))
770  {
771  SchedXaction *sx;
772  gboolean sx_is_in_model = FALSE;
773 
774  sx = GNC_SX(ent);
775  // only send `updated` if it's actually in the model
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)
778  {
779  if (sx_is_in_model)
780  {
781  if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
782  {
783  g_signal_emit_by_name(instances, "updated", (gpointer)sx);
784  }
785  else
786  {
787  /* the sx was enabled but is now disabled */
788  g_signal_emit_by_name(instances, "removing", (gpointer)sx);
789  }
790  }
791  else
792  {
793  /* determine if this is a legitimate SX or just a "one-off" / being created */
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)))
796  {
797  /* it's moved from disabled to enabled, add the instances */
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);
802  }
803  }
804  }
805  /* else { unsupported event type; ignore } */
806  }
807  else if (GNC_IS_SXES(ent))
808  {
809  SchedXaction *sx = GNC_SX(evt_data);
810 
811  if (event_type & GNC_EVENT_ITEM_REMOVED)
812  {
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)
816  {
817  g_signal_emit_by_name(instances, "removing", (gpointer)sx);
818  }
819  else if (instances->include_disabled)
820  {
821  g_warning("could not remove instances that do not exist in the model");
822  }
823  }
824  else if (event_type & GNC_EVENT_ITEM_ADDED)
825  {
826  if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
827  {
828  /* generate instances, add to instance list, emit update. */
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);
833  }
834  }
835  /* else { g_critical("unsupported event type [%d]\n", event_type); } */
836  }
837 }
838 
839 typedef struct _HashListPair
840 {
841  GHashTable *hash;
842  GList *list;
843 } HashListPair;
844 
845 static void
846 _find_unreferenced_vars(gchar *key,
847  gpointer value,
848  HashListPair *cb_pair)
849 {
850  if (cb_pair->hash == NULL ||
851  !g_hash_table_lookup_extended(cb_pair->hash, key, NULL, NULL))
852  {
853  DEBUG("variable [%s] not found", key);
854  cb_pair->list = g_list_prepend (cb_pair->list, key);
855  }
856 }
857 
858 void
860 {
861  GncSxInstances *existing, *new_instances;
862  GList *link;
863 
864  link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
865  if (link == NULL)
866  {
867  g_critical("couldn't find sx [%p]\n", sx);
868  return;
869  }
870 
871  // merge the new instance data into the existing structure, mutating as little as possible.
872  existing = (GncSxInstances*)link->data;
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;
876  {
877  GList *existing_iter, *new_iter;
878  gboolean existing_remain, new_remain;
879 
880  // step through the lists pairwise, and retain the existing
881  // instance if the dates align, as soon as they don't stop and
882  // cleanup.
883  existing_iter = existing->instance_list;
884  new_iter = new_instances->instance_list;
885  for (; existing_iter != NULL && new_iter != NULL; existing_iter = existing_iter->next, new_iter = new_iter->next)
886  {
887  GncSxInstance *existing_inst, *new_inst;
888  gboolean same_instance_date;
889  existing_inst = (GncSxInstance*)existing_iter->data;
890  new_inst = (GncSxInstance*)new_iter->data;
891 
892  same_instance_date = g_date_compare(&existing_inst->date, &new_inst->date) == 0;
893  if (!same_instance_date)
894  break;
895  }
896 
897  existing_remain = (existing_iter != NULL);
898  new_remain = (new_iter != NULL);
899 
900  if (existing_remain)
901  {
902  // delete excess
903  gnc_g_list_cut(&existing->instance_list, existing_iter);
904  g_list_foreach(existing_iter, (GFunc)gnc_sx_instance_free, NULL);
905  }
906 
907  if (new_remain)
908  {
909  // append new
910  GList *new_iter_iter;
911  gnc_g_list_cut(&new_instances->instance_list, new_iter);
912 
913  for (new_iter_iter = new_iter; new_iter_iter != NULL; new_iter_iter = new_iter_iter->next)
914  {
915  GncSxInstance *inst = (GncSxInstance*)new_iter_iter->data;
916  inst->parent = existing;
917  existing->instance_list = g_list_append(existing->instance_list, new_iter_iter->data);
918  }
919  g_list_free(new_iter);
920  }
921  }
922 
923  // handle variables
924  {
925  GList *removed_var_names = NULL, *added_var_names = NULL;
926  GList *inst_iter = NULL;
927 
928  if (existing->variable_names != NULL)
929  {
930  HashListPair removed_cb_data;
931  removed_cb_data.hash = new_instances->variable_names;
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);
935  }
936  DEBUG("%d removed variables", g_list_length(removed_var_names));
937 
938  if (new_instances->variable_names != NULL)
939  {
940  HashListPair added_cb_data;
941  added_cb_data.hash = existing->variable_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);
945  }
946  DEBUG("%d added variables", g_list_length(added_var_names));
947 
948  if (existing->variable_names != NULL)
949  {
950  g_hash_table_destroy(existing->variable_names);
951  }
952  existing->variable_names = new_instances->variable_names;
953  new_instances->variable_names = NULL;
954 
955  for (inst_iter = existing->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
956  {
957  GList *var_iter;
958  GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
959 
960  for (var_iter = removed_var_names; var_iter != NULL; var_iter = var_iter->next)
961  {
962  gchar *to_remove_key = (gchar*)var_iter->data;
963  g_hash_table_remove(inst->variable_bindings, to_remove_key);
964  }
965 
966  for (var_iter = added_var_names; var_iter != NULL; var_iter = var_iter->next)
967  {
968  gchar *to_add_key = (gchar*)var_iter->data;
969  if (!g_hash_table_lookup_extended(
970  inst->variable_bindings, to_add_key, NULL, NULL))
971  {
972  GncSxVariable *parent_var
973  = g_hash_table_lookup(existing->variable_names, to_add_key);
974  GncSxVariable *var_copy;
975 
976  g_assert(parent_var != NULL);
977  var_copy = gnc_sx_variable_new_copy(parent_var);
978  g_hash_table_insert(inst->variable_bindings, g_strdup(to_add_key), var_copy);
979  }
980  }
981  }
982  }
983  gnc_sx_instances_free(new_instances);
984 }
985 
986 void
987 gnc_sx_instance_model_remove_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
988 {
989  GList *instance_link = NULL;
990 
991  instance_link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
992  if (instance_link == NULL)
993  {
994  g_warning("instance not found!\n");
995  return;
996  }
997 
998  model->sx_instance_list = g_list_remove_link(model->sx_instance_list, instance_link);
999  gnc_sx_instances_free((GncSxInstances*)instance_link->data);
1000 }
1001 
1002 static void
1003 increment_sx_state(GncSxInstance *inst, GDate **last_occur_date, int *instance_count, int *remain_occur_count)
1004 {
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))
1008  {
1009  *last_occur_date = &inst->date;
1010  }
1011 
1012  *instance_count = gnc_sx_get_instance_count(inst->parent->sx, inst->temporal_state) + 1;
1013 
1014  if (*remain_occur_count > 0)
1015  {
1016  *remain_occur_count -= 1;
1017  }
1018 }
1019 
1020 static gboolean
1021 _get_template_split_account(const SchedXaction* sx,
1022  const Split *template_split,
1023  Account **split_acct,
1024  GList **creation_errors)
1025 {
1026  gboolean success = TRUE;
1027  GncGUID *acct_guid = NULL;
1028  qof_instance_get (QOF_INSTANCE (template_split),
1029  "sx-account", &acct_guid,
1030  NULL);
1031  *split_acct = xaccAccountLookup(acct_guid, gnc_get_current_book());
1032  if (!*split_acct && sx && creation_errors)
1033  {
1034  char guid_str[GUID_ENCODING_LENGTH+1];
1035 /* Translators: A list of error messages from the Scheduled Transactions (SX).
1036  They might appear in their editor or in "Since last run". */
1037  gchar* err = N_("Unknown account for guid [%s], cancelling SX [%s] creation.");
1038  guid_to_string_buff((const GncGUID*)acct_guid, guid_str);
1039  REPORT_ERROR(creation_errors, err, guid_str, xaccSchedXactionGetName(sx));
1040  success = FALSE;
1041  }
1042 
1043  guid_free (acct_guid);
1044  return success;
1045 }
1046 
1047 static void
1048 _get_sx_formula_value(const SchedXaction* sx,
1049  const Split *template_split,
1050  gnc_numeric *numeric,
1051  GList **creation_errors,
1052  const char *formula_key,
1053  const char* numeric_key,
1054  GHashTable *variable_bindings)
1055 {
1056 
1057  char *formula_str = NULL, *parseErrorLoc = NULL;
1058  gnc_numeric *numeric_val = NULL;
1059  qof_instance_get (QOF_INSTANCE (template_split),
1060  formula_key, &formula_str,
1061  numeric_key, &numeric_val,
1062  NULL);
1063 
1064  if ((variable_bindings == NULL ||
1065  g_hash_table_size (variable_bindings) == 0) &&
1066  numeric_val != NULL &&
1067  gnc_numeric_check(*numeric_val) == GNC_ERROR_OK &&
1068  !gnc_numeric_zero_p(*numeric_val))
1069  {
1070  /* If there are no variables to parse and we had a valid numeric stored
1071  * then we can skip parsing the formula, which might save some
1072  * localization problems with separators. */
1073  numeric->num = numeric_val->num;
1074  numeric->denom = numeric_val->denom;
1075  g_free (formula_str);
1076  g_free (numeric_val);
1077  return;
1078  }
1079 
1080  if (formula_str != NULL && strlen(formula_str) != 0)
1081  {
1082  GHashTable *parser_vars = NULL;
1083  if (variable_bindings)
1084  {
1085  parser_vars = gnc_sx_instance_get_variables_for_parser(variable_bindings);
1086  }
1087  if (!gnc_exp_parser_parse_separate_vars(formula_str,
1088  numeric,
1089  &parseErrorLoc,
1090  parser_vars))
1091  {
1092  gchar *err = N_("Error parsing SX [%s] key [%s]=formula [%s] at [%s]: %s.");
1093  REPORT_ERROR(creation_errors, err,
1094  xaccSchedXactionGetName(sx),
1095  formula_key,
1096  formula_str,
1097  parseErrorLoc,
1098  gnc_exp_parser_error_string());
1099  }
1100 
1101  if (parser_vars != NULL)
1102  {
1103  g_hash_table_destroy(parser_vars);
1104  }
1105  }
1106  g_free (formula_str);
1107  g_free (numeric_val);
1108 }
1109 
1110 static void
1111 _get_credit_formula_value(GncSxInstance *instance,
1112  const Split *template_split, gnc_numeric *credit_num,
1113  GList **creation_errors)
1114 {
1115  _get_sx_formula_value(instance->parent->sx, template_split, credit_num,
1116  creation_errors, "sx-credit-formula",
1117  "sx-credit-numeric", instance->variable_bindings);
1118 }
1119 
1120 static void
1121 _get_debit_formula_value(GncSxInstance *instance, const Split *template_split,
1122  gnc_numeric *debit_num, GList **creation_errors)
1123 {
1124  _get_sx_formula_value(instance->parent->sx, template_split, debit_num,
1125  creation_errors, "sx-debit-formula",
1126  "sx-debit-numeric", instance->variable_bindings);
1127 }
1128 
1129 static gnc_numeric
1130 split_apply_formulas (const Split *split, SxTxnCreationData* creation_data)
1131 {
1132  gnc_numeric credit_num = gnc_numeric_zero();
1133  gnc_numeric debit_num = gnc_numeric_zero();
1134  gnc_numeric final;
1135  gint gncn_error;
1136  SchedXaction *sx = creation_data->instance->parent->sx;
1137 
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);
1142 
1143  final = gnc_numeric_sub_fixed(debit_num, credit_num);
1144 
1145  gncn_error = gnc_numeric_check(final);
1146  if (gncn_error != GNC_ERROR_OK)
1147  {
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();
1152  }
1153  return final;
1154 }
1155 
1156 static void
1157 split_apply_exchange_rate (Split *split, GHashTable *bindings,
1158  gnc_commodity *txn_cmdty,
1159  gnc_commodity *split_cmdty, gnc_numeric *final)
1160 {
1161  gchar *exchange_rate_var_name;
1162  GncSxVariable *exchange_rate_var;
1163  gnc_numeric amt;
1164  gnc_numeric exchange_rate = gnc_numeric_create (1, 1);
1165 
1166  exchange_rate_var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
1167  exchange_rate_var =
1168  (GncSxVariable*)g_hash_table_lookup(bindings,
1169  exchange_rate_var_name);
1170 
1171  if (exchange_rate_var != NULL)
1172  {
1173  exchange_rate = exchange_rate_var->value;
1174  DEBUG("exchange_rate is %s", gnc_numeric_to_string (exchange_rate));
1175  }
1176  g_free (exchange_rate_var_name);
1177 
1178  if (!gnc_commodity_is_currency (split_cmdty))
1179  amt = gnc_numeric_div(*final, exchange_rate,
1180  gnc_commodity_get_fraction (split_cmdty),
1182  else
1183  amt = gnc_numeric_mul(*final, exchange_rate, 1000,
1185 
1186 
1187  DEBUG("amount is %s for memo split '%s'", gnc_numeric_to_string (amt),
1188  xaccSplitGetMemo (split));
1189  xaccSplitSetAmount(split, amt); /* marks split dirty */
1190 
1191 }
1192 /* If the template_txn was created from the SX Editor then it has the default
1193  * currency even if none of its splits do; if the template_txn was created from
1194  * a non-currency register then it will be requesting backwards prices. Check
1195  * that the template_txn currency is in at least one split; if it's not a
1196  * currency and one of the splits is, use that currency. If there are no
1197  * currencies at all assume that the user knew what they were doing and return
1198  * the template_transaction's commodity.
1199  *
1200  * Since we're going through the split commodities anyway, check that they all
1201  * have usable values. If we find an error return NULL as a signal to
1202  * create_each_transaction_helper to bail out.
1203  */
1204 
1205 static gnc_commodity*
1206 get_transaction_currency(SxTxnCreationData *creation_data,
1207  SchedXaction *sx, Transaction *template_txn)
1208 {
1209  gnc_commodity *first_currency = NULL, *first_cmdty = NULL;
1210  gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
1211  gnc_commodity *txn_cmdty = xaccTransGetCurrency (template_txn);
1212  GList* txn_splits = xaccTransGetSplitList (template_txn);
1213  GList** creation_errors =
1214  creation_data ? creation_data->creation_errors : NULL;
1215 
1216  if (txn_cmdty)
1217  DEBUG("Template txn currency is %s.",
1218  gnc_commodity_get_mnemonic (txn_cmdty));
1219  else
1220  DEBUG("No template txn currency.");
1221 
1222  for (;txn_splits; txn_splits = txn_splits->next)
1223  {
1224  Split* t_split = (Split*)txn_splits->data;
1225  Account* split_account = NULL;
1226  gnc_commodity *split_cmdty = NULL;
1227 
1228  if (!_get_template_split_account(sx, t_split, &split_account,
1229  creation_errors))
1230  {
1231  err_flag = TRUE;
1232  break;
1233  }
1234  /* Don't consider the commodity of a transaction that has
1235  * neither a credit nor a debit formula. */
1236 
1237  if (split_is_marker(t_split))
1238  continue;
1239 
1240  split_cmdty = xaccAccountGetCommodity (split_account);
1241  if (!txn_cmdty)
1242  txn_cmdty = split_cmdty;
1243  if (!first_cmdty)
1244  first_cmdty = split_cmdty;
1245  if (gnc_commodity_equal (split_cmdty, txn_cmdty))
1246  txn_cmdty_in_splits = TRUE;
1247  if (!first_currency && gnc_commodity_is_currency (split_cmdty))
1248  first_currency = split_cmdty;
1249  }
1250  if (err_flag)
1251  {
1252  g_critical("Error in SX transaction [%s], split missing account: "
1253  "Creation aborted.", xaccSchedXactionGetName(sx));
1254  return NULL;
1255  }
1256  if (first_currency &&
1257  (!txn_cmdty_in_splits || !gnc_commodity_is_currency (txn_cmdty)))
1258  return first_currency;
1259  if (!txn_cmdty_in_splits)
1260  return first_cmdty;
1261  return txn_cmdty;
1262 }
1263 
1264 static gboolean
1265 create_each_transaction_helper(Transaction *template_txn, void *user_data)
1266 {
1267  Transaction *new_txn;
1268  GList *txn_splits, *template_splits, *node;
1269  Split *copying_split;
1270  SxTxnCreationData *creation_data = (SxTxnCreationData*)user_data;
1271  SchedXaction *sx = creation_data->instance->parent->sx;
1272  gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
1273  sx, template_txn);
1274 
1275  /* No txn_cmdty means there was a defective split. Bail. */
1276  if (txn_cmdty == NULL)
1277  return FALSE;
1278 
1279  /* FIXME: In general, this should [correctly] deal with errors such
1280  as not finding the appropriate Accounts and not being able to
1281  parse the formula|credit/debit strings. */
1282 
1283  new_txn = xaccTransCloneNoKvp(template_txn);
1284  xaccTransBeginEdit(new_txn);
1285 
1286  DEBUG("creating template txn desc [%s] for sx [%s]",
1287  xaccTransGetDescription(new_txn),
1288  xaccSchedXactionGetName(sx));
1289 
1290 
1291  /* Bug#500427: copy the notes, if any */
1292  if (xaccTransGetNotes(template_txn) != NULL)
1293  xaccTransSetNotes (new_txn, xaccTransGetNotes (template_txn));
1294 
1295  xaccTransSetDate(new_txn,
1296  g_date_get_day(&creation_data->instance->date),
1297  g_date_get_month(&creation_data->instance->date),
1298  g_date_get_year(&creation_data->instance->date));
1299 
1300  xaccTransSetDateEnteredSecs(new_txn, gnc_time(NULL));
1301  template_splits = xaccTransGetSplitList(template_txn);
1302  txn_splits = xaccTransGetSplitList(new_txn);
1303  if ((template_splits == NULL) || (txn_splits == NULL))
1304  {
1305  g_critical("transaction w/o splits for sx [%s]",
1306  xaccSchedXactionGetName(sx));
1307  xaccTransDestroy(new_txn);
1308  xaccTransCommitEdit(new_txn);
1309  return FALSE;
1310  }
1311 
1312  if (txn_cmdty == NULL)
1313  {
1314  xaccTransDestroy(new_txn);
1315  xaccTransCommitEdit(new_txn);
1316  return FALSE;
1317  }
1318  xaccTransSetCurrency(new_txn, txn_cmdty);
1319 
1320  for (;
1321  txn_splits && template_splits;
1322  txn_splits = txn_splits->next, template_splits = template_splits->next)
1323  {
1324  const Split *template_split;
1325  Account *split_acct;
1326  gnc_commodity *split_cmdty = NULL;
1327 
1328  /* FIXME: Ick. This assumes that the split lists will be ordered
1329  identically. :( They are, but we'd rather not have to count on
1330  it. --jsled */
1331  template_split = (Split*)template_splits->data;
1332  copying_split = (Split*)txn_splits->data;
1333 
1334  _get_template_split_account(sx, template_split, &split_acct,
1335  creation_data->creation_errors);
1336 
1337  split_cmdty = xaccAccountGetCommodity(split_acct);
1338  xaccSplitSetAccount(copying_split, split_acct);
1339 
1340  {
1341  gnc_numeric final = split_apply_formulas(template_split,
1342  creation_data);
1343  xaccSplitSetValue(copying_split, final);
1344  DEBUG("value is %s for memo split '%s'",
1345  gnc_numeric_to_string (final),
1346  xaccSplitGetMemo (copying_split));
1347  if (! gnc_commodity_equal(split_cmdty, txn_cmdty))
1348  {
1349  split_apply_exchange_rate(copying_split,
1350  creation_data->instance->variable_bindings,
1351  txn_cmdty, split_cmdty, &final);
1352  }
1353 
1354  xaccSplitScrub(copying_split);
1355  }
1356  }
1357 
1358 
1359  {
1360  qof_instance_set (QOF_INSTANCE (new_txn),
1361  "from-sched-xaction",
1362  xaccSchedXactionGetGUID(creation_data->instance->parent->sx),
1363  NULL);
1364  }
1365 
1366  xaccTransCommitEdit(new_txn);
1367 
1368  if (creation_data->created_txn_guids != NULL)
1369  {
1370  *creation_data->created_txn_guids
1371  = g_list_append(*(creation_data->created_txn_guids),
1372  (gpointer)xaccTransGetGUID(new_txn));
1373  }
1374 
1375  return FALSE;
1376 }
1377 
1378 static void
1379 create_transactions_for_instance(GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
1380 {
1381  SxTxnCreationData creation_data;
1382  Account *sx_template_account;
1383 
1384  sx_template_account = gnc_sx_get_template_transaction_account(instance->parent->sx);
1385 
1386  creation_data.instance = instance;
1387  creation_data.created_txn_guids = created_txn_guids;
1388  creation_data.creation_errors = creation_errors;
1389  /* Don't update the GUI for every transaction, it can really slow things
1390  * down.
1391  */
1393  xaccAccountForEachTransaction(sx_template_account,
1394  create_each_transaction_helper,
1395  &creation_data);
1396  qof_event_resume();
1397 }
1398 
1399 void
1401  gboolean auto_create_only,
1402  GList **created_transaction_guids,
1403  GList **creation_errors)
1404 {
1405  GList *iter;
1406 
1407  if (qof_book_is_readonly(gnc_get_current_book()))
1408  {
1409  /* Is the book read-only? Then don't change anything here. */
1410  return;
1411  }
1412 
1413  for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
1414  {
1415  GList *instance_iter;
1416  GncSxInstances *instances = (GncSxInstances*)iter->data;
1417  GDate *last_occur_date;
1418  gint instance_count = 0;
1419  gint remain_occur_count = 0;
1420 
1421  // If there are no instances, then skip; specifically, skip
1422  // re-setting SchedXaction fields, which will dirty the book
1423  // spuriously.
1424  if (g_list_length(instances->instance_list) == 0)
1425  continue;
1426 
1427  last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
1428  instance_count = gnc_sx_get_instance_count(instances->sx, NULL);
1429  remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
1430 
1431  for (instance_iter = instances->instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
1432  {
1433  GncSxInstance *inst = (GncSxInstance*)instance_iter->data;
1434  gboolean sx_is_auto_create;
1435  GList *instance_errors = NULL;
1436 
1437  xaccSchedXactionGetAutoCreate(inst->parent->sx, &sx_is_auto_create, NULL);
1438  if (auto_create_only && !sx_is_auto_create)
1439  {
1440  if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
1441  {
1442  break;
1443  }
1444  continue;
1445  }
1446 
1447  if (inst->orig_state == SX_INSTANCE_STATE_POSTPONED
1448  && inst->state != SX_INSTANCE_STATE_POSTPONED)
1449  {
1450  // remove from postponed list
1451  g_assert(inst->temporal_state != NULL);
1453  inst->temporal_state);
1454  }
1455 
1456  switch (inst->state)
1457  {
1458  case SX_INSTANCE_STATE_CREATED:
1459  // nop: we've already processed this.
1460  break;
1461  case SX_INSTANCE_STATE_IGNORED:
1462  increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1463  break;
1464  case SX_INSTANCE_STATE_POSTPONED:
1465  if (inst->orig_state != SX_INSTANCE_STATE_POSTPONED)
1466  {
1467  gnc_sx_add_defer_instance(instances->sx,
1469  }
1470  increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1471  break;
1472  case SX_INSTANCE_STATE_TO_CREATE:
1473  create_transactions_for_instance (inst,
1474  created_transaction_guids,
1475  &instance_errors);
1476  if (instance_errors == NULL)
1477  {
1478  increment_sx_state (inst, &last_occur_date,
1479  &instance_count,
1480  &remain_occur_count);
1482  (model, inst, SX_INSTANCE_STATE_CREATED);
1483  }
1484  else
1485  *creation_errors = g_list_concat (*creation_errors,
1486  instance_errors);
1487  break;
1488  case SX_INSTANCE_STATE_REMINDER:
1489  // do nothing
1490  // assert no non-remind instances after this?
1491  break;
1492  default:
1493  g_assert_not_reached();
1494  break;
1495  }
1496  }
1497 
1498  xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
1499  gnc_sx_set_instance_count(instances->sx, instance_count);
1500  xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
1501  }
1502 }
1503 
1504 void
1506  GncSxInstance *instance,
1507  GncSxInstanceState new_state)
1508 {
1509  if (instance->state == new_state)
1510  return;
1511 
1512  instance->state = new_state;
1513 
1514  // ensure 'remind' constraints are met:
1515  {
1516  GList *inst_iter;
1517  inst_iter = g_list_find(instance->parent->instance_list, instance);
1518  g_assert(inst_iter != NULL);
1519  if (instance->state != SX_INSTANCE_STATE_REMINDER)
1520  {
1521  // iterate backwards, making sure reminders are changed to 'postponed'
1522  for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
1523  {
1524  GncSxInstance *prev_inst = (GncSxInstance*)inst_iter->data;
1525  if (prev_inst->state != SX_INSTANCE_STATE_REMINDER)
1526  continue;
1527  prev_inst->state = SX_INSTANCE_STATE_POSTPONED;
1528  }
1529  }
1530  else
1531  {
1532  // iterate forward, make sure transactions are set to 'remind'
1533  for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
1534  {
1535  GncSxInstance *next_inst = (GncSxInstance*)inst_iter->data;
1536  if (next_inst->state == SX_INSTANCE_STATE_REMINDER)
1537  continue;
1538  next_inst->state = SX_INSTANCE_STATE_REMINDER;
1539  }
1540  }
1541  }
1542 
1543  g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
1544 }
1545 
1546 void
1547 gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
1548  GncSxInstance *instance,
1549  GncSxVariable *variable,
1550  gnc_numeric *new_value)
1551 {
1552 
1553  if (gnc_numeric_equal(variable->value, *new_value))
1554  return;
1555  variable->value = *new_value;
1556  g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
1557 }
1558 
1559 static void
1560 _list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
1561 {
1562  *result_list = g_list_prepend (*result_list, value);
1563 }
1564 
1565 GList*
1567 {
1568  GList *rtn = NULL;
1569  GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
1570 
1571  for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1572  {
1573  GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
1574  for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1575  {
1576  GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
1577 
1578  if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
1579  continue;
1580 
1581  g_hash_table_foreach(inst->variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
1582  for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
1583  {
1584  GncSxVariable *var = (GncSxVariable*)var_iter->data;
1585  if (gnc_numeric_check(var->value) != GNC_ERROR_OK)
1586  {
1587  GncSxVariableNeeded *need = g_new0(GncSxVariableNeeded, 1);
1588  need->instance = inst;
1589  need->variable = var;
1590  rtn = g_list_prepend (rtn, need);
1591  }
1592  }
1593  g_list_free(var_list);
1594  var_list = NULL;
1595  }
1596  }
1597  return rtn;
1598 }
1599 
1600 void
1602 {
1603  GList *sx_iter, *inst_iter;
1604 
1605  g_return_if_fail(model != NULL);
1606  g_return_if_fail(summary != NULL);
1607 
1608  summary->need_dialog = FALSE;
1609  summary->num_instances = 0;
1610  summary->num_to_create_instances = 0;
1611  summary->num_auto_create_instances = 0;
1613 
1614  for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1615  {
1616  GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
1617  gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
1618  xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
1619  for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1620  {
1621  GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
1622  summary->num_instances++;
1623 
1624  if (inst->state == SX_INSTANCE_STATE_TO_CREATE)
1625  {
1626  if (sx_is_auto_create)
1627  {
1628  if (!sx_notify)
1629  {
1631  }
1632  else
1633  {
1634  summary->num_auto_create_instances++;
1635  }
1636  }
1637  else
1638  {
1639  summary->num_to_create_instances++;
1640  }
1641  }
1642  }
1643  }
1644 
1645  // if all the instances are 'auto-create, no-notify', then we don't need
1646  // the dialog.
1647  summary->need_dialog
1648  = (summary->num_instances != 0
1649  && summary->num_auto_create_no_notify_instances != summary->num_instances);
1650 }
1651 
1652 void
1654 {
1655  PINFO("num_instances: %d", summary->num_instances);
1656  PINFO("num_to_create: %d", summary->num_to_create_instances);
1657  PINFO("num_auto_create_instances: %d", summary->num_auto_create_instances);
1658  PINFO("num_auto_create_no_notify_instances: %d", summary->num_auto_create_no_notify_instances);
1659  PINFO("need dialog? %s", summary->need_dialog ? "true" : "false");
1660 }
1661 
1662 static void gnc_numeric_free(gpointer data)
1663 {
1664  gnc_numeric *p = (gnc_numeric*) data;
1665  g_free(p);
1666 }
1667 
1669 {
1670  return g_hash_table_new_full (guid_hash_to_guint, guid_g_hash_table_equal,
1671  NULL, gnc_numeric_free);
1672 }
1673 
1674 typedef struct
1675 {
1676  GHashTable *hash;
1677  GList **creation_errors;
1678  const SchedXaction *sx;
1679  gnc_numeric count;
1680 } SxCashflowData;
1681 
1682 static void add_to_hash_amount(GHashTable* hash, const GncGUID* guid, const gnc_numeric* amount)
1683 {
1684  /* Do we have a number belonging to this GUID in the hash? If yes,
1685  * modify it in-place; if not, insert the new element into the
1686  * hash. */
1687  gnc_numeric* elem = g_hash_table_lookup(hash, guid);
1688  gchar guidstr[GUID_ENCODING_LENGTH+1];
1689  guid_to_string_buff(guid, guidstr);
1690  if (!elem)
1691  {
1692  elem = g_new0(gnc_numeric, 1);
1693  *elem = gnc_numeric_zero();
1694  g_hash_table_insert(hash, (gpointer) guid, elem);
1695  }
1696 
1697  /* Check input arguments for sanity */
1698  if (gnc_numeric_check(*amount) != GNC_ERROR_OK)
1699  {
1700  g_critical("Oops, the given amount [%s] has the error code %d, at guid [%s].",
1701  gnc_num_dbg_to_string(*amount),
1702  gnc_numeric_check(*amount),
1703  guidstr);
1704  return;
1705  }
1706  if (gnc_numeric_check(*elem) != GNC_ERROR_OK)
1707  {
1708  g_critical("Oops, the account's amount [%s] has the error code %d, at guid [%s].",
1709  gnc_num_dbg_to_string(*elem),
1710  gnc_numeric_check(*elem),
1711  guidstr);
1712  return;
1713  }
1714 
1715  /* Watch out - don't use gnc_numeric_add_fixed here because it
1716  * will refuse to add 1/5+1/10; instead, we have to use the flags
1717  * as given here explicitly. Eventually, add the given amount to
1718  * the entry in the hash. */
1719  *elem = gnc_numeric_add(*elem, *amount,
1722 
1723  /* Check for sanity of the output. */
1724  if (gnc_numeric_check(*elem) != GNC_ERROR_OK)
1725  {
1726  g_critical("Oops, after addition at guid [%s] the resulting amount [%s] has the error code %d; added amount = [%s].",
1727  guidstr,
1728  gnc_num_dbg_to_string(*elem),
1729  gnc_numeric_check(*elem),
1730  gnc_num_dbg_to_string(*amount));
1731  return;
1732  }
1733 
1734  /* In case anyone wants to see this in the debug log. */
1735  DEBUG("Adding to guid [%s] the value [%s]. Value now [%s].",
1736  guidstr,
1737  gnc_num_dbg_to_string(*amount),
1738  gnc_num_dbg_to_string(*elem));
1739 }
1740 
1741 static gboolean
1742 create_cashflow_helper(Transaction *template_txn, void *user_data)
1743 {
1744  SxCashflowData *creation_data = user_data;
1745  GList *template_splits;
1746  const gnc_commodity *first_cmdty = NULL;
1747 
1748  DEBUG("Evaluating txn desc [%s] for sx [%s]",
1749  xaccTransGetDescription(template_txn),
1750  xaccSchedXactionGetName(creation_data->sx));
1751 
1752  template_splits = xaccTransGetSplitList(template_txn);
1753 
1754  if (template_splits == NULL)
1755  {
1756  g_critical("transaction w/o splits for sx [%s]",
1757  xaccSchedXactionGetName(creation_data->sx));
1758  return FALSE;
1759  }
1760 
1761  for (;
1762  template_splits;
1763  template_splits = template_splits->next)
1764  {
1765  Account *split_acct;
1766  const gnc_commodity *split_cmdty = NULL;
1767  const Split *template_split = (const Split*) template_splits->data;
1768 
1769  /* Get the account that should be used for this split. */
1770  if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors))
1771  {
1772  DEBUG("Could not find account for split");
1773  break;
1774  }
1775 
1776  /* The split's account also has some commodity */
1777  split_cmdty = xaccAccountGetCommodity(split_acct);
1778  if (first_cmdty == NULL)
1779  {
1780  first_cmdty = split_cmdty;
1781  //xaccTransSetCurrency(new_txn, first_cmdty);
1782  }
1783 
1784  {
1785  gnc_numeric credit_num = gnc_numeric_zero();
1786  gnc_numeric debit_num = gnc_numeric_zero();
1787  gnc_numeric final_once, final;
1788  gint gncn_error;
1789 
1790  /* Credit value */
1791  _get_sx_formula_value(creation_data->sx, template_split,
1792  &credit_num, creation_data->creation_errors,
1793  "sx-credit-formula", "sx-credit-numeric",
1794  NULL);
1795  /* Debit value */
1796  _get_sx_formula_value(creation_data->sx, template_split,
1797  &debit_num, creation_data->creation_errors,
1798  "sx-debit-formula", "sx-debit-numeric", NULL);
1799 
1800  /* The resulting cash flow number: debit minus credit,
1801  * multiplied with the count factor. */
1802  final_once = gnc_numeric_sub_fixed( debit_num, credit_num );
1803  /* Multiply with the count factor. */
1804  final = gnc_numeric_mul(final_once, creation_data->count,
1805  gnc_numeric_denom(final_once),
1807 
1808  gncn_error = gnc_numeric_check(final);
1809  if (gncn_error != GNC_ERROR_OK)
1810  {
1811  gchar* err = N_("Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1812  REPORT_ERROR(creation_data->creation_errors, err,
1813  gncn_error, xaccSchedXactionGetName(creation_data->sx));
1814  final = gnc_numeric_zero();
1815  }
1816 
1817  /* Print error message if we would have needed an exchange rate */
1818  if (! gnc_commodity_equal(split_cmdty, first_cmdty))
1819  {
1820  gchar *err = N_("No exchange rate available in SX [%s] for %s -> %s, value is zero.");
1821  REPORT_ERROR(creation_data->creation_errors, err,
1822  xaccSchedXactionGetName(creation_data->sx),
1823  gnc_commodity_get_mnemonic(split_cmdty),
1824  gnc_commodity_get_mnemonic(first_cmdty));
1825  final = gnc_numeric_zero();
1826  }
1827 
1828  /* And add the resulting value to the hash */
1829  add_to_hash_amount(creation_data->hash, xaccAccountGetGUID(split_acct), &final);
1830  }
1831  }
1832 
1833  return FALSE;
1834 }
1835 
1836 static void
1837 instantiate_cashflow_internal(const SchedXaction* sx,
1838  GHashTable* map,
1839  GList **creation_errors, gint count)
1840 {
1841  SxCashflowData create_cashflow_data;
1842  Account* sx_template_account = gnc_sx_get_template_transaction_account(sx);
1843 
1844  if (!sx_template_account)
1845  {
1846  g_critical("Huh? No template account for the SX %s", xaccSchedXactionGetName(sx));
1847  return;
1848  }
1849 
1850  if (!xaccSchedXactionGetEnabled(sx))
1851  {
1852  DEBUG("Skipping non-enabled SX [%s]",
1853  xaccSchedXactionGetName(sx));
1854  return;
1855  }
1856 
1857  create_cashflow_data.hash = map;
1858  create_cashflow_data.creation_errors = creation_errors;
1859  create_cashflow_data.sx = sx;
1860  create_cashflow_data.count = gnc_numeric_create(count, 1);
1861 
1862  /* The cash flow numbers are in the transactions of the template
1863  * account, so run this foreach on the transactions. */
1864  xaccAccountForEachTransaction(sx_template_account,
1865  create_cashflow_helper,
1866  &create_cashflow_data);
1867 }
1868 
1869 typedef struct
1870 {
1871  GHashTable *hash;
1872  GList **creation_errors;
1873  const GDate *range_start;
1874  const GDate *range_end;
1875 } SxAllCashflow;
1876 
1877 static void instantiate_cashflow_cb(gpointer data, gpointer _user_data)
1878 {
1879  const SchedXaction* sx = (const SchedXaction*) data;
1880  SxAllCashflow* userdata = (SxAllCashflow*) _user_data;
1881  gint count;
1882 
1883  g_assert(sx);
1884  g_assert(userdata);
1885 
1886  /* How often does this particular SX occur in the date range? */
1887  count = gnc_sx_get_num_occur_daterange(sx, userdata->range_start,
1888  userdata->range_end);
1889  if (count > 0)
1890  {
1891  /* If it occurs at least once, calculate ("instantiate") its
1892  * cash flow and add it to the result
1893  * g_hash<GUID,gnc_numeric> */
1894  instantiate_cashflow_internal(sx,
1895  userdata->hash,
1896  userdata->creation_errors,
1897  count);
1898  }
1899 }
1900 
1901 void gnc_sx_all_instantiate_cashflow(GList *all_sxes,
1902  const GDate *range_start, const GDate *range_end,
1903  GHashTable* map, GList **creation_errors)
1904 {
1905  SxAllCashflow userdata;
1906  userdata.hash = map;
1907  userdata.creation_errors = creation_errors;
1908  userdata.range_start = range_start;
1909  userdata.range_end = range_end;
1910 
1911  /* The work is done in the callback for each SX */
1912  g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
1913 }
1914 
1915 
1916 GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_end)
1917 {
1918  GHashTable *result_map = gnc_g_hash_new_guid_numeric();
1919  GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
1921  &range_start, &range_end,
1922  result_map, NULL);
1923  return result_map;
1924 }
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:92
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
Definition: gnc-numeric.h:196
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...
Definition: Account.cpp:5437
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.
Definition: SchedXaction.c:988
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&#39;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.
Definition: qoflog.h:256
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.
Definition: qoflog.h:264
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.
Definition: SX-book.c:65
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...
Definition: guid.cpp:174
Just the variable temporal bits from the SX structure.
Definition: SchedXaction.h:127
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
Definition: guid.cpp:228
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...
Definition: gnc-numeric.h:166
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.
Definition: SchedXaction.c:926
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.
Definition: Transaction.c:1426
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
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...
Definition: Account.cpp:3083
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
#define xaccAccountGetGUID(X)
Definition: Account.h:248
convert single-entry accounts to clean double-entry
Definition: finvar.h:98
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account&#39;s commodity that the split should have...
Definition: gmock-Split.cpp:77
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.
Definition: qofevent.h:45
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.
Definition: guid.h:84
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.
Definition: Scrub.c:235
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 ...
Definition: Transaction.c:645
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
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)...
Definition: SchedXaction.c:779
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Argument is not a valid number.
Definition: gnc-numeric.h:225
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.
Definition: Transaction.c:2080
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)
Definition: SchedXaction.h:323
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)
GLib helper routines.
#define xaccTransGetGUID(X)
Definition: Transaction.h:793
Never round at all, and signal an error if there is a fractional result in a computation.
Definition: gnc-numeric.h:178
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:506
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.
Definition: qofevent.cpp:145
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1247
gnc_numeric value
only numeric values are supported.
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3448
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
Definition: Transaction.c:1367
void qof_event_resume(void)
Resume engine event generation.
Definition: qofevent.cpp:156
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
Definition: gnc-date.cpp:273
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
Definition: Transaction.c:2054
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.
Definition: gnc-event.h:45
No error.
Definition: gnc-numeric.h:224
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:246
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.
Definition: guid.h:75
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.
Definition: guid.cpp:248
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...
Definition: Account.cpp:2050
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.
Definition: SchedXaction.c:969