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