GnuCash c935c2f+
Loading...
Searching...
No Matches
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"
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"
56static 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
68typedef 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
87static GncSxInstanceModel* gnc_sx_instance_model_new(void);
88
89static GncSxInstance* gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceState state, GDate *date, void *temporal_state, gint sequence_num);
90
91static gint _get_vars_helper(Transaction *txn, void *var_hash_data);
92
93static GncSxVariable* gnc_sx_variable_new(gchar *name);
94
95static void _gnc_sx_instance_event_handler(QofInstance *ent, QofEventId event_type, gpointer user_data, gpointer evt_data);
96static gnc_commodity* get_transaction_currency(SxTxnCreationData *creation_data, SchedXaction *sx, Transaction *template_txn);
97/* ------------------------------------------------------------ */
98
99typedef struct
100{
101 const char *name;
102 gnc_numeric amount;
103} ScrubItem;
104
105enum
106{
107 REMOVING, UPDATED, ADDED,
108 LAST_SIGNAL
109};
110
111static guint signals[LAST_SIGNAL] = { 0 };
112
113static void
114scrub_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 */
153void
154gnc_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
176static 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
182static 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
194static void
195_wipe_parsed_sx_var(gchar *key, GncSxVariable *var, gpointer unused_user_data)
196{
198}
199
200static gboolean
201split_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
224GHashTable*
225gnc_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
234int
235gnc_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
265static GncSxVariable*
266gnc_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
276gnc_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
284static GncSxVariable*
285gnc_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
293void
294gnc_sx_variable_free(GncSxVariable *var)
295{
296 g_free(var->name);
297 g_free(var);
298}
299
300static inline gchar*
301var_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
313static 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
378Account*
379gnc_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
390void
391gnc_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
397static 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
409void
410gnc_sx_randomize_variables(GHashTable *vars)
411{
412 g_hash_table_foreach(vars, (GHFunc)_set_var_to_random_value, NULL);
413}
414
415static 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
424static GncSxInstance*
425gnc_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
461static gint
462_compare_GncSxVariables(gconstpointer a, gconstpointer b)
463{
464 return strcmp(((const GncSxVariable*)a)->name, ((const GncSxVariable*)b)->name);
465}
466
467static 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
474GList *
475gnc_sx_instance_get_variables(GncSxInstance *inst)
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
482static 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
558static GncSxInstances*
559sx_gen_select_instances(gpointer *data, gpointer user_data)
560{
561 GncSxInstances *instances = _gnc_sx_gen_instances(data, user_data);
562 GList *instlist = instances->instance_list;
563
564 if (instlist == NULL)
565 {
566 SchedXaction *sx = instances->sx;
567 SXTmpStateData *temporal_state = gnc_sx_create_temporal_state(sx);
568 GDate cur_date = xaccSchedXactionGetNextInstance(sx, temporal_state);
569
570 if (g_date_valid(&cur_date))
571 {
572 int seq_num = gnc_sx_get_instance_count(sx, temporal_state);
573 GncSxInstance *inst = gnc_sx_instance_new(instances, SX_INSTANCE_STATE_REMINDER,
574 &cur_date, temporal_state, seq_num);
575
576 instances->instance_list = g_list_prepend (instlist, inst);
577 }
578
579 gnc_sx_destroy_temporal_state (temporal_state);
580 }
581
582 return instances;
583}
584
585static GncSxInstances*
586sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
587{
588 return model->include_disabled ?
589 sx_gen_select_instances((gpointer)sx, (gpointer)&model->range_end) :
590 _gnc_sx_gen_instances((gpointer)sx, (gpointer)&model->range_end);
591}
592
593GncSxInstanceModel*
594gnc_sx_get_current_instances(void)
595{
596 GDate now;
597 g_date_clear(&now, 1);
598 gnc_gdate_set_time64 (&now, gnc_time (NULL));
599 return gnc_sx_get_instances(&now, FALSE);
600}
601
602GncSxInstanceModel*
603gnc_sx_get_instances(const GDate *range_end, gboolean include_disabled)
604{
605 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
606 GncSxInstanceModel *instances;
607
608 g_assert(range_end != NULL);
609 g_assert(g_date_valid(range_end));
610
611 instances = gnc_sx_instance_model_new();
612 instances->include_disabled = include_disabled;
613 instances->range_end = *range_end;
614
615 if (include_disabled)
616 {
617 instances->sx_instance_list = gnc_g_list_map(all_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
618 }
619 else
620 {
621 GList *sx_iter = g_list_first(all_sxes);
622 GList *enabled_sxes = NULL;
623
624 for (; sx_iter != NULL; sx_iter = sx_iter->next)
625 {
626 SchedXaction *sx = (SchedXaction*)sx_iter->data;
627 if (xaccSchedXactionGetEnabled(sx))
628 {
629 enabled_sxes = g_list_prepend (enabled_sxes, sx);
630 }
631 }
632 enabled_sxes = g_list_reverse (enabled_sxes);
633 instances->sx_instance_list = gnc_g_list_map(enabled_sxes, (GncGMapFunc)_gnc_sx_gen_instances, (gpointer)range_end);
634 g_list_free(enabled_sxes);
635 }
636
637 return instances;
638}
639
640GncSxInstanceModel*
641gnc_sx_get_select_instances(GList *sel_sxes)
642{
643 GncSxInstanceModel *instances;
644 GDate now;
645
646 g_date_clear(&now, 1);
647 gnc_gdate_set_time64(&now, gnc_time(NULL));
648
649 instances = gnc_sx_instance_model_new();
650 instances->include_disabled = TRUE;
651 instances->range_end = now;
652
653 instances->sx_instance_list = gnc_g_list_map(sel_sxes, (GncGMapFunc)sx_gen_select_instances, (gpointer)&now);
654
655 return instances;
656}
657
658G_DEFINE_TYPE (GncSxInstanceModel, gnc_sx_instance_model, G_TYPE_OBJECT)
659
660static GncSxInstanceModel*
661gnc_sx_instance_model_new(void)
662{
663 return GNC_SX_INSTANCE_MODEL(g_object_new(GNC_TYPE_SX_INSTANCE_MODEL, NULL));
664}
665
666static void
667gnc_sx_instance_model_dispose(GObject *object)
668{
669 GncSxInstanceModel *model;
670 g_return_if_fail(object != NULL);
671 model = GNC_SX_INSTANCE_MODEL(object);
672
673 g_return_if_fail(!model->disposed);
674 model->disposed = TRUE;
675
676 qof_event_unregister_handler(model->qof_event_handler_id);
677
678 G_OBJECT_CLASS(gnc_sx_instance_model_parent_class)->dispose(object);
679}
680
681static void
682gnc_sx_instance_free(GncSxInstance *instance)
683{
685
686 if (instance->variable_bindings != NULL)
687 {
688 g_hash_table_destroy(instance->variable_bindings);
689 }
690 instance->variable_bindings = NULL;
691
692 g_free(instance);
693}
694
695static void
696gnc_sx_instances_free(GncSxInstances *instances)
697{
698 GList *instance_iter;
699
700 if (instances->variable_names != NULL)
701 {
702 g_hash_table_destroy(instances->variable_names);
703 }
704 instances->variable_names = NULL;
705
706 instances->sx = NULL;
707
708 for (instance_iter = instances->instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
709 {
710 GncSxInstance *inst = (GncSxInstance*)instance_iter->data;
711 gnc_sx_instance_free(inst);
712 }
713 g_list_free(instances->instance_list);
714 instances->instance_list = NULL;
715
716 g_free(instances);
717}
718
719static void
720gnc_sx_instance_model_finalize (GObject *object)
721{
722 GncSxInstanceModel *model;
723 GList *sx_list_iter;
724
725 g_return_if_fail(object != NULL);
726
727 model = GNC_SX_INSTANCE_MODEL(object);
728 for (sx_list_iter = model->sx_instance_list; sx_list_iter != NULL; sx_list_iter = sx_list_iter->next)
729 {
730 GncSxInstances *instances = (GncSxInstances*)sx_list_iter->data;
731 gnc_sx_instances_free(instances);
732 }
733 g_list_free(model->sx_instance_list);
734 model->sx_instance_list = NULL;
735
736 G_OBJECT_CLASS(gnc_sx_instance_model_parent_class)->finalize(object);
737}
738
739static void
740gnc_sx_instance_model_class_init (GncSxInstanceModelClass *klass)
741{
742 GObjectClass *object_class = G_OBJECT_CLASS(klass);
743
744 object_class->dispose = gnc_sx_instance_model_dispose;
745 object_class->finalize = gnc_sx_instance_model_finalize;
746
747 signals[REMOVING] =
748 g_signal_new("removing",
749 GNC_TYPE_SX_INSTANCE_MODEL,
750 G_SIGNAL_RUN_FIRST,
751 0, /* class offset */
752 NULL, /* accumulator */
753 NULL, /* accum data */
754 g_cclosure_marshal_VOID__POINTER,
755 G_TYPE_NONE,
756 1,
757 G_TYPE_POINTER);
758
759 signals[UPDATED] =
760 g_signal_new("updated",
761 GNC_TYPE_SX_INSTANCE_MODEL,
762 G_SIGNAL_RUN_FIRST,
763 0, /* class offset */
764 NULL, /* accumulator */
765 NULL, /* accum data */
766 g_cclosure_marshal_VOID__POINTER,
767 G_TYPE_NONE,
768 1,
769 G_TYPE_POINTER);
770
771 signals[ADDED] =
772 g_signal_new("added",
773 GNC_TYPE_SX_INSTANCE_MODEL,
774 G_SIGNAL_RUN_FIRST,
775 0, /* class offset */
776 NULL, /* accumulator */
777 NULL, /* accum data */
778 g_cclosure_marshal_VOID__POINTER,
779 G_TYPE_NONE,
780 1,
781 G_TYPE_POINTER);
782}
783
784static void
785gnc_sx_instance_model_init(GncSxInstanceModel *inst)
786{
787 g_date_clear(&inst->range_end, 1);
788 inst->sx_instance_list = NULL;
789 inst->qof_event_handler_id = qof_event_register_handler(_gnc_sx_instance_event_handler, inst);
790}
791
792static gint
793_gnc_sx_instance_find_by_sx(GncSxInstances *in_list_instances, SchedXaction *sx_to_find)
794{
795 if (in_list_instances->sx == sx_to_find)
796 return 0;
797 return -1;
798}
799
800static void
801_gnc_sx_instance_event_handler(QofInstance *ent, QofEventId event_type, gpointer user_data, gpointer evt_data)
802{
803 GncSxInstanceModel *instances = GNC_SX_INSTANCE_MODEL(user_data);
804
805 /* selection rules {
806 // (gnc_collection_get_schedxaction_list(book), GNC_EVENT_ITEM_ADDED)
807 // (gnc_collection_get_schedxaction_list(book), GNC_EVENT_ITEM_REMOVED)
808 // (GNC_IS_SX(ent), QOF_EVENT_MODIFIED)
809 // } */
810 if (!(GNC_IS_SX(ent) || GNC_IS_SXES(ent)))
811 return;
812
813 if (GNC_IS_SX(ent))
814 {
815 SchedXaction *sx;
816 gboolean sx_is_in_model = FALSE;
817
818 sx = GNC_SX(ent);
819 // only send `updated` if it's actually in the model
820 sx_is_in_model = (g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx) != NULL);
821 if (event_type & QOF_EVENT_MODIFY)
822 {
823 if (sx_is_in_model)
824 {
825 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
826 {
827 g_signal_emit_by_name(instances, "updated", (gpointer)sx);
828 }
829 else
830 {
831 /* the sx was enabled but is now disabled */
832 g_signal_emit_by_name(instances, "removing", (gpointer)sx);
833 }
834 }
835 else
836 {
837 /* determine if this is a legitimate SX or just a "one-off" / being created */
838 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
839 if (g_list_find(all_sxes, sx) && (!instances->include_disabled && xaccSchedXactionGetEnabled(sx)))
840 {
841 /* it's moved from disabled to enabled, add the instances */
842 instances->sx_instance_list = g_list_append(instances->sx_instance_list, sx_instances(instances, sx));
843 g_signal_emit_by_name(instances, "added", (gpointer)sx);
844 }
845 }
846 }
847 /* else { unsupported event type; ignore } */
848 }
849 else if (GNC_IS_SXES(ent))
850 {
851 SchedXaction *sx = GNC_SX(evt_data);
852
853 if (event_type & GNC_EVENT_ITEM_REMOVED)
854 {
855 GList *instances_link;
856 instances_link = g_list_find_custom(instances->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
857 if (instances_link != NULL)
858 {
859 g_signal_emit_by_name(instances, "removing", (gpointer)sx);
860 }
861 else if (instances->include_disabled)
862 {
863 g_warning("could not remove instances that do not exist in the model");
864 }
865 }
866 else if (event_type & GNC_EVENT_ITEM_ADDED)
867 {
868 if (instances->include_disabled || xaccSchedXactionGetEnabled(sx))
869 {
870 /* generate instances, add to instance list, emit update. */
871 instances->sx_instance_list = g_list_append(instances->sx_instance_list, sx_instances(instances, sx));
872 g_signal_emit_by_name(instances, "added", (gpointer)sx);
873 }
874 }
875 /* else { g_critical("unsupported event type [%d]\n", event_type); } */
876 }
877}
878
879typedef struct _HashListPair
880{
881 GHashTable *hash;
882 GList *list;
884
885static void
886_find_unreferenced_vars(gchar *key,
887 gpointer value,
888 HashListPair *cb_pair)
889{
890 if (cb_pair->hash == NULL ||
891 !g_hash_table_lookup_extended(cb_pair->hash, key, NULL, NULL))
892 {
893 DEBUG("variable [%s] not found", key);
894 cb_pair->list = g_list_prepend (cb_pair->list, key);
895 }
896}
897
898void
899gnc_sx_instance_model_update_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
900{
901 GncSxInstances *existing, *new_instances;
902 GList *link;
903
904 link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
905 if (link == NULL)
906 {
907 g_critical("couldn't find sx [%p]\n", sx);
908 return;
909 }
910
911 // merge the new instance data into the existing structure, mutating as little as possible.
912 existing = (GncSxInstances*)link->data;
913 new_instances = sx_instances(model, sx);
914 existing->sx = new_instances->sx;
915 existing->next_instance_date = new_instances->next_instance_date;
916 {
917 GList *existing_iter, *new_iter;
918 gboolean existing_remain, new_remain;
919
920 // step through the lists pairwise, and retain the existing
921 // instance if the dates align, as soon as they don't stop and
922 // cleanup.
923 existing_iter = existing->instance_list;
924 new_iter = new_instances->instance_list;
925 for (; existing_iter != NULL && new_iter != NULL; existing_iter = existing_iter->next, new_iter = new_iter->next)
926 {
927 GncSxInstance *existing_inst, *new_inst;
928 gboolean same_instance_date;
929 existing_inst = (GncSxInstance*)existing_iter->data;
930 new_inst = (GncSxInstance*)new_iter->data;
931
932 same_instance_date = g_date_compare(&existing_inst->date, &new_inst->date) == 0;
933 if (!same_instance_date)
934 break;
935 }
936
937 existing_remain = (existing_iter != NULL);
938 new_remain = (new_iter != NULL);
939
940 if (existing_remain)
941 {
942 // delete excess
943 gnc_g_list_cut(&existing->instance_list, existing_iter);
944 g_list_foreach(existing_iter, (GFunc)gnc_sx_instance_free, NULL);
945 }
946
947 if (new_remain)
948 {
949 // append new
950 GList *new_iter_iter;
951 gnc_g_list_cut(&new_instances->instance_list, new_iter);
952
953 for (new_iter_iter = new_iter; new_iter_iter != NULL; new_iter_iter = new_iter_iter->next)
954 {
955 GncSxInstance *inst = (GncSxInstance*)new_iter_iter->data;
956 inst->parent = existing;
957 existing->instance_list = g_list_append(existing->instance_list, new_iter_iter->data);
958 }
959 g_list_free(new_iter);
960 }
961 }
962
963 // handle variables
964 {
965 GList *removed_var_names = NULL, *added_var_names = NULL;
966 GList *inst_iter = NULL;
967
968 if (existing->variable_names != NULL)
969 {
970 HashListPair removed_cb_data;
971 removed_cb_data.hash = new_instances->variable_names;
972 removed_cb_data.list = NULL;
973 g_hash_table_foreach(existing->variable_names, (GHFunc)_find_unreferenced_vars, &removed_cb_data);
974 removed_var_names = g_list_reverse (removed_cb_data.list);
975 }
976 DEBUG("%d removed variables", g_list_length(removed_var_names));
977
978 if (new_instances->variable_names != NULL)
979 {
980 HashListPair added_cb_data;
981 added_cb_data.hash = existing->variable_names;
982 added_cb_data.list = NULL;
983 g_hash_table_foreach(new_instances->variable_names, (GHFunc)_find_unreferenced_vars, &added_cb_data);
984 added_var_names = g_list_reverse (added_cb_data.list);
985 }
986 DEBUG("%d added variables", g_list_length(added_var_names));
987
988 if (existing->variable_names != NULL)
989 {
990 g_hash_table_destroy(existing->variable_names);
991 }
992 existing->variable_names = new_instances->variable_names;
993 new_instances->variable_names = NULL;
994
995 for (inst_iter = existing->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
996 {
997 GList *var_iter;
998 GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
999
1000 for (var_iter = removed_var_names; var_iter != NULL; var_iter = var_iter->next)
1001 {
1002 gchar *to_remove_key = (gchar*)var_iter->data;
1003 g_hash_table_remove(inst->variable_bindings, to_remove_key);
1004 }
1005
1006 for (var_iter = added_var_names; var_iter != NULL; var_iter = var_iter->next)
1007 {
1008 gchar *to_add_key = (gchar*)var_iter->data;
1009 if (!g_hash_table_lookup_extended(
1010 inst->variable_bindings, to_add_key, NULL, NULL))
1011 {
1012 GncSxVariable *parent_var
1013 = g_hash_table_lookup(existing->variable_names, to_add_key);
1014 GncSxVariable *var_copy;
1015
1016 g_assert(parent_var != NULL);
1017 var_copy = gnc_sx_variable_new_copy(parent_var);
1018 g_hash_table_insert(inst->variable_bindings, g_strdup(to_add_key), var_copy);
1019 }
1020 }
1021 }
1022 }
1023 gnc_sx_instances_free(new_instances);
1024}
1025
1026void
1027gnc_sx_instance_model_remove_sx_instances(GncSxInstanceModel *model, SchedXaction *sx)
1028{
1029 GList *instance_link = NULL;
1030
1031 instance_link = g_list_find_custom(model->sx_instance_list, sx, (GCompareFunc)_gnc_sx_instance_find_by_sx);
1032 if (instance_link == NULL)
1033 {
1034 g_warning("instance not found!\n");
1035 return;
1036 }
1037
1038 model->sx_instance_list = g_list_remove_link(model->sx_instance_list, instance_link);
1039 gnc_sx_instances_free((GncSxInstances*)instance_link->data);
1040}
1041
1042static void
1043increment_sx_state(GncSxInstance *inst, GDate **last_occur_date, int *instance_count, int *remain_occur_count)
1044{
1045 if (!g_date_valid(*last_occur_date)
1046 || (g_date_valid(*last_occur_date)
1047 && g_date_compare(*last_occur_date, &inst->date) <= 0))
1048 {
1049 *last_occur_date = &inst->date;
1050 }
1051
1052 *instance_count = gnc_sx_get_instance_count(inst->parent->sx, inst->temporal_state) + 1;
1053
1054 if (*remain_occur_count > 0)
1055 {
1056 *remain_occur_count -= 1;
1057 }
1058}
1059
1060static gboolean
1061_get_template_split_account(const SchedXaction* sx,
1062 const Split *template_split,
1063 Account **split_acct,
1064 GList **creation_errors)
1065{
1066 gboolean success = TRUE;
1067 GncGUID *acct_guid = NULL;
1068 qof_instance_get (QOF_INSTANCE (template_split),
1069 "sx-account", &acct_guid,
1070 NULL);
1071 *split_acct = xaccAccountLookup(acct_guid, gnc_get_current_book());
1072 if (!*split_acct && sx && creation_errors)
1073 {
1074 char guid_str[GUID_ENCODING_LENGTH+1];
1075/* Translators: A list of error messages from the Scheduled Transactions (SX).
1076 They might appear in their editor or in "Since last run". */
1077 gchar* err = N_("Unknown account for guid [%s], cancelling SX [%s] creation.");
1078 guid_to_string_buff((const GncGUID*)acct_guid, guid_str);
1079 REPORT_ERROR(creation_errors, err, guid_str, xaccSchedXactionGetName(sx));
1080 success = FALSE;
1081 }
1082
1083 guid_free (acct_guid);
1084 return success;
1085}
1086
1087static void
1088_get_sx_formula_value(const SchedXaction* sx,
1089 const Split *template_split,
1090 gnc_numeric *numeric,
1091 GList **creation_errors,
1092 const char *formula_key,
1093 const char* numeric_key,
1094 GHashTable *variable_bindings)
1095{
1096
1097 char *formula_str = NULL, *parseErrorLoc = NULL;
1098 gnc_numeric *numeric_val = NULL;
1099 qof_instance_get (QOF_INSTANCE (template_split),
1100 formula_key, &formula_str,
1101 numeric_key, &numeric_val,
1102 NULL);
1103
1104 if ((variable_bindings == NULL ||
1105 g_hash_table_size (variable_bindings) == 0) &&
1106 numeric_val != NULL &&
1107 gnc_numeric_check(*numeric_val) == GNC_ERROR_OK &&
1108 !gnc_numeric_zero_p(*numeric_val))
1109 {
1110 /* If there are no variables to parse and we had a valid numeric stored
1111 * then we can skip parsing the formula, which might save some
1112 * localization problems with separators. */
1113 numeric->num = numeric_val->num;
1114 numeric->denom = numeric_val->denom;
1115 g_free (formula_str);
1116 g_free (numeric_val);
1117 return;
1118 }
1119
1120 if (formula_str != NULL && strlen(formula_str) != 0)
1121 {
1122 GHashTable *parser_vars = NULL;
1123 if (variable_bindings)
1124 {
1125 parser_vars = gnc_sx_instance_get_variables_for_parser(variable_bindings);
1126 }
1127 if (!gnc_exp_parser_parse_separate_vars(formula_str,
1128 numeric,
1129 &parseErrorLoc,
1130 parser_vars))
1131 {
1132 gchar *err = N_("Error parsing SX [%s] key [%s]=formula [%s] at [%s]: %s.");
1133 REPORT_ERROR(creation_errors, err,
1134 xaccSchedXactionGetName(sx),
1135 formula_key,
1136 formula_str,
1137 parseErrorLoc,
1138 gnc_exp_parser_error_string());
1139 }
1140
1141 if (parser_vars != NULL)
1142 {
1143 g_hash_table_destroy(parser_vars);
1144 }
1145 }
1146 g_free (formula_str);
1147 g_free (numeric_val);
1148}
1149
1150static void
1151_get_credit_formula_value(GncSxInstance *instance,
1152 const Split *template_split, gnc_numeric *credit_num,
1153 GList **creation_errors)
1154{
1155 _get_sx_formula_value(instance->parent->sx, template_split, credit_num,
1156 creation_errors, "sx-credit-formula",
1157 "sx-credit-numeric", instance->variable_bindings);
1158}
1159
1160static void
1161_get_debit_formula_value(GncSxInstance *instance, const Split *template_split,
1162 gnc_numeric *debit_num, GList **creation_errors)
1163{
1164 _get_sx_formula_value(instance->parent->sx, template_split, debit_num,
1165 creation_errors, "sx-debit-formula",
1166 "sx-debit-numeric", instance->variable_bindings);
1167}
1168
1169static gnc_numeric
1170split_apply_formulas (const Split *split, SxTxnCreationData* creation_data)
1171{
1172 gnc_numeric credit_num = gnc_numeric_zero();
1173 gnc_numeric debit_num = gnc_numeric_zero();
1174 gnc_numeric final;
1175 gint gncn_error;
1176 SchedXaction *sx = creation_data->instance->parent->sx;
1177
1178 _get_credit_formula_value(creation_data->instance, split, &credit_num,
1179 creation_data->creation_errors);
1180 _get_debit_formula_value(creation_data->instance, split, &debit_num,
1181 creation_data->creation_errors);
1182
1183 final = gnc_numeric_sub_fixed(debit_num, credit_num);
1184
1185 gncn_error = gnc_numeric_check(final);
1186 if (gncn_error != GNC_ERROR_OK)
1187 {
1188 gchar *err = N_("Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1189 REPORT_ERROR(creation_data->creation_errors, err,
1190 gncn_error, xaccSchedXactionGetName(sx));
1191 final = gnc_numeric_zero();
1192 }
1193 return final;
1194}
1195
1196static void
1197split_apply_exchange_rate (Split *split, GHashTable *bindings,
1198 gnc_commodity *txn_cmdty,
1199 gnc_commodity *split_cmdty, gnc_numeric *final)
1200{
1201 gchar *exchange_rate_var_name;
1202 GncSxVariable *exchange_rate_var;
1203 gnc_numeric amt;
1204 gnc_numeric exchange_rate = gnc_numeric_create (1, 1);
1205
1206 exchange_rate_var_name = var_name_from_commodities(split_cmdty, txn_cmdty);
1207 exchange_rate_var =
1208 (GncSxVariable*)g_hash_table_lookup(bindings,
1209 exchange_rate_var_name);
1210
1211 if (exchange_rate_var != NULL)
1212 {
1213 exchange_rate = exchange_rate_var->value;
1214 DEBUG("exchange_rate is %s", gnc_num_dbg_to_string (exchange_rate));
1215 }
1216 g_free (exchange_rate_var_name);
1217
1218 if (!gnc_commodity_is_currency (split_cmdty))
1219 amt = gnc_numeric_div(*final, exchange_rate,
1220 gnc_commodity_get_fraction (split_cmdty),
1221 GNC_HOW_RND_ROUND_HALF_UP);
1222 else
1223 amt = gnc_numeric_mul(*final, exchange_rate, 1000,
1224 GNC_HOW_RND_ROUND_HALF_UP);
1225
1226
1227 DEBUG("amount is %s for memo split '%s'", gnc_num_dbg_to_string (amt),
1228 xaccSplitGetMemo (split));
1229 xaccSplitSetAmount(split, amt); /* marks split dirty */
1230
1231}
1232/* If the template_txn was created from the SX Editor then it has the default
1233 * currency even if none of its splits do; if the template_txn was created from
1234 * a non-currency register then it will be requesting backwards prices. Check
1235 * that the template_txn currency is in at least one split; if it's not a
1236 * currency and one of the splits is, use that currency. If there are no
1237 * currencies at all assume that the user knew what they were doing and return
1238 * the template_transaction's commodity.
1239 *
1240 * Since we're going through the split commodities anyway, check that they all
1241 * have usable values. If we find an error return NULL as a signal to
1242 * create_each_transaction_helper to bail out.
1243 */
1244
1245static gnc_commodity*
1246get_transaction_currency(SxTxnCreationData *creation_data,
1247 SchedXaction *sx, Transaction *template_txn)
1248{
1249 gnc_commodity *first_currency = NULL, *first_cmdty = NULL,
1250 *fallback_cmdty = NULL;
1251 gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
1252 gnc_commodity *txn_cmdty = xaccTransGetCurrency (template_txn);
1253 GList* txn_splits = xaccTransGetSplitList (template_txn);
1254 GList** creation_errors =
1255 creation_data ? creation_data->creation_errors : NULL;
1256
1257 if (txn_cmdty)
1258 DEBUG("Template txn currency is %s.",
1259 gnc_commodity_get_mnemonic (txn_cmdty));
1260 else
1261 DEBUG("No template txn currency.");
1262
1263 for (;txn_splits; txn_splits = txn_splits->next)
1264 {
1265 Split* t_split = (Split*)txn_splits->data;
1266 Account* split_account = NULL;
1267 gnc_commodity *split_cmdty = NULL;
1268
1269 if (!_get_template_split_account(sx, t_split, &split_account,
1270 creation_errors))
1271 {
1272 err_flag = TRUE;
1273 break;
1274 }
1275 /* Don't consider the commodity of a transaction that has
1276 * neither a credit nor a debit formula. */
1277
1278 if (!fallback_cmdty)
1279 fallback_cmdty = xaccAccountGetCommodity (split_account);
1280
1281 if (split_is_marker(t_split))
1282 continue;
1283
1284 split_cmdty = xaccAccountGetCommodity (split_account);
1285 if (!txn_cmdty)
1286 txn_cmdty = split_cmdty;
1287 if (!first_cmdty)
1288 first_cmdty = split_cmdty;
1289 if (gnc_commodity_equal (split_cmdty, txn_cmdty))
1290 txn_cmdty_in_splits = TRUE;
1291 if (!first_currency && gnc_commodity_is_currency (split_cmdty))
1292 first_currency = split_cmdty;
1293 }
1294 if (err_flag)
1295 {
1296 g_critical("Error in SX transaction [%s], split missing account: "
1297 "Creation aborted.", xaccSchedXactionGetName(sx));
1298 return NULL;
1299 }
1300 if (first_currency &&
1301 (!txn_cmdty_in_splits || !gnc_commodity_is_currency (txn_cmdty)))
1302 return first_currency;
1303 if (!txn_cmdty_in_splits && first_cmdty)
1304 return first_cmdty;
1305 if (txn_cmdty)
1306 return txn_cmdty;
1307 return fallback_cmdty;
1308}
1309
1310static gboolean
1311create_each_transaction_helper(Transaction *template_txn, void *user_data)
1312{
1313 Transaction *new_txn;
1314 GList *txn_splits, *template_splits;
1315 Split *copying_split;
1316 SxTxnCreationData *creation_data = (SxTxnCreationData*)user_data;
1317 SchedXaction *sx = creation_data->instance->parent->sx;
1318 gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
1319 sx, template_txn);
1320
1321 /* No txn_cmdty means there was a defective split. Bail. */
1322 if (txn_cmdty == NULL)
1323 return FALSE;
1324
1325 /* FIXME: In general, this should [correctly] deal with errors such
1326 as not finding the appropriate Accounts and not being able to
1327 parse the formula|credit/debit strings. */
1328
1329 new_txn = xaccTransCloneNoKvp(template_txn);
1330 xaccTransBeginEdit(new_txn);
1331
1332 DEBUG("creating template txn desc [%s] for sx [%s]",
1333 xaccTransGetDescription(new_txn),
1334 xaccSchedXactionGetName(sx));
1335
1336
1337 /* Bug#500427: copy the notes, if any */
1338 if (xaccTransGetNotes(template_txn) != NULL)
1339 xaccTransSetNotes (new_txn, xaccTransGetNotes (template_txn));
1340
1341 xaccTransSetDate(new_txn,
1342 g_date_get_day(&creation_data->instance->date),
1343 g_date_get_month(&creation_data->instance->date),
1344 g_date_get_year(&creation_data->instance->date));
1345
1346 xaccTransSetDateEnteredSecs(new_txn, gnc_time(NULL));
1347 template_splits = xaccTransGetSplitList(template_txn);
1348 txn_splits = xaccTransGetSplitList(new_txn);
1349 if ((template_splits == NULL) || (txn_splits == NULL))
1350 {
1351 g_critical("transaction w/o splits for sx [%s]",
1352 xaccSchedXactionGetName(sx));
1353 xaccTransDestroy(new_txn);
1354 xaccTransCommitEdit(new_txn);
1355 return FALSE;
1356 }
1357
1358 if (txn_cmdty == NULL)
1359 {
1360 xaccTransDestroy(new_txn);
1361 xaccTransCommitEdit(new_txn);
1362 return FALSE;
1363 }
1364 xaccTransSetCurrency(new_txn, txn_cmdty);
1365
1366 for (;
1367 txn_splits && template_splits;
1368 txn_splits = txn_splits->next, template_splits = template_splits->next)
1369 {
1370 const Split *template_split;
1371 Account *split_acct;
1372 gnc_commodity *split_cmdty = NULL;
1373
1374 /* FIXME: Ick. This assumes that the split lists will be ordered
1375 identically. :( They are, but we'd rather not have to count on
1376 it. --jsled */
1377 template_split = (Split*)template_splits->data;
1378 copying_split = (Split*)txn_splits->data;
1379
1380 _get_template_split_account(sx, template_split, &split_acct,
1381 creation_data->creation_errors);
1382
1383 split_cmdty = xaccAccountGetCommodity(split_acct);
1384 xaccSplitSetAccount(copying_split, split_acct);
1385
1386 {
1387 gnc_numeric final = split_apply_formulas(template_split,
1388 creation_data);
1389 xaccSplitSetValue(copying_split, final);
1390 DEBUG("value is %s for memo split '%s'",
1391 gnc_num_dbg_to_string (final),
1392 xaccSplitGetMemo (copying_split));
1393 if (! gnc_commodity_equal(split_cmdty, txn_cmdty))
1394 {
1395 split_apply_exchange_rate(copying_split,
1396 creation_data->instance->variable_bindings,
1397 txn_cmdty, split_cmdty, &final);
1398 }
1399
1400 xaccSplitScrub(copying_split);
1401 }
1402 }
1403
1404
1405 {
1406 qof_instance_set (QOF_INSTANCE (new_txn),
1407 "from-sched-xaction",
1408 xaccSchedXactionGetGUID(creation_data->instance->parent->sx),
1409 NULL);
1410 }
1411
1412 xaccTransCommitEdit(new_txn);
1413
1414 if (creation_data->created_txn_guids != NULL)
1415 {
1416 *creation_data->created_txn_guids
1417 = g_list_append(*(creation_data->created_txn_guids),
1418 (gpointer)xaccTransGetGUID(new_txn));
1419 }
1420
1421 return FALSE;
1422}
1423
1424static void
1425create_transactions_for_instance(GncSxInstance *instance, GList **created_txn_guids, GList **creation_errors)
1426{
1427 SxTxnCreationData creation_data;
1428 Account *sx_template_account;
1429
1430 sx_template_account = gnc_sx_get_template_transaction_account(instance->parent->sx);
1431
1432 creation_data.instance = instance;
1433 creation_data.created_txn_guids = created_txn_guids;
1434 creation_data.creation_errors = creation_errors;
1435 /* Don't update the GUI for every transaction, it can really slow things
1436 * down.
1437 */
1439 xaccAccountForEachTransaction(sx_template_account,
1440 create_each_transaction_helper,
1441 &creation_data);
1443}
1444
1445void
1446gnc_sx_instance_model_effect_change(GncSxInstanceModel *model,
1447 gboolean auto_create_only,
1448 GList **created_transaction_guids,
1449 GList **creation_errors)
1450{
1451 GList *iter;
1452
1453 if (qof_book_is_readonly(gnc_get_current_book()))
1454 {
1455 /* Is the book read-only? Then don't change anything here. */
1456 return;
1457 }
1458
1459 for (iter = model->sx_instance_list; iter != NULL; iter = iter->next)
1460 {
1461 GList *instance_iter;
1462 GncSxInstances *instances = (GncSxInstances*)iter->data;
1463 GDate *last_occur_date;
1464 gint instance_count = 0;
1465 gint remain_occur_count = 0;
1466
1467 // If there are no instances, then skip; specifically, skip
1468 // re-setting SchedXaction fields, which will dirty the book
1469 // spuriously.
1470 if (g_list_length(instances->instance_list) == 0)
1471 continue;
1472
1473 last_occur_date = (GDate*) xaccSchedXactionGetLastOccurDate(instances->sx);
1474 instance_count = gnc_sx_get_instance_count(instances->sx, NULL);
1475 remain_occur_count = xaccSchedXactionGetRemOccur(instances->sx);
1476
1477 for (instance_iter = instances->instance_list; instance_iter != NULL; instance_iter = instance_iter->next)
1478 {
1479 GncSxInstance *inst = (GncSxInstance*)instance_iter->data;
1480 gboolean sx_is_auto_create;
1481 GList *instance_errors = NULL;
1482
1483 xaccSchedXactionGetAutoCreate(inst->parent->sx, &sx_is_auto_create, NULL);
1484 if (auto_create_only && !sx_is_auto_create)
1485 {
1486 if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
1487 {
1488 break;
1489 }
1490 continue;
1491 }
1492
1493 if (inst->orig_state == SX_INSTANCE_STATE_POSTPONED
1494 && inst->state != SX_INSTANCE_STATE_POSTPONED)
1495 {
1496 // remove from postponed list
1497 g_assert(inst->temporal_state != NULL);
1499 inst->temporal_state);
1500 }
1501
1502 switch (inst->state)
1503 {
1504 case SX_INSTANCE_STATE_CREATED:
1505 // nop: we've already processed this.
1506 break;
1507 case SX_INSTANCE_STATE_IGNORED:
1508 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1509 break;
1510 case SX_INSTANCE_STATE_POSTPONED:
1511 if (inst->orig_state != SX_INSTANCE_STATE_POSTPONED)
1512 {
1513 gnc_sx_add_defer_instance(instances->sx,
1515 }
1516 increment_sx_state(inst, &last_occur_date, &instance_count, &remain_occur_count);
1517 break;
1518 case SX_INSTANCE_STATE_TO_CREATE:
1519 create_transactions_for_instance (inst,
1520 created_transaction_guids,
1521 &instance_errors);
1522 if (instance_errors == NULL)
1523 {
1524 increment_sx_state (inst, &last_occur_date,
1525 &instance_count,
1526 &remain_occur_count);
1527 gnc_sx_instance_model_change_instance_state
1528 (model, inst, SX_INSTANCE_STATE_CREATED);
1529 }
1530 else if (creation_errors)
1531 {
1532 *creation_errors = g_list_concat (*creation_errors,
1533 instance_errors);
1534 instance_errors = NULL;
1535 }
1536 break;
1537 case SX_INSTANCE_STATE_REMINDER:
1538 // do nothing
1539 // assert no non-remind instances after this?
1540 break;
1541 default:
1542 g_assert_not_reached();
1543 break;
1544 }
1545
1546 if (instance_errors)
1547 g_list_free_full (instance_errors, g_free);
1548 }
1549
1550 xaccSchedXactionSetLastOccurDate(instances->sx, last_occur_date);
1551 gnc_sx_set_instance_count(instances->sx, instance_count);
1552 xaccSchedXactionSetRemOccur(instances->sx, remain_occur_count);
1553 }
1554}
1555
1556void
1557gnc_sx_instance_model_change_instance_state(GncSxInstanceModel *model,
1558 GncSxInstance *instance,
1559 GncSxInstanceState new_state)
1560{
1561 if (instance->state == new_state)
1562 return;
1563
1564 instance->state = new_state;
1565
1566 // ensure 'remind' constraints are met:
1567 {
1568 GList *inst_iter;
1569 inst_iter = g_list_find(instance->parent->instance_list, instance);
1570 g_assert(inst_iter != NULL);
1571 if (instance->state != SX_INSTANCE_STATE_REMINDER)
1572 {
1573 // iterate backwards, making sure reminders are changed to 'postponed'
1574 for (inst_iter = inst_iter->prev; inst_iter != NULL; inst_iter = inst_iter->prev)
1575 {
1576 GncSxInstance *prev_inst = (GncSxInstance*)inst_iter->data;
1577 if (prev_inst->state != SX_INSTANCE_STATE_REMINDER)
1578 continue;
1579 prev_inst->state = SX_INSTANCE_STATE_POSTPONED;
1580 }
1581 }
1582 else
1583 {
1584 // iterate forward, make sure transactions are set to 'remind'
1585 for (inst_iter = inst_iter->next; inst_iter != NULL; inst_iter = inst_iter->next)
1586 {
1587 GncSxInstance *next_inst = (GncSxInstance*)inst_iter->data;
1588 if (next_inst->state == SX_INSTANCE_STATE_REMINDER)
1589 continue;
1590 next_inst->state = SX_INSTANCE_STATE_REMINDER;
1591 }
1592 }
1593 }
1594
1595 g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
1596}
1597
1598void
1599gnc_sx_instance_model_set_variable(GncSxInstanceModel *model,
1600 GncSxInstance *instance,
1601 GncSxVariable *variable,
1602 gnc_numeric *new_value)
1603{
1604
1605 if (gnc_numeric_equal(variable->value, *new_value))
1606 return;
1607 variable->value = *new_value;
1608 g_signal_emit_by_name(model, "updated", (gpointer)instance->parent->sx);
1609}
1610
1611static void
1612_list_from_hash_elts(gpointer key, gpointer value, GList **result_list)
1613{
1614 *result_list = g_list_prepend (*result_list, value);
1615}
1616
1617GList*
1618gnc_sx_instance_model_check_variables(GncSxInstanceModel *model)
1619{
1620 GList *rtn = NULL;
1621 GList *sx_iter, *inst_iter, *var_list = NULL, *var_iter;
1622
1623 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1624 {
1625 GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
1626 for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1627 {
1628 GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
1629
1630 if (inst->state != SX_INSTANCE_STATE_TO_CREATE)
1631 continue;
1632
1633 g_hash_table_foreach(inst->variable_bindings, (GHFunc)_list_from_hash_elts, &var_list);
1634 for (var_iter = var_list; var_iter != NULL; var_iter = var_iter->next)
1635 {
1636 GncSxVariable *var = (GncSxVariable*)var_iter->data;
1638 {
1639 GncSxVariableNeeded *need = g_new0(GncSxVariableNeeded, 1);
1640 need->instance = inst;
1641 need->variable = var;
1642 rtn = g_list_prepend (rtn, need);
1643 }
1644 }
1645 g_list_free(var_list);
1646 var_list = NULL;
1647 }
1648 }
1649 return rtn;
1650}
1651
1652void
1653gnc_sx_instance_model_summarize(GncSxInstanceModel *model, GncSxSummary *summary)
1654{
1655 GList *sx_iter, *inst_iter;
1656
1657 g_return_if_fail(model != NULL);
1658 g_return_if_fail(summary != NULL);
1659
1660 summary->need_dialog = FALSE;
1661 summary->num_instances = 0;
1662 summary->num_to_create_instances = 0;
1663 summary->num_auto_create_instances = 0;
1665
1666 for (sx_iter = model->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next)
1667 {
1668 GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
1669 gboolean sx_is_auto_create = FALSE, sx_notify = FALSE;
1670 xaccSchedXactionGetAutoCreate(instances->sx, &sx_is_auto_create, &sx_notify);
1671 for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
1672 {
1673 GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
1674 summary->num_instances++;
1675
1676 if (inst->state == SX_INSTANCE_STATE_TO_CREATE)
1677 {
1678 if (sx_is_auto_create)
1679 {
1680 if (!sx_notify)
1681 {
1683 }
1684 else
1685 {
1686 summary->num_auto_create_instances++;
1687 }
1688 }
1689 else
1690 {
1691 summary->num_to_create_instances++;
1692 }
1693 }
1694 }
1695 }
1696
1697 // if all the instances are 'auto-create, no-notify', then we don't need
1698 // the dialog.
1699 summary->need_dialog
1700 = (summary->num_instances != 0
1701 && summary->num_auto_create_no_notify_instances != summary->num_instances);
1702}
1703
1704void
1705gnc_sx_summary_print(const GncSxSummary *summary)
1706{
1707 PINFO("num_instances: %d", summary->num_instances);
1708 PINFO("num_to_create: %d", summary->num_to_create_instances);
1709 PINFO("num_auto_create_instances: %d", summary->num_auto_create_instances);
1710 PINFO("num_auto_create_no_notify_instances: %d", summary->num_auto_create_no_notify_instances);
1711 PINFO("need dialog? %s", summary->need_dialog ? "true" : "false");
1712}
1713
1714static void gnc_numeric_free(gpointer data)
1715{
1716 gnc_numeric *p = (gnc_numeric*) data;
1717 g_free(p);
1718}
1719
1720GHashTable* gnc_g_hash_new_guid_numeric()
1721{
1722 return g_hash_table_new_full (guid_hash_to_guint, guid_g_hash_table_equal,
1723 NULL, gnc_numeric_free);
1724}
1725
1726typedef struct
1727{
1728 GHashTable *hash;
1729 GList **creation_errors;
1730 const SchedXaction *sx;
1731 gnc_numeric count;
1733
1734static void add_to_hash_amount(GHashTable* hash, const GncGUID* guid, const gnc_numeric* amount)
1735{
1736 /* Do we have a number belonging to this GUID in the hash? If yes,
1737 * modify it in-place; if not, insert the new element into the
1738 * hash. */
1739 gnc_numeric* elem = g_hash_table_lookup(hash, guid);
1740 gchar guidstr[GUID_ENCODING_LENGTH+1];
1741 guid_to_string_buff(guid, guidstr);
1742 if (!elem)
1743 {
1744 elem = g_new0(gnc_numeric, 1);
1745 *elem = gnc_numeric_zero();
1746 g_hash_table_insert(hash, (gpointer) guid, elem);
1747 }
1748
1749 /* Check input arguments for sanity */
1750 if (gnc_numeric_check(*amount) != GNC_ERROR_OK)
1751 {
1752 g_critical("Oops, the given amount [%s] has the error code %d, at guid [%s].",
1753 gnc_num_dbg_to_string(*amount),
1754 gnc_numeric_check(*amount),
1755 guidstr);
1756 return;
1757 }
1758 if (gnc_numeric_check(*elem) != GNC_ERROR_OK)
1759 {
1760 g_critical("Oops, the account's amount [%s] has the error code %d, at guid [%s].",
1761 gnc_num_dbg_to_string(*elem),
1762 gnc_numeric_check(*elem),
1763 guidstr);
1764 return;
1765 }
1766
1767 /* Watch out - don't use gnc_numeric_add_fixed here because it
1768 * will refuse to add 1/5+1/10; instead, we have to use the flags
1769 * as given here explicitly. Eventually, add the given amount to
1770 * the entry in the hash. */
1771 *elem = gnc_numeric_add(*elem, *amount,
1773 GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_NEVER);
1774
1775 /* Check for sanity of the output. */
1776 if (gnc_numeric_check(*elem) != GNC_ERROR_OK)
1777 {
1778 g_critical("Oops, after addition at guid [%s] the resulting amount [%s] has the error code %d; added amount = [%s].",
1779 guidstr,
1780 gnc_num_dbg_to_string(*elem),
1781 gnc_numeric_check(*elem),
1782 gnc_num_dbg_to_string(*amount));
1783 return;
1784 }
1785
1786 /* In case anyone wants to see this in the debug log. */
1787 DEBUG("Adding to guid [%s] the value [%s]. Value now [%s].",
1788 guidstr,
1789 gnc_num_dbg_to_string(*amount),
1790 gnc_num_dbg_to_string(*elem));
1791}
1792
1793static gboolean
1794create_cashflow_helper(Transaction *template_txn, void *user_data)
1795{
1796 SxCashflowData *creation_data = user_data;
1797 GList *template_splits;
1798 const gnc_commodity *first_cmdty = NULL;
1799
1800 DEBUG("Evaluating txn desc [%s] for sx [%s]",
1801 xaccTransGetDescription(template_txn),
1802 xaccSchedXactionGetName(creation_data->sx));
1803
1804 template_splits = xaccTransGetSplitList(template_txn);
1805
1806 if (template_splits == NULL)
1807 {
1808 g_critical("transaction w/o splits for sx [%s]",
1809 xaccSchedXactionGetName(creation_data->sx));
1810 return FALSE;
1811 }
1812
1813 for (;
1814 template_splits;
1815 template_splits = template_splits->next)
1816 {
1817 Account *split_acct;
1818 const gnc_commodity *split_cmdty = NULL;
1819 const Split *template_split = (const Split*) template_splits->data;
1820
1821 /* Get the account that should be used for this split. */
1822 if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors))
1823 {
1824 DEBUG("Could not find account for split");
1825 break;
1826 }
1827
1828 /* The split's account also has some commodity */
1829 split_cmdty = xaccAccountGetCommodity(split_acct);
1830 if (first_cmdty == NULL)
1831 {
1832 first_cmdty = split_cmdty;
1833 //xaccTransSetCurrency(new_txn, first_cmdty);
1834 }
1835
1836 {
1837 gnc_numeric credit_num = gnc_numeric_zero();
1838 gnc_numeric debit_num = gnc_numeric_zero();
1839 gnc_numeric final_once, final;
1840 gint gncn_error;
1841
1842 /* Credit value */
1843 _get_sx_formula_value(creation_data->sx, template_split,
1844 &credit_num, creation_data->creation_errors,
1845 "sx-credit-formula", "sx-credit-numeric",
1846 NULL);
1847 /* Debit value */
1848 _get_sx_formula_value(creation_data->sx, template_split,
1849 &debit_num, creation_data->creation_errors,
1850 "sx-debit-formula", "sx-debit-numeric", NULL);
1851
1852 /* The resulting cash flow number: debit minus credit,
1853 * multiplied with the count factor. */
1854 final_once = gnc_numeric_sub_fixed( debit_num, credit_num );
1855 /* Multiply with the count factor. */
1856 final = gnc_numeric_mul(final_once, creation_data->count,
1857 gnc_numeric_denom(final_once),
1858 GNC_HOW_RND_ROUND_HALF_UP);
1859
1860 gncn_error = gnc_numeric_check(final);
1861 if (gncn_error != GNC_ERROR_OK)
1862 {
1863 gchar* err = N_("Error %d in SX [%s] final gnc_numeric value, using 0 instead.");
1864 REPORT_ERROR(creation_data->creation_errors, err,
1865 gncn_error, xaccSchedXactionGetName(creation_data->sx));
1866 final = gnc_numeric_zero();
1867 }
1868
1869 /* Print error message if we would have needed an exchange rate */
1870 if (! gnc_commodity_equal(split_cmdty, first_cmdty))
1871 {
1872 gchar *err = N_("No exchange rate available in SX [%s] for %s -> %s, value is zero.");
1873 REPORT_ERROR(creation_data->creation_errors, err,
1874 xaccSchedXactionGetName(creation_data->sx),
1875 gnc_commodity_get_mnemonic(split_cmdty),
1876 gnc_commodity_get_mnemonic(first_cmdty));
1877 final = gnc_numeric_zero();
1878 }
1879
1880 /* And add the resulting value to the hash */
1881 add_to_hash_amount(creation_data->hash, xaccAccountGetGUID(split_acct), &final);
1882 }
1883 }
1884
1885 return FALSE;
1886}
1887
1888static void
1889instantiate_cashflow_internal(const SchedXaction* sx,
1890 GHashTable* map,
1891 GList **creation_errors, gint count)
1892{
1893 SxCashflowData create_cashflow_data;
1894 Account* sx_template_account = gnc_sx_get_template_transaction_account(sx);
1895
1896 if (!sx_template_account)
1897 {
1898 g_critical("Huh? No template account for the SX %s", xaccSchedXactionGetName(sx));
1899 return;
1900 }
1901
1902 if (!xaccSchedXactionGetEnabled(sx))
1903 {
1904 DEBUG("Skipping non-enabled SX [%s]",
1905 xaccSchedXactionGetName(sx));
1906 return;
1907 }
1908
1909 create_cashflow_data.hash = map;
1910 create_cashflow_data.creation_errors = creation_errors;
1911 create_cashflow_data.sx = sx;
1912 create_cashflow_data.count = gnc_numeric_create(count, 1);
1913
1914 /* The cash flow numbers are in the transactions of the template
1915 * account, so run this foreach on the transactions. */
1916 xaccAccountForEachTransaction(sx_template_account,
1917 create_cashflow_helper,
1918 &create_cashflow_data);
1919}
1920
1921typedef struct
1922{
1923 GHashTable *hash;
1924 GList **creation_errors;
1925 const GDate *range_start;
1926 const GDate *range_end;
1928
1929static void instantiate_cashflow_cb(gpointer data, gpointer _user_data)
1930{
1931 const SchedXaction* sx = (const SchedXaction*) data;
1932 SxAllCashflow* userdata = (SxAllCashflow*) _user_data;
1933 gint count;
1934
1935 g_assert(sx);
1936 g_assert(userdata);
1937
1938 /* How often does this particular SX occur in the date range? */
1939 count = gnc_sx_get_num_occur_daterange(sx, userdata->range_start,
1940 userdata->range_end);
1941 if (count > 0)
1942 {
1943 /* If it occurs at least once, calculate ("instantiate") its
1944 * cash flow and add it to the result
1945 * g_hash<GUID,gnc_numeric> */
1946 instantiate_cashflow_internal(sx,
1947 userdata->hash,
1948 userdata->creation_errors,
1949 count);
1950 }
1951}
1952
1953void gnc_sx_all_instantiate_cashflow(GList *all_sxes,
1954 const GDate *range_start, const GDate *range_end,
1955 GHashTable* map, GList **creation_errors)
1956{
1957 SxAllCashflow userdata;
1958 userdata.hash = map;
1959 userdata.creation_errors = creation_errors;
1960 userdata.range_start = range_start;
1961 userdata.range_end = range_end;
1962
1963 /* The work is done in the callback for each SX */
1964 g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata);
1965}
1966
1967
1968GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_end)
1969{
1970 GHashTable *result_map = gnc_g_hash_new_guid_numeric();
1971 GList *all_sxes = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
1972 gnc_sx_all_instantiate_cashflow(all_sxes,
1973 &range_start, &range_end,
1974 result_map, NULL);
1975 return result_map;
1976}
1977
1978GList *gnc_sx_instance_model_get_sx_instances_list (GncSxInstanceModel *model)
1979{
1980 return model->sx_instance_list;
1981}
Account handling public routines.
Anchor Scheduled Transaction info in a book.
Scheduled Transactions public handling routines.
convert single-entry accounts to clean double-entry
API for Transactions and Splits (journal entries)
API for Transactions and Splits (journal entries)
Commodity handling public routines.
Date and Time handling routines.
Additional event handling code.
GLib helper routines.
utility functions for the GnuCash UI
#define xaccAccountGetGUID(X)
Definition Account.h:252
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
Definition Account.cpp:2050
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
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.
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
void xaccTransSetDate(Transaction *trans, int day, int mon, int year)
The xaccTransSetDate() method does the same thing as xaccTransSetDate[Posted]Secs(),...
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
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:3093
#define xaccTransGetGUID(X)
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
Transaction * xaccTransCloneNoKvp(const Transaction *from)
The xaccTransCloneNoKvp() method will create a complete copy of an existing transaction except that t...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
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:5150
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition qofevent.cpp:103
void qof_event_resume(void)
Resume engine event generation.
Definition qofevent.cpp:156
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition qofevent.cpp:73
void qof_event_suspend(void)
Suspend all engine events.
Definition qofevent.cpp:145
gint QofEventId
Define the type of events allowed.
Definition qofevent.h:45
#define GNC_EVENT_ITEM_ADDED
These events are used when a split is added to an account.
Definition gnc-event.h:45
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
Definition guid.cpp:255
gint guid_g_hash_table_equal(gconstpointer guid_a, gconstpointer guid_b)
Equality function for two GUIDs in a GHashTable.
Definition guid.cpp:269
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:208
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...
GList * gnc_g_list_map(GList *list, GncGMapFunc fn, gpointer user_data)
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.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
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 ...
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
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...
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_ERROR_OK
No error.
@ GNC_ERROR_ARG
Argument is not a valid number.
#define xaccSchedXactionGetGUID(X)
SXTmpStateData * gnc_sx_clone_temporal_state(SXTmpStateData *tsd)
Allocates and returns a one-by-one copy of the given temporal state.
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.
GDate xaccSchedXactionGetNextInstance(const SchedXaction *sx, SXTmpStateData *tsd)
Returns the next occurrence of a scheduled transaction.
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).
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
Definition SX-book.cpp:65
void gnc_sx_remove_defer_instance(SchedXaction *sx, void *deferStateData)
Removes an instance from the deferred list.
void gnc_sx_add_defer_instance(SchedXaction *sx, void *deferStateData)
Adds an instance to the deferred list of the SX.
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.
void gnc_sx_destroy_temporal_state(SXTmpStateData *tsd)
Frees the given stateDate object.
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.
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
void xaccSplitScrub(Split *split)
The xaccSplitScrub method ensures that if this split has the same commodity and currency,...
Definition Scrub.cpp:424
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
GncSxInstanceState orig_state
the original state at generation time.
SXTmpStateData * temporal_state
the sx creation temporal state.
GHashTable * variable_bindings
variable bindings.
GDate date
the instance date.
GncSxInstanceState state
the current state of the instance (during editing)
GncSxInstances * parent
the parent instances collection.
GList * instance_list
GList<GncSxInstance*>
GHashTable * variable_names
<name:char*,GncSxVariable*>
gint num_auto_create_no_notify_instances
The number of automatically-created instances that do no request notification.
gint num_to_create_instances
The number of (not-auto-create) to-create instances.
gboolean need_dialog
If the dialog needs to be displayed.
gint num_instances
The number of total instances (in any state).
gint num_auto_create_instances
The total number of auto-create instances.
gnc_numeric value
only numeric values are supported.
Just the variable temporal bits from the SX structure.