GnuCash  4.11-243-g1cac132214+
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  *fallback_cmdty = NULL;
1211  gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
1212  gnc_commodity *txn_cmdty = xaccTransGetCurrency (template_txn);
1213  GList* txn_splits = xaccTransGetSplitList (template_txn);
1214  GList** creation_errors =
1215  creation_data ? creation_data->creation_errors : NULL;
1216 
1217  if (txn_cmdty)
1218  DEBUG("Template txn currency is %s.",
1219  gnc_commodity_get_mnemonic (txn_cmdty));
1220  else
1221  DEBUG("No template txn currency.");
1222 
1223  for (;txn_splits; txn_splits = txn_splits->next)
1224  {
1225  Split* t_split = (Split*)txn_splits->data;
1226  Account* split_account = NULL;
1227  gnc_commodity *split_cmdty = NULL;
1228 
1229  if (!_get_template_split_account(sx, t_split, &split_account,
1230  creation_errors))
1231  {
1232  err_flag = TRUE;
1233  break;
1234  }
1235  /* Don't consider the commodity of a transaction that has
1236  * neither a credit nor a debit formula. */
1237 
1238  if (!fallback_cmdty)
1239  fallback_cmdty = xaccAccountGetCommodity (split_account);
1240 
1241  if (split_is_marker(t_split))
1242  continue;
1243 
1244  split_cmdty = xaccAccountGetCommodity (split_account);
1245  if (!txn_cmdty)
1246  txn_cmdty = split_cmdty;
1247  if (!first_cmdty)
1248  first_cmdty = split_cmdty;
1249  if (gnc_commodity_equal (split_cmdty, txn_cmdty))
1250  txn_cmdty_in_splits = TRUE;
1251  if (!first_currency && gnc_commodity_is_currency (split_cmdty))
1252  first_currency = split_cmdty;
1253  }
1254  if (err_flag)
1255  {
1256  g_critical("Error in SX transaction [%s], split missing account: "
1257  "Creation aborted.", xaccSchedXactionGetName(sx));
1258  return NULL;
1259  }
1260  if (first_currency &&
1261  (!txn_cmdty_in_splits || !gnc_commodity_is_currency (txn_cmdty)))
1262  return first_currency;
1263  if (!txn_cmdty_in_splits && first_cmdty)
1264  return first_cmdty;
1265  if (txn_cmdty)
1266  return txn_cmdty;
1267  return fallback_cmdty;
1268 }
1269 
1270 static gboolean
1271 create_each_transaction_helper(Transaction *template_txn, void *user_data)
1272 {
1273  Transaction *new_txn;
1274  GList *txn_splits, *template_splits, *node;
1275  Split *copying_split;
1276  SxTxnCreationData *creation_data = (SxTxnCreationData*)user_data;
1277  SchedXaction *sx = creation_data->instance->parent->sx;
1278  gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
1279  sx, template_txn);
1280 
1281  /* No txn_cmdty means there was a defective split. Bail. */
1282  if (txn_cmdty == NULL)
1283  return FALSE;
1284 
1285  /* FIXME: In general, this should [correctly] deal with errors such
1286  as not finding the appropriate Accounts and not being able to
1287  parse the formula|credit/debit strings. */
1288 
1289  new_txn = xaccTransCloneNoKvp(template_txn);
1290  xaccTransBeginEdit(new_txn);
1291 
1292  DEBUG("creating template txn desc [%s] for sx [%s]",
1293  xaccTransGetDescription(new_txn),
1294  xaccSchedXactionGetName(sx));
1295 
1296 
1297  /* Bug#500427: copy the notes, if any */
1298  if (xaccTransGetNotes(template_txn) != NULL)
1299  xaccTransSetNotes (new_txn, xaccTransGetNotes (template_txn));
1300 
1301  xaccTransSetDate(new_txn,
1302  g_date_get_day(&creation_data->instance->date),
1303  g_date_get_month(&creation_data->instance->date),
1304  g_date_get_year(&creation_data->instance->date));
1305 
1306  xaccTransSetDateEnteredSecs(new_txn, gnc_time(NULL));
1307  template_splits = xaccTransGetSplitList(template_txn);
1308  txn_splits = xaccTransGetSplitList(new_txn);
1309  if ((template_splits == NULL) || (txn_splits == NULL))
1310  {
1311  g_critical("transaction w/o splits for sx [%s]",
1312  xaccSchedXactionGetName(sx));
1313  xaccTransDestroy(new_txn);
1314  xaccTransCommitEdit(new_txn);
1315  return FALSE;
1316  }
1317 
1318  if (txn_cmdty == NULL)
1319  {
1320  xaccTransDestroy(new_txn);
1321  xaccTransCommitEdit(new_txn);
1322  return FALSE;
1323  }
1324  xaccTransSetCurrency(new_txn, txn_cmdty);
1325 
1326  for (;
1327  txn_splits && template_splits;
1328  txn_splits = txn_splits->next, template_splits = template_splits->next)
1329  {
1330  const Split *template_split;
1331  Account *split_acct;
1332  gnc_commodity *split_cmdty = NULL;
1333 
1334  /* FIXME: Ick. This assumes that the split lists will be ordered
1335  identically. :( They are, but we'd rather not have to count on
1336  it. --jsled */
1337  template_split = (Split*)template_splits->data;
1338  copying_split = (Split*)txn_splits->data;
1339 
1340  _get_template_split_account(sx, template_split, &split_acct,
1341  creation_data->creation_errors);
1342 
1343  split_cmdty = xaccAccountGetCommodity(split_acct);
1344  xaccSplitSetAccount(copying_split, split_acct);
1345 
1346  {
1347  gnc_numeric final = split_apply_formulas(template_split,
1348  creation_data);
1349  xaccSplitSetValue(copying_split, final);
1350  DEBUG("value is %s for memo split '%s'",
1351  gnc_numeric_to_string (final),
1352  xaccSplitGetMemo (copying_split));
1353  if (! gnc_commodity_equal(split_cmdty, txn_cmdty))
1354  {
1355  split_apply_exchange_rate(copying_split,
1356  creation_data->instance->variable_bindings,
1357  txn_cmdty, split_cmdty, &final);
1358  }
1359 
1360  xaccSplitScrub(copying_split);
1361  }
1362  }
1363 
1364 
1365  {
1366  qof_instance_set (QOF_INSTANCE (new_txn),
1367  "from-sched-xaction",
1368  xaccSchedXactionGetGUID(creation_data->instance->parent->sx),
1369  NULL);
1370  }
1371 
1372  xaccTransCommitEdit(new_txn);
1373 
1374  if (creation_data->created_txn_guids != NULL)
1375  {
1376  *creation_data->created_txn_guids
1377  = g_list_append(*(creation_data->created_txn_guids),
1378  (gpointer)xaccTransGetGUID(new_txn));
1379  }
1380 
1381  return FALSE;
1382 }
1383 
1384 static void
1385 create_transactions_for_instance(GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
1386 {
1387  SxTxnCreationData creation_data;
1388  Account *sx_template_account;
1389 
1390  sx_template_account = gnc_sx_get_template_transaction_account(instance->parent->sx);
1391 
1392  creation_data.instance = instance;
1393  creation_data.created_txn_guids = created_txn_guids;
1394  creation_data.creation_errors = creation_errors;
1395  /* Don't update the GUI for every transaction, it can really slow things
1396  * down.
1397  */
1399  xaccAccountForEachTransaction(sx_template_account,
1400  create_each_transaction_helper,
1401  &creation_data);
1402  qof_event_resume();
1403 }
1404 
1405 void
1407  gboolean auto_create_only,
1408  GList **created_transaction_guids,
1409  GList **creation_errors)
1410 {
1411  GList *iter;
1412 
1413  if (qof_book_is_readonly(gnc_get_current_book()))
1414  {
1415  /* Is the book read-only? Then don't change anything here. */
1416  return;
1417  }
1418 
1419  for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
1420  {
1421  GList *instance_iter;
1422  GncSxInstances *instances = (GncSxInstances*)iter->data;
1423  GDate *last_occur_date;
1424  gint instance_count = 0;
1425  gint remain_occur_count = 0;
1426 
1427  // If there are no instances, then skip; specifically, skip
1428  // re-setting SchedXaction fields, which will dirty the book
1429  // spuriously.
1430  if (g_list_length(instances->instance_list) == 0)
1431  continue;
1432 
1433  last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
1434  instance_count = gnc_sx_get_instance_count(instances->sx, NULL);
1435  remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
1436 
1437  for (instance_iter = instances->instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
1438  {
1439  GncSxInstance *inst = (GncSxInstance*)instance_iter->data;
1440  gboolean sx_is_auto_create;
1441  GList *instance_errors = NULL;
1442 
1443  xaccSchedXactionGetAutoCreate(inst->parent->sx, &sx_is_auto_create, NULL);
1444  if (auto_create_only && !sx_is_auto_create)
1445  {
1446  if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
1447  {
1448  break;
1449  }
1450  continue;
1451  }
1452 
1453  if (inst->orig_state == SX_INSTANCE_STATE_POSTPONED
1454  && inst->state != SX_INSTANCE_STATE_POSTPONED)
1455  {
1456  // remove from postponed list
1457  g_assert(inst->temporal_state != NULL);
1459  inst->temporal_state);
1460  }
1461 
1462  switch (inst->state)
1463  {
1464  case SX_INSTANCE_STATE_CREATED:
1465  // nop: we've already processed this.
1466  break;
1467  case SX_INSTANCE_STATE_IGNORED:
1468  increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1469  break;
1470  case SX_INSTANCE_STATE_POSTPONED:
1471  if (inst->orig_state != SX_INSTANCE_STATE_POSTPONED)
1472  {
1473  gnc_sx_add_defer_instance(instances->sx,
1475  }
1476  increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1477  break;
1478  case SX_INSTANCE_STATE_TO_CREATE:
1479  create_transactions_for_instance (inst,
1480  created_transaction_guids,
1481  &instance_errors);
1482  if (instance_errors == NULL)
1483  {
1484  increment_sx_state (inst, &last_occur_date,
1485  &instance_count,
1486  &remain_occur_count);
1488  (model, inst, SX_INSTANCE_STATE_CREATED);
1489  }
1490  else
1491  *creation_errors = g_list_concat (*creation_errors,
1492  instance_errors);
1493  break;
1494  case SX_INSTANCE_STATE_REMINDER:
1495  // do nothing
1496  // assert no non-remind instances after this?
1497  break;
1498  default:
1499  g_assert_not_reached();
1500  break;
1501  }
1502  }
1503 
1504  xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
1505  gnc_sx_set_instance_count(instances->sx, instance_count);
1506  xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
1507  }
1508 }
1509 
1510 void
1512  GncSxInstance *instance,
1513  GncSxInstanceState new_state)
1514 {
1515  if (instance->state == new_state)
1516  return;
1517 
1518  instance->state = new_state;
1519 
1520  // ensure 'remind' constraints are met:
1521  {
1522  GList *inst_iter;
1523  inst_iter = g_list_find(instance->parent->instance_list, instance);
1524  g_assert(inst_iter != NULL);
1525  if (instance->state != SX_INSTANCE_STATE_REMINDER)
1526  {
1527  // iterate backwards, making sure reminders are changed to 'postponed'
1528  for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
1529  {
1530  GncSxInstance *prev_inst = (GncSxInstance*)inst_iter->data;
1531  if (prev_inst->state != SX_INSTANCE_STATE_REMINDER)
1532  continue;
1533  prev_inst->state = SX_INSTANCE_STATE_POSTPONED;
1534  }
1535  }
1536  else
1537  {
1538  // iterate forward, make sure transactions are set to 'remind'
1539  for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
1540  {
1541  GncSxInstance *next_inst = (GncSxInstance*)inst_iter->data;
1542  if (next_inst->state == SX_INSTANCE_STATE_REMINDER)
1543  continue;
1544  next_inst->state = SX_INSTANCE_STATE_REMINDER;
1545  }
1546  }
1547  }
1548 
1549  g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
1550 }
1551 
1552 void
1553 gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
1554  GncSxInstance *instance,
1555  GncSxVariable *variable,
1556  gnc_numeric *new_value)
1557 {
1558 
1559  if (gnc_numeric_equal(variable->value, *new_value))
1560  return;
1561  variable->value = *new_value;
1562  g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
1563 }
1564 
1565 static void
1566 _list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
1567 {
1568  *result_list = g_list_prepend (*result_list, value);
1569 }
1570 
1571 GList*
1573 {
1574  GList *rtn = NULL;
1575  GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
1576 
1577  for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1578  {
1579  GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
1580  for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1581  {
1582  GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
1583 
1584  if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
1585  continue;
1586 
1587  g_hash_table_foreach(inst->variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
1588  for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
1589  {
1590  GncSxVariable *var = (GncSxVariable*)var_iter->data;
1591  if (gnc_numeric_check(var->value) != GNC_ERROR_OK)
1592  {
1593  GncSxVariableNeeded *need = g_new0(GncSxVariableNeeded, 1);
1594  need->instance = inst;
1595  need->variable = var;
1596  rtn = g_list_prepend (rtn, need);
1597  }
1598  }
1599  g_list_free(var_list);
1600  var_list = NULL;
1601  }
1602  }
1603  return rtn;
1604 }
1605 
1606 void
1608 {
1609  GList *sx_iter, *inst_iter;
1610 
1611  g_return_if_fail(model != NULL);
1612  g_return_if_fail(summary != NULL);
1613 
1614  summary->need_dialog = FALSE;
1615  summary->num_instances = 0;
1616  summary->num_to_create_instances = 0;
1617  summary->num_auto_create_instances = 0;
1619 
1620  for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1621  {
1622  GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
1623  gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
1624  xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
1625  for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1626  {
1627  GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
1628  summary->num_instances++;
1629 
1630  if (inst->state == SX_INSTANCE_STATE_TO_CREATE)
1631  {
1632  if (sx_is_auto_create)
1633  {
1634  if (!sx_notify)
1635  {
1637  }
1638  else
1639  {
1640  summary->num_auto_create_instances++;
1641  }
1642  }
1643  else
1644  {
1645  summary->num_to_create_instances++;
1646  }
1647  }
1648  }
1649  }
1650 
1651  // if all the instances are 'auto-create, no-notify', then we don't need
1652  // the dialog.
1653  summary->need_dialog
1654  = (summary->num_instances != 0
1655  && summary->num_auto_create_no_notify_instances != summary->num_instances);
1656 }
1657 
1658 void
1660 {
1661  PINFO("num_instances: %d", summary->num_instances);
1662  PINFO("num_to_create: %d", summary->num_to_create_instances);
1663  PINFO("num_auto_create_instances: %d", summary->num_auto_create_instances);
1664  PINFO("num_auto_create_no_notify_instances: %d", summary->num_auto_create_no_notify_instances);
1665  PINFO("need dialog? %s", summary->need_dialog ? "true" : "false");
1666 }
1667 
1668 static void gnc_numeric_free(gpointer data)
1669 {
1670  gnc_numeric *p = (gnc_numeric*) data;
1671  g_free(p);
1672 }
1673 
1675 {
1676  return g_hash_table_new_full (guid_hash_to_guint, guid_g_hash_table_equal,
1677  NULL, gnc_numeric_free);
1678 }
1679 
1680 typedef struct
1681 {
1682  GHashTable *hash;
1683  GList **creation_errors;
1684  const SchedXaction *sx;
1685  gnc_numeric count;
1686 } SxCashflowData;
1687 
1688 static void add_to_hash_amount(GHashTable* hash, const GncGUID* guid, const gnc_numeric* amount)
1689 {
1690  /* Do we have a number belonging to this GUID in the hash? If yes,
1691  * modify it in-place; if not, insert the new element into the
1692  * hash. */
1693  gnc_numeric* elem = g_hash_table_lookup(hash, guid);
1694  gchar guidstr[GUID_ENCODING_LENGTH+1];
1695  guid_to_string_buff(guid, guidstr);
1696  if (!elem)
1697  {
1698  elem = g_new0(gnc_numeric, 1);
1699  *elem = gnc_numeric_zero();
1700  g_hash_table_insert(hash, (gpointer) guid, elem);
1701  }
1702 
1703  /* Check input arguments for sanity */
1704  if (gnc_numeric_check(*amount) != GNC_ERROR_OK)
1705  {
1706  g_critical("Oops, the given amount [%s] has the error code %d, at guid [%s].",
1707  gnc_num_dbg_to_string(*amount),
1708  gnc_numeric_check(*amount),
1709  guidstr);
1710  return;
1711  }
1712  if (gnc_numeric_check(*elem) != GNC_ERROR_OK)
1713  {
1714  g_critical("Oops, the account's amount [%s] has the error code %d, at guid [%s].",
1715  gnc_num_dbg_to_string(*elem),
1716  gnc_numeric_check(*elem),
1717  guidstr);
1718  return;
1719  }
1720 
1721  /* Watch out - don't use gnc_numeric_add_fixed here because it
1722  * will refuse to add 1/5+1/10; instead, we have to use the flags
1723  * as given here explicitly. Eventually, add the given amount to
1724  * the entry in the hash. */
1725  *elem = gnc_numeric_add(*elem, *amount,
1728 
1729  /* Check for sanity of the output. */
1730  if (gnc_numeric_check(*elem) != GNC_ERROR_OK)
1731  {
1732  g_critical("Oops, after addition at guid [%s] the resulting amount [%s] has the error code %d; added amount = [%s].",
1733  guidstr,
1734  gnc_num_dbg_to_string(*elem),
1735  gnc_numeric_check(*elem),
1736  gnc_num_dbg_to_string(*amount));
1737  return;
1738  }
1739 
1740  /* In case anyone wants to see this in the debug log. */
1741  DEBUG("Adding to guid [%s] the value [%s]. Value now [%s].",
1742  guidstr,
1743  gnc_num_dbg_to_string(*amount),
1744  gnc_num_dbg_to_string(*elem));
1745 }
1746 
1747 static gboolean
1748 create_cashflow_helper(Transaction *template_txn, void *user_data)
1749 {
1750  SxCashflowData *creation_data = user_data;
1751  GList *template_splits;
1752  const gnc_commodity *first_cmdty = NULL;
1753 
1754  DEBUG("Evaluating txn desc [%s] for sx [%s]",
1755  xaccTransGetDescription(template_txn),
1756  xaccSchedXactionGetName(creation_data->sx));
1757 
1758  template_splits = xaccTransGetSplitList(template_txn);
1759 
1760  if (template_splits == NULL)
1761  {
1762  g_critical("transaction w/o splits for sx [%s]",
1763  xaccSchedXactionGetName(creation_data->sx));
1764  return FALSE;
1765  }
1766 
1767  for (;
1768  template_splits;
1769  template_splits = template_splits->next)
1770  {
1771  Account *split_acct;
1772  const gnc_commodity *split_cmdty = NULL;
1773  const Split *template_split = (const Split*) template_splits->data;
1774 
1775  /* Get the account that should be used for this split. */
1776  if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors))
1777  {
1778  DEBUG("Could not find account for split");
1779  break;
1780  }
1781 
1782  /* The split's account also has some commodity */
1783  split_cmdty = xaccAccountGetCommodity(split_acct);
1784  if (first_cmdty == NULL)
1785  {
1786  first_cmdty = split_cmdty;
1787  //xaccTransSetCurrency(new_txn, first_cmdty);
1788  }
1789 
1790  {
1791  gnc_numeric credit_num = gnc_numeric_zero();
1792  gnc_numeric debit_num = gnc_numeric_zero();
1793  gnc_numeric final_once, final;
1794  gint gncn_error;
1795 
1796  /* Credit value */
1797  _get_sx_formula_value(creation_data->sx, template_split,
1798  &credit_num, creation_data->creation_errors,
1799  "sx-credit-formula", "sx-credit-numeric",
1800  NULL);
1801  /* Debit value */
1802  _get_sx_formula_value(creation_data->sx, template_split,
1803  &debit_num, creation_data->creation_errors,
1804  "sx-debit-formula", "sx-debit-numeric", NULL);
1805 
1806  /* The resulting cash flow number: debit minus credit,
1807  * multiplied with the count factor. */
1808  final_once = gnc_numeric_sub_fixed( debit_num, credit_num );
1809  /* Multiply with the count factor. */
1810  final = gnc_numeric_mul(final_once, creation_data->count,
1811  gnc_numeric_denom(final_once),
1813 
1814  gncn_error = gnc_numeric_check(final);
1815  if (gncn_error != GNC_ERROR_OK)
1816  {
1817  gchar* err = N_("Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1818  REPORT_ERROR(creation_data->creation_errors, err,
1819  gncn_error, xaccSchedXactionGetName(creation_data->sx));
1820  final = gnc_numeric_zero();
1821  }
1822 
1823  /* Print error message if we would have needed an exchange rate */
1824  if (! gnc_commodity_equal(split_cmdty, first_cmdty))
1825  {
1826  gchar *err = N_("No exchange rate available in SX [%s] for %s -> %s, value is zero.");
1827  REPORT_ERROR(creation_data->creation_errors, err,
1828  xaccSchedXactionGetName(creation_data->sx),
1829  gnc_commodity_get_mnemonic(split_cmdty),
1830  gnc_commodity_get_mnemonic(first_cmdty));
1831  final = gnc_numeric_zero();
1832  }
1833 
1834  /* And add the resulting value to the hash */
1835  add_to_hash_amount(creation_data->hash, xaccAccountGetGUID(split_acct), &final);
1836  }
1837  }
1838 
1839  return FALSE;
1840 }
1841 
1842 static void
1843 instantiate_cashflow_internal(const SchedXaction* sx,
1844  GHashTable* map,
1845  GList **creation_errors, gint count)
1846 {
1847  SxCashflowData create_cashflow_data;
1848  Account* sx_template_account = gnc_sx_get_template_transaction_account(sx);
1849 
1850  if (!sx_template_account)
1851  {
1852  g_critical("Huh? No template account for the SX %s", xaccSchedXactionGetName(sx));
1853  return;
1854  }
1855 
1856  if (!xaccSchedXactionGetEnabled(sx))
1857  {
1858  DEBUG("Skipping non-enabled SX [%s]",
1859  xaccSchedXactionGetName(sx));
1860  return;
1861  }
1862 
1863  create_cashflow_data.hash = map;
1864  create_cashflow_data.creation_errors = creation_errors;
1865  create_cashflow_data.sx = sx;
1866  create_cashflow_data.count = gnc_numeric_create(count, 1);
1867 
1868  /* The cash flow numbers are in the transactions of the template
1869  * account, so run this foreach on the transactions. */
1870  xaccAccountForEachTransaction(sx_template_account,
1871  create_cashflow_helper,
1872  &create_cashflow_data);
1873 }
1874 
1875 typedef struct
1876 {
1877  GHashTable *hash;
1878  GList **creation_errors;
1879  const GDate *range_start;
1880  const GDate *range_end;
1881 } SxAllCashflow;
1882 
1883 static void instantiate_cashflow_cb(gpointer data, gpointer _user_data)
1884 {
1885  const SchedXaction* sx = (const SchedXaction*) data;
1886  SxAllCashflow* userdata = (SxAllCashflow*) _user_data;
1887  gint count;
1888 
1889  g_assert(sx);
1890  g_assert(userdata);
1891 
1892  /* How often does this particular SX occur in the date range? */
1893  count = gnc_sx_get_num_occur_daterange(sx, userdata->range_start,
1894  userdata->range_end);
1895  if (count > 0)
1896  {
1897  /* If it occurs at least once, calculate ("instantiate") its
1898  * cash flow and add it to the result
1899  * g_hash<GUID,gnc_numeric> */
1900  instantiate_cashflow_internal(sx,
1901  userdata->hash,
1902  userdata->creation_errors,
1903  count);
1904  }
1905 }
1906 
1907 void gnc_sx_all_instantiate_cashflow(GList *all_sxes,
1908  const GDate *range_start, const GDate *range_end,
1909  GHashTable* map, GList **creation_errors)
1910 {
1911  SxAllCashflow userdata;
1912  userdata.hash = map;
1913  userdata.creation_errors = creation_errors;
1914  userdata.range_start = range_start;
1915  userdata.range_end = range_end;
1916 
1917  /* The work is done in the callback for each SX */
1918  g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
1919 }
1920 
1921 
1922 GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_end)
1923 {
1924  GHashTable *result_map = gnc_g_hash_new_guid_numeric();
1925  GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
1927  &range_start, &range_end,
1928  result_map, NULL);
1929  return result_map;
1930 }
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:5458
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:580
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