Branch data Line data Source code
1 : : /********************************************************************\
2 : : * SchedXaction.c -- Scheduled Transaction implementation. *
3 : : * Copyright (C) 2001,2007 Joshua Sled <jsled@asynchronous.org> *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : \********************************************************************/
23 : :
24 : : #include <config.h>
25 : :
26 : : #include <glib.h>
27 : : #include <glib/gi18n.h>
28 : : #include <string.h>
29 : : #include <stdint.h>
30 : :
31 : : #include "qof.h"
32 : :
33 : : #include "Account.h"
34 : : #include "Account.hpp"
35 : : #include "SX-book.h"
36 : : #include "SX-book-p.h"
37 : : #include "SX-ttinfo.hpp"
38 : : #include "SchedXaction.h"
39 : : #include "SchedXaction.hpp"
40 : : #include "Transaction.h"
41 : : #include "gnc-engine.h"
42 : : #include "engine-helpers.h"
43 : : #include "qofinstance-p.h"
44 : :
45 : : #include <unordered_set>
46 : :
47 : : #undef G_LOG_DOMAIN
48 : : #define G_LOG_DOMAIN "gnc.engine.sx"
49 : :
50 : : enum
51 : : {
52 : : PROP_0,
53 : : PROP_NAME, /* Table */
54 : : PROP_ENABLED, /* Table */
55 : : PROP_START_DATE, /* Table */
56 : : PROP_END_DATE, /* Table */
57 : : PROP_LAST_OCCURANCE_DATE, /* Table */
58 : : PROP_NUM_OCCURANCE, /* Table */
59 : : PROP_REM_OCCURANCE, /* Table */
60 : : PROP_AUTO_CREATE, /* Table */
61 : : PROP_AUTO_CREATE_NOTIFY, /* Table */
62 : : PROP_ADVANCE_CREATION_DAYS, /* Table */
63 : : PROP_ADVANCE_REMINDER_DAYS, /* Table */
64 : : PROP_INSTANCE_COUNT, /* Table */
65 : : PROP_TEMPLATE_ACCOUNT /* Table */
66 : : };
67 : :
68 : : /* GObject initialization */
69 : 270 : G_DEFINE_TYPE(SchedXaction, gnc_schedxaction, QOF_TYPE_INSTANCE)
70 : :
71 : : static void
72 : 24 : gnc_schedxaction_init(SchedXaction* sx)
73 : : {
74 : 24 : sx->schedule = NULL;
75 : :
76 : 24 : g_date_clear( &sx->last_date, 1 );
77 : 24 : g_date_clear( &sx->start_date, 1 );
78 : 24 : g_date_clear( &sx->end_date, 1 );
79 : :
80 : 24 : sx->enabled = 1;
81 : 24 : sx->num_occurances_total = 0;
82 : 24 : sx->autoCreateOption = FALSE;
83 : 24 : sx->autoCreateNotify = FALSE;
84 : 24 : sx->advanceCreateDays = 0;
85 : 24 : sx->advanceRemindDays = 0;
86 : 24 : sx->instance_num = 0;
87 : 24 : sx->deferredList = NULL;
88 : 24 : }
89 : :
90 : : static void
91 : 8 : gnc_schedxaction_dispose(GObject *sxp)
92 : : {
93 : 8 : G_OBJECT_CLASS(gnc_schedxaction_parent_class)->dispose(sxp);
94 : 8 : }
95 : :
96 : : static void
97 : 8 : gnc_schedxaction_finalize(GObject* sxp)
98 : : {
99 : 8 : G_OBJECT_CLASS(gnc_schedxaction_parent_class)->finalize(sxp);
100 : 8 : }
101 : :
102 : : /* Note that g_value_set_object() refs the object, as does
103 : : * g_object_get(). But g_object_get() only unrefs once when it disgorges
104 : : * the object, leaving an unbalanced ref, which leaks. So instead of
105 : : * using g_value_set_object(), use g_value_take_object() which doesn't
106 : : * ref the object when used in get_property().
107 : : */
108 : : static void
109 : 42 : gnc_schedxaction_get_property (GObject *object,
110 : : guint prop_id,
111 : : GValue *value,
112 : : GParamSpec *pspec)
113 : : {
114 : : SchedXaction *sx;
115 : :
116 : 42 : g_return_if_fail(GNC_IS_SCHEDXACTION(object));
117 : :
118 : 42 : sx = GNC_SCHEDXACTION(object);
119 : 42 : switch (prop_id)
120 : : {
121 : 3 : case PROP_NAME:
122 : 3 : g_value_set_string(value, sx->name);
123 : 3 : break;
124 : 3 : case PROP_ENABLED:
125 : 3 : g_value_set_boolean(value, sx->enabled);
126 : 3 : break;
127 : 3 : case PROP_NUM_OCCURANCE:
128 : 3 : g_value_set_int(value, sx->num_occurances_total);
129 : 3 : break;
130 : 3 : case PROP_REM_OCCURANCE:
131 : 3 : g_value_set_int(value, sx->num_occurances_remain);
132 : 3 : break;
133 : 3 : case PROP_AUTO_CREATE:
134 : 3 : g_value_set_boolean(value, sx->autoCreateOption);
135 : 3 : break;
136 : 3 : case PROP_AUTO_CREATE_NOTIFY:
137 : 3 : g_value_set_boolean(value, sx->autoCreateNotify);
138 : 3 : break;
139 : 3 : case PROP_ADVANCE_CREATION_DAYS:
140 : 3 : g_value_set_int(value, sx->advanceCreateDays);
141 : 3 : break;
142 : 3 : case PROP_ADVANCE_REMINDER_DAYS:
143 : 3 : g_value_set_int(value, sx->advanceRemindDays);
144 : 3 : break;
145 : 6 : case PROP_START_DATE:
146 : 6 : g_value_set_boxed(value, &sx->start_date);
147 : 6 : break;
148 : 3 : case PROP_END_DATE:
149 : : /* g_value_set_boxed raises a critical error if sx->end_date
150 : : * is invalid */
151 : 3 : if (g_date_valid (&sx->end_date))
152 : 1 : g_value_set_boxed(value, &sx->end_date);
153 : 3 : break;
154 : 3 : case PROP_LAST_OCCURANCE_DATE:
155 : : /* g_value_set_boxed raises a critical error if sx->last_date
156 : : * is invalid */
157 : 3 : if (g_date_valid (&sx->last_date))
158 : 0 : g_value_set_boxed(value, &sx->last_date);
159 : 3 : break;
160 : 3 : case PROP_INSTANCE_COUNT:
161 : 3 : g_value_set_int(value, sx->instance_num);
162 : 3 : break;
163 : 3 : case PROP_TEMPLATE_ACCOUNT:
164 : 3 : g_value_take_object(value, sx->template_acct);
165 : 3 : break;
166 : 0 : default:
167 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
168 : 0 : break;
169 : : }
170 : : }
171 : :
172 : : static void
173 : 34 : gnc_schedxaction_set_property (GObject *object,
174 : : guint prop_id,
175 : : const GValue *value,
176 : : GParamSpec *pspec)
177 : : {
178 : : SchedXaction *sx;
179 : :
180 : 34 : g_return_if_fail(GNC_IS_SCHEDXACTION(object));
181 : :
182 : 34 : sx = GNC_SCHEDXACTION(object);
183 : 34 : g_assert (qof_instance_get_editlevel(sx));
184 : :
185 : 34 : switch (prop_id)
186 : : {
187 : 3 : case PROP_NAME:
188 : 3 : xaccSchedXactionSetName(sx, g_value_get_string(value));
189 : 3 : break;
190 : 3 : case PROP_ENABLED:
191 : 3 : xaccSchedXactionSetEnabled(sx, g_value_get_boolean(value));
192 : 3 : break;
193 : 3 : case PROP_NUM_OCCURANCE:
194 : 3 : xaccSchedXactionSetNumOccur(sx, g_value_get_int(value));
195 : 3 : break;
196 : 3 : case PROP_REM_OCCURANCE:
197 : 3 : xaccSchedXactionSetRemOccur(sx, g_value_get_int(value));
198 : 3 : break;
199 : 3 : case PROP_AUTO_CREATE:
200 : 3 : xaccSchedXactionSetAutoCreate(sx, g_value_get_boolean(value), sx->autoCreateNotify);
201 : 3 : break;
202 : 3 : case PROP_AUTO_CREATE_NOTIFY:
203 : 3 : xaccSchedXactionSetAutoCreate(sx, sx->autoCreateOption, g_value_get_boolean(value));
204 : 3 : break;
205 : 3 : case PROP_ADVANCE_CREATION_DAYS:
206 : 3 : xaccSchedXactionSetAdvanceCreation(sx, g_value_get_int(value));
207 : 3 : break;
208 : 3 : case PROP_ADVANCE_REMINDER_DAYS:
209 : 3 : xaccSchedXactionSetAdvanceReminder(sx, g_value_get_int(value));
210 : 3 : break;
211 : 3 : case PROP_START_DATE:
212 : : /* Note: when passed through a boxed gvalue, the julian value of the date is copied.
213 : : The date may appear invalid until a function requiring for dmy calculation is
214 : : called. */
215 : 3 : xaccSchedXactionSetStartDate(sx, static_cast<const GDate*>(g_value_get_boxed(value)));
216 : 3 : break;
217 : 1 : case PROP_END_DATE:
218 : : /* Note: when passed through a boxed gvalue, the julian value of the date is copied.
219 : : The date may appear invalid until a function requiring for dmy calculation is
220 : : called. */
221 : 1 : xaccSchedXactionSetEndDate(sx, static_cast<const GDate*>(g_value_get_boxed(value)));
222 : 1 : break;
223 : 0 : case PROP_LAST_OCCURANCE_DATE:
224 : : /* Note: when passed through a boxed gvalue, the julian value of the date is copied.
225 : : The date may appear invalid until a function requiring for dmy calculation is
226 : : called. */
227 : 0 : xaccSchedXactionSetLastOccurDate(sx, static_cast<const GDate*>(g_value_get_boxed(value)));
228 : 0 : break;
229 : 3 : case PROP_INSTANCE_COUNT:
230 : 3 : gnc_sx_set_instance_count(sx, g_value_get_int(value));
231 : 3 : break;
232 : 3 : case PROP_TEMPLATE_ACCOUNT:
233 : 3 : sx_set_template_account(sx, GNC_ACCOUNT(g_value_get_object(value)));
234 : 3 : break;
235 : 0 : default:
236 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
237 : 0 : break;
238 : : }
239 : : }
240 : :
241 : : static void
242 : 3 : gnc_schedxaction_class_init (SchedXactionClass *klass)
243 : : {
244 : 3 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
245 : :
246 : 3 : gobject_class->dispose = gnc_schedxaction_dispose;
247 : 3 : gobject_class->finalize = gnc_schedxaction_finalize;
248 : 3 : gobject_class->set_property = gnc_schedxaction_set_property;
249 : 3 : gobject_class->get_property = gnc_schedxaction_get_property;
250 : :
251 : : g_object_class_install_property
252 : 3 : (gobject_class,
253 : : PROP_NAME,
254 : : g_param_spec_string ("name",
255 : : "Scheduled Transaction Name",
256 : : "The name is an arbitrary string "
257 : : "assigned by the user. It is intended to "
258 : : "a short, 5 to 30 character long string "
259 : : "that is displayed by the GUI.",
260 : : NULL,
261 : : G_PARAM_READWRITE));
262 : :
263 : : g_object_class_install_property
264 : 3 : (gobject_class,
265 : : PROP_ENABLED,
266 : : g_param_spec_boolean ("enabled",
267 : : "Enabled",
268 : : "TRUE if the scheduled transaction is enabled.",
269 : : TRUE,
270 : : G_PARAM_READWRITE));
271 : :
272 : : g_object_class_install_property
273 : 3 : (gobject_class,
274 : : PROP_NUM_OCCURANCE,
275 : : g_param_spec_int ("num-occurance",
276 : : "Number of occurrences",
277 : : "Total number of occurrences for this scheduled transaction.",
278 : : 0,
279 : : G_MAXINT16,
280 : : 1,
281 : : G_PARAM_READWRITE));
282 : :
283 : : g_object_class_install_property
284 : 3 : (gobject_class,
285 : : PROP_REM_OCCURANCE,
286 : : g_param_spec_int ("rem-occurance",
287 : : "Number of occurrences remaining",
288 : : "Remaining number of occurrences for this scheduled transaction.",
289 : : 0,
290 : : G_MAXINT16,
291 : : 1,
292 : : G_PARAM_READWRITE));
293 : :
294 : : g_object_class_install_property
295 : 3 : (gobject_class,
296 : : PROP_AUTO_CREATE,
297 : : g_param_spec_boolean ("auto-create",
298 : : "Auto-create",
299 : : "TRUE if the transaction will be automatically "
300 : : "created when its time comes.",
301 : : FALSE,
302 : : G_PARAM_READWRITE));
303 : :
304 : : g_object_class_install_property
305 : 3 : (gobject_class,
306 : : PROP_AUTO_CREATE_NOTIFY,
307 : : g_param_spec_boolean ("auto-create-notify",
308 : : "Auto-create-notify",
309 : : "TRUE if the the user will be notified when the transaction "
310 : : "is automatically created.",
311 : : FALSE,
312 : : G_PARAM_READWRITE));
313 : :
314 : : g_object_class_install_property
315 : 3 : (gobject_class,
316 : : PROP_ADVANCE_CREATION_DAYS,
317 : : g_param_spec_int ("advance-creation-days",
318 : : "Days in advance to create",
319 : : "Number of days in advance to create this scheduled transaction.",
320 : : 0,
321 : : G_MAXINT16,
322 : : 0,
323 : : G_PARAM_READWRITE));
324 : :
325 : : g_object_class_install_property
326 : 3 : (gobject_class,
327 : : PROP_ADVANCE_REMINDER_DAYS,
328 : : g_param_spec_int ("advance-reminder-days",
329 : : "Days in advance to remind",
330 : : "Number of days in advance to remind about this scheduled transaction.",
331 : : 0,
332 : : G_MAXINT16,
333 : : 0,
334 : : G_PARAM_READWRITE));
335 : :
336 : : g_object_class_install_property
337 : 3 : (gobject_class,
338 : : PROP_START_DATE,
339 : : g_param_spec_boxed("start-date",
340 : : "Start Date",
341 : : "Date for the first occurrence for the scheduled transaction.",
342 : : G_TYPE_DATE,
343 : : G_PARAM_READWRITE));
344 : :
345 : : g_object_class_install_property
346 : 3 : (gobject_class,
347 : : PROP_END_DATE,
348 : : g_param_spec_boxed("end-date",
349 : : "End Date",
350 : : "Date for the scheduled transaction to end.",
351 : : G_TYPE_DATE,
352 : : G_PARAM_READWRITE));
353 : :
354 : : g_object_class_install_property
355 : 3 : (gobject_class,
356 : : PROP_LAST_OCCURANCE_DATE,
357 : : g_param_spec_boxed("last-occurance-date",
358 : : "Last Occurrence Date",
359 : : "Date for the last occurrence of the scheduled transaction.",
360 : : G_TYPE_DATE,
361 : : G_PARAM_READWRITE));
362 : :
363 : : g_object_class_install_property
364 : 3 : (gobject_class,
365 : : PROP_INSTANCE_COUNT,
366 : : g_param_spec_int ("instance-count",
367 : : "Instance count",
368 : : "Number of instances of this scheduled transaction.",
369 : : 0,
370 : : G_MAXINT16,
371 : : 0,
372 : : G_PARAM_READWRITE));
373 : :
374 : : g_object_class_install_property
375 : 3 : (gobject_class,
376 : : PROP_TEMPLATE_ACCOUNT,
377 : : g_param_spec_object("template-account",
378 : : "Template account",
379 : : "Account which holds the template transactions.",
380 : : GNC_TYPE_ACCOUNT,
381 : : G_PARAM_READWRITE));
382 : 3 : }
383 : :
384 : : static void
385 : 24 : xaccSchedXactionInit(SchedXaction *sx, QofBook *book)
386 : : {
387 : : Account *ra;
388 : : const GncGUID *guid;
389 : : gchar guidstr[GUID_ENCODING_LENGTH+1];
390 : :
391 : 24 : qof_instance_init_data (&sx->inst, GNC_ID_SCHEDXACTION, book);
392 : :
393 : : /* create a new template account for our splits */
394 : 24 : sx->template_acct = xaccMallocAccount(book);
395 : 24 : guid = qof_instance_get_guid( sx );
396 : 24 : xaccAccountBeginEdit( sx->template_acct );
397 : 24 : guid_to_string_buff( guid, guidstr );
398 : 24 : xaccAccountSetName( sx->template_acct, guidstr);
399 : : xaccAccountSetCommodity
400 : 24 : (sx->template_acct,
401 : 24 : gnc_commodity_table_lookup( gnc_commodity_table_get_table(book),
402 : : GNC_COMMODITY_NS_TEMPLATE, "template") );
403 : 24 : xaccAccountSetType( sx->template_acct, ACCT_TYPE_BANK );
404 : 24 : xaccAccountCommitEdit( sx->template_acct );
405 : 24 : ra = gnc_book_get_template_root( book );
406 : 24 : gnc_account_append_child( ra, sx->template_acct );
407 : 24 : }
408 : :
409 : : SchedXaction*
410 : 24 : xaccSchedXactionMalloc(QofBook *book)
411 : : {
412 : : SchedXaction *sx;
413 : :
414 : 24 : g_return_val_if_fail (book, NULL);
415 : :
416 : 24 : sx = GNC_SX(g_object_new(GNC_TYPE_SCHEDXACTION, NULL));
417 : 24 : xaccSchedXactionInit( sx, book );
418 : 24 : qof_event_gen( &sx->inst, QOF_EVENT_CREATE , NULL);
419 : :
420 : 24 : return sx;
421 : : }
422 : :
423 : : static void
424 : 12 : delete_template_trans(SchedXaction *sx)
425 : : {
426 : 12 : std::unordered_set<Transaction*> txns;
427 : 12 : auto& splits{xaccAccountGetSplits (sx->template_acct)};
428 : 12 : std::for_each (splits.begin(), splits.end(),
429 : 0 : [&txns](auto s){ txns.insert (xaccSplitGetParent (s)); });
430 : 12 : std::for_each (txns.begin(), txns.end(),
431 : 0 : [](auto t)
432 : : {
433 : 0 : xaccTransBeginEdit (t);
434 : 0 : xaccTransDestroy (t);
435 : 0 : xaccTransCommitEdit (t);
436 : 0 : });
437 : 24 : return;
438 : 12 : }
439 : :
440 : : void
441 : 8 : sx_set_template_account (SchedXaction *sx, Account *account)
442 : : {
443 : : Account *old;
444 : :
445 : 8 : old = sx->template_acct;
446 : 8 : sx->template_acct = account;
447 : 8 : if (old)
448 : : {
449 : 8 : xaccAccountBeginEdit(old);
450 : 8 : xaccAccountDestroy(old);
451 : : }
452 : 8 : }
453 : :
454 : : void
455 : 8 : xaccSchedXactionDestroy( SchedXaction *sx )
456 : : {
457 : 8 : qof_instance_set_destroying( QOF_INSTANCE(sx), TRUE );
458 : 8 : gnc_sx_commit_edit( sx );
459 : 8 : }
460 : :
461 : : static void
462 : 8 : xaccSchedXactionFree( SchedXaction *sx )
463 : : {
464 : 8 : if ( sx == NULL ) return;
465 : :
466 : 8 : qof_event_gen( &sx->inst, QOF_EVENT_DESTROY , NULL);
467 : :
468 : 8 : if ( sx->name )
469 : 8 : g_free( sx->name );
470 : :
471 : : /*
472 : : * we have to delete the transactions in the
473 : : * template account ourselves
474 : : */
475 : :
476 : 8 : delete_template_trans( sx );
477 : :
478 : 8 : xaccAccountBeginEdit( sx->template_acct );
479 : 8 : xaccAccountDestroy( sx->template_acct );
480 : :
481 : 8 : g_list_free_full (sx->deferredList, g_free);
482 : :
483 : : /* a GList of Recurrences */
484 : 8 : g_list_free_full (sx->schedule, g_free);
485 : :
486 : : /* qof_instance_release (&sx->inst); */
487 : 8 : g_object_unref( sx );
488 : : }
489 : :
490 : : /* ============================================================ */
491 : :
492 : : void
493 : 145 : gnc_sx_begin_edit (SchedXaction *sx)
494 : : {
495 : 145 : qof_begin_edit (&sx->inst);
496 : 145 : }
497 : :
498 : 8 : static void sx_free(QofInstance* inst )
499 : : {
500 : 8 : xaccSchedXactionFree( GNC_SX(inst) );
501 : 8 : }
502 : :
503 : 0 : static void commit_err (QofInstance *inst, QofBackendError errcode)
504 : : {
505 : 0 : g_critical("Failed to commit: %d", errcode);
506 : 0 : gnc_engine_signal_commit_error( errcode );
507 : 0 : }
508 : :
509 : 107 : static void commit_done(QofInstance *inst)
510 : : {
511 : 107 : qof_event_gen (inst, QOF_EVENT_MODIFY, NULL);
512 : 107 : }
513 : :
514 : : void
515 : 145 : gnc_sx_commit_edit (SchedXaction *sx)
516 : : {
517 : 145 : if (!qof_commit_edit (QOF_INSTANCE(sx))) return;
518 : 115 : qof_commit_edit_part2 (&sx->inst, commit_err, commit_done, sx_free);
519 : : }
520 : :
521 : : /* ============================================================ */
522 : :
523 : : GList*
524 : 5 : gnc_sx_get_schedule(const SchedXaction *sx)
525 : : {
526 : 5 : return sx->schedule;
527 : : }
528 : :
529 : : void
530 : 24 : gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
531 : : {
532 : 24 : g_return_if_fail(sx);
533 : 24 : gnc_sx_begin_edit(sx);
534 : 24 : sx->schedule = schedule;
535 : 24 : qof_instance_set_dirty(&sx->inst);
536 : 24 : gnc_sx_commit_edit(sx);
537 : : }
538 : :
539 : : gchar *
540 : 7 : xaccSchedXactionGetName( const SchedXaction *sx )
541 : : {
542 : 7 : return sx->name;
543 : : }
544 : :
545 : : void
546 : 24 : xaccSchedXactionSetName( SchedXaction *sx, const gchar *newName )
547 : : {
548 : 24 : g_return_if_fail( newName != NULL );
549 : 24 : gnc_sx_begin_edit(sx);
550 : 24 : if ( sx->name != NULL )
551 : : {
552 : 0 : g_free( sx->name );
553 : 0 : sx->name = NULL;
554 : : }
555 : 24 : sx->name = g_strdup( newName );
556 : 24 : qof_instance_set_dirty(&sx->inst);
557 : 24 : gnc_sx_commit_edit(sx);
558 : : }
559 : :
560 : : const GDate*
561 : 2 : xaccSchedXactionGetStartDate(const SchedXaction *sx )
562 : : {
563 : 2 : g_assert (sx);
564 : 2 : return &sx->start_date;
565 : : }
566 : :
567 : : time64
568 : 0 : xaccSchedXactionGetStartDateTT(const SchedXaction *sx )
569 : : {
570 : 0 : g_assert (sx);
571 : 0 : return gdate_to_time64(sx->start_date);
572 : : }
573 : :
574 : : void
575 : 24 : xaccSchedXactionSetStartDate( SchedXaction *sx, const GDate* newStart )
576 : : {
577 : 24 : if ( newStart == NULL || !g_date_valid( newStart ))
578 : : {
579 : : /* XXX: I reject the bad data - is this the right
580 : : * thing to do <rgmerk>.
581 : : * This warning is only human readable - the caller
582 : : * doesn't know the call failed. This is bad
583 : : */
584 : 0 : g_critical("Invalid Start Date");
585 : 0 : return;
586 : : }
587 : 24 : gnc_sx_begin_edit(sx);
588 : 24 : sx->start_date = *newStart;
589 : 24 : qof_instance_set_dirty(&sx->inst);
590 : 24 : gnc_sx_commit_edit(sx);
591 : : }
592 : :
593 : : void
594 : 0 : xaccSchedXactionSetStartDateTT( SchedXaction *sx, const time64 newStart )
595 : : {
596 : 0 : if ( newStart == INT64_MAX )
597 : : {
598 : : /* XXX: I reject the bad data - is this the right
599 : : * thing to do <rgmerk>.
600 : : * This warning is only human readable - the caller
601 : : * doesn't know the call failed. This is bad
602 : : */
603 : 0 : g_critical("Invalid Start Date");
604 : 0 : return;
605 : : }
606 : 0 : gnc_sx_begin_edit(sx);
607 : 0 : gnc_gdate_set_time64(&sx->start_date, newStart);
608 : 0 : qof_instance_set_dirty(&sx->inst);
609 : 0 : gnc_sx_commit_edit(sx);
610 : : }
611 : :
612 : : gboolean
613 : 89 : xaccSchedXactionHasEndDate( const SchedXaction *sx )
614 : : {
615 : 89 : return sx != NULL && g_date_valid( &sx->end_date );
616 : : }
617 : :
618 : : const GDate*
619 : 29 : xaccSchedXactionGetEndDate(const SchedXaction *sx )
620 : : {
621 : 29 : g_assert (sx);
622 : 29 : return &sx->end_date;
623 : : }
624 : :
625 : : void
626 : 3 : xaccSchedXactionSetEndDate( SchedXaction *sx, const GDate *newEnd )
627 : : {
628 : : /* Note that an invalid GDate IS a permissible value: It means that
629 : : * the SX is to run "forever". See gnc_sxed_save_sx() and
630 : : * schedXact_editor_populate() in dialog-sx-editor.c.
631 : : */
632 : 6 : if (newEnd == NULL ||
633 : 3 : (g_date_valid(newEnd) && g_date_compare( newEnd, &sx->start_date ) < 0 ))
634 : : {
635 : : /* XXX: I reject the bad data - is this the right
636 : : * thing to do <rgmerk>.
637 : : * This warning is only human readable - the caller
638 : : * doesn't know the call failed. This is bad
639 : : */
640 : 0 : g_critical("Bad End Date: Invalid or before Start Date");
641 : 0 : return;
642 : : }
643 : :
644 : 3 : gnc_sx_begin_edit(sx);
645 : 3 : sx->end_date = *newEnd;
646 : 3 : qof_instance_set_dirty(&sx->inst);
647 : 3 : gnc_sx_commit_edit(sx);
648 : : }
649 : :
650 : : const GDate*
651 : 6 : xaccSchedXactionGetLastOccurDate(const SchedXaction *sx )
652 : : {
653 : 6 : return &sx->last_date;
654 : : }
655 : :
656 : : time64
657 : 0 : xaccSchedXactionGetLastOccurDateTT(const SchedXaction *sx )
658 : : {
659 : 0 : return gdate_to_time64(sx->last_date);
660 : : }
661 : :
662 : : void
663 : 6 : xaccSchedXactionSetLastOccurDate(SchedXaction *sx, const GDate* new_last_occur)
664 : : {
665 : 6 : g_return_if_fail (new_last_occur != NULL);
666 : 6 : if (g_date_valid(&sx->last_date)
667 : 6 : && g_date_compare(&sx->last_date, new_last_occur) == 0)
668 : 0 : return;
669 : 6 : gnc_sx_begin_edit(sx);
670 : 6 : sx->last_date = *new_last_occur;
671 : 6 : qof_instance_set_dirty(&sx->inst);
672 : 6 : gnc_sx_commit_edit(sx);
673 : : }
674 : :
675 : : void
676 : 0 : xaccSchedXactionSetLastOccurDateTT(SchedXaction *sx, time64 new_last_occur)
677 : : {
678 : : GDate last_occur;
679 : 0 : g_return_if_fail (new_last_occur != INT64_MAX);
680 : 0 : gnc_gdate_set_time64(&last_occur, new_last_occur);
681 : 0 : if (g_date_valid(&sx->last_date)
682 : 0 : && g_date_compare(&sx->last_date, &last_occur) == 0)
683 : 0 : return;
684 : 0 : gnc_sx_begin_edit(sx);
685 : 0 : sx->last_date = last_occur;
686 : 0 : qof_instance_set_dirty(&sx->inst);
687 : 0 : gnc_sx_commit_edit(sx);
688 : : }
689 : :
690 : : gboolean
691 : 119 : xaccSchedXactionHasOccurDef( const SchedXaction *sx )
692 : : {
693 : 119 : return ( xaccSchedXactionGetNumOccur( sx ) != 0 );
694 : : }
695 : :
696 : : gint
697 : 119 : xaccSchedXactionGetNumOccur( const SchedXaction *sx )
698 : : {
699 : 119 : return sx->num_occurances_total;
700 : : }
701 : :
702 : : void
703 : 8 : xaccSchedXactionSetNumOccur(SchedXaction *sx, gint new_num)
704 : : {
705 : 8 : if (sx->num_occurances_total == new_num)
706 : 2 : return;
707 : 6 : gnc_sx_begin_edit(sx);
708 : 6 : sx->num_occurances_remain = sx->num_occurances_total = new_num;
709 : 6 : qof_instance_set_dirty(&sx->inst);
710 : 6 : gnc_sx_commit_edit(sx);
711 : : }
712 : :
713 : : gint
714 : 4 : xaccSchedXactionGetRemOccur( const SchedXaction *sx )
715 : : {
716 : 4 : return sx->num_occurances_remain;
717 : : }
718 : :
719 : : void
720 : 12 : xaccSchedXactionSetRemOccur(SchedXaction *sx, gint num_remain)
721 : : {
722 : : /* FIXME This condition can be tightened up */
723 : 12 : if (num_remain > sx->num_occurances_total)
724 : : {
725 : 0 : g_warning("number remaining [%d] > total occurrences [%d]",
726 : : num_remain, sx->num_occurances_total);
727 : : }
728 : : else
729 : : {
730 : 12 : if (num_remain == sx->num_occurances_remain)
731 : 6 : return;
732 : 6 : gnc_sx_begin_edit(sx);
733 : 6 : sx->num_occurances_remain = num_remain;
734 : 6 : qof_instance_set_dirty(&sx->inst);
735 : 6 : gnc_sx_commit_edit(sx);
736 : : }
737 : : }
738 : :
739 : 6 : gint gnc_sx_get_num_occur_daterange(const SchedXaction *sx, const GDate* start_date, const GDate* end_date)
740 : : {
741 : 6 : gint result = 0;
742 : : SXTmpStateData *tmpState;
743 : : gboolean countFirstDate;
744 : :
745 : : /* SX still active? If not, return now. */
746 : 6 : if ((xaccSchedXactionHasOccurDef(sx)
747 : 0 : && xaccSchedXactionGetRemOccur(sx) <= 0)
748 : 12 : || (xaccSchedXactionHasEndDate(sx)
749 : 6 : && g_date_compare(xaccSchedXactionGetEndDate(sx), start_date) < 0))
750 : : {
751 : 1 : return result;
752 : : }
753 : :
754 : 5 : tmpState = gnc_sx_create_temporal_state (sx);
755 : :
756 : : /* Should we count the first valid date we encounter? Only if the
757 : : * SX has not yet occurred so far, or if its last valid date was
758 : : * before the start date. */
759 : 5 : countFirstDate = !g_date_valid(&tmpState->last_date)
760 : 5 : || (g_date_compare(&tmpState->last_date, start_date) < 0);
761 : :
762 : : /* No valid date? SX has never occurred so far. */
763 : 5 : if (!g_date_valid(&tmpState->last_date))
764 : : {
765 : : /* SX has never occurred so far */
766 : 0 : gnc_sx_incr_temporal_state (sx, tmpState);
767 : 0 : if (xaccSchedXactionHasOccurDef(sx) && tmpState->num_occur_rem < 0)
768 : : {
769 : 0 : g_free (tmpState);
770 : 0 : return result;
771 : : }
772 : : }
773 : :
774 : : /* Increase the tmpState until we are in our interval of
775 : : * interest. Only calculate anything if the sx hasn't already
776 : : * ended. */
777 : 14 : while (g_date_compare(&tmpState->last_date, start_date) < 0)
778 : : {
779 : 9 : gnc_sx_incr_temporal_state (sx, tmpState);
780 : 9 : if (xaccSchedXactionHasOccurDef(sx) && tmpState->num_occur_rem < 0)
781 : : {
782 : 0 : g_free (tmpState);
783 : 0 : return result;
784 : : }
785 : : }
786 : :
787 : : /* Now we are in our interval of interest. Increment the
788 : : * occurrence date until we are beyond the end of our
789 : : * interval. Make sure to check for invalid dates here: It means
790 : : * the SX has ended. */
791 : 12 : while (g_date_valid(&tmpState->last_date)
792 : 9 : && (g_date_compare(&tmpState->last_date, end_date) <= 0)
793 : 7 : && (!xaccSchedXactionHasEndDate(sx)
794 : 7 : || g_date_compare(&tmpState->last_date, xaccSchedXactionGetEndDate(sx)) <= 0)
795 : 21 : && (!xaccSchedXactionHasOccurDef(sx)
796 : : /* The >=0 (i.e. the ==) is important here, otherwise
797 : : * we miss the last valid occurrence of a SX which is
798 : : * limited by num_occur */
799 : 0 : || tmpState->num_occur_rem >= 0))
800 : : {
801 : 7 : ++result;
802 : 7 : gnc_sx_incr_temporal_state (sx, tmpState);
803 : : }
804 : :
805 : : /* If the first valid date shouldn't be counted, decrease the
806 : : * result number by one. */
807 : 5 : if (!countFirstDate && result > 0)
808 : 0 : --result;
809 : :
810 : 5 : g_free (tmpState);
811 : 5 : return result;
812 : : }
813 : :
814 : : gboolean
815 : 16 : xaccSchedXactionGetEnabled( const SchedXaction *sx )
816 : : {
817 : 16 : return sx->enabled;
818 : : }
819 : :
820 : : void
821 : 3 : xaccSchedXactionSetEnabled( SchedXaction *sx, gboolean newEnabled)
822 : : {
823 : 3 : gnc_sx_begin_edit(sx);
824 : 3 : sx->enabled = newEnabled;
825 : 3 : qof_instance_set_dirty(&sx->inst);
826 : 3 : gnc_sx_commit_edit(sx);
827 : 3 : }
828 : :
829 : : void
830 : 8 : xaccSchedXactionGetAutoCreate( const SchedXaction *sx,
831 : : gboolean *outAutoCreate,
832 : : gboolean *outNotify )
833 : : {
834 : 8 : if (outAutoCreate != NULL)
835 : 8 : *outAutoCreate = sx->autoCreateOption;
836 : 8 : if (outNotify != NULL)
837 : 4 : *outNotify = sx->autoCreateNotify;
838 : 8 : return;
839 : : }
840 : :
841 : : void
842 : 10 : xaccSchedXactionSetAutoCreate( SchedXaction *sx,
843 : : gboolean newAutoCreate,
844 : : gboolean newNotify )
845 : : {
846 : :
847 : 10 : gnc_sx_begin_edit(sx);
848 : 10 : sx->autoCreateOption = newAutoCreate;
849 : 10 : sx->autoCreateNotify = newNotify;
850 : 10 : qof_instance_set_dirty(&sx->inst);
851 : 10 : gnc_sx_commit_edit(sx);
852 : 10 : return;
853 : : }
854 : :
855 : : gint
856 : 16 : xaccSchedXactionGetAdvanceCreation( const SchedXaction *sx )
857 : : {
858 : 16 : return sx->advanceCreateDays;
859 : : }
860 : :
861 : : void
862 : 8 : xaccSchedXactionSetAdvanceCreation( SchedXaction *sx, gint createDays )
863 : : {
864 : 8 : gnc_sx_begin_edit(sx);
865 : 8 : sx->advanceCreateDays = createDays;
866 : 8 : qof_instance_set_dirty(&sx->inst);
867 : 8 : gnc_sx_commit_edit(sx);
868 : 8 : }
869 : :
870 : : gint
871 : 16 : xaccSchedXactionGetAdvanceReminder( const SchedXaction *sx )
872 : : {
873 : 16 : return sx->advanceRemindDays;
874 : : }
875 : :
876 : : void
877 : 8 : xaccSchedXactionSetAdvanceReminder( SchedXaction *sx, gint reminderDays )
878 : : {
879 : 8 : gnc_sx_begin_edit(sx);
880 : 8 : sx->advanceRemindDays = reminderDays;
881 : 8 : qof_instance_set_dirty(&sx->inst);
882 : 8 : gnc_sx_commit_edit(sx);
883 : 8 : }
884 : :
885 : : GDate
886 : 74 : xaccSchedXactionGetNextInstance (const SchedXaction *sx, SXTmpStateData *tsd)
887 : : {
888 : : GDate prev_occur, next_occur;
889 : :
890 : 74 : g_date_clear( &prev_occur, 1 );
891 : 74 : if ( tsd != NULL )
892 : 74 : prev_occur = tsd->last_date;
893 : :
894 : : /* If prev_occur is in the "cleared" state and sx->start_date isn't, then
895 : : * we're at the beginning. We want to pretend prev_occur is the day before
896 : : * the start_date in case the start_date is today so that the SX will fire
897 : : * today. If start_date isn't valid either then the SX will fire anyway, no
898 : : * harm done. prev_occur cannot be before start_date either.
899 : : */
900 : 74 : if (g_date_valid (&sx->start_date) && (!g_date_valid ( &prev_occur ) || g_date_compare (&prev_occur, &sx->start_date)<0))
901 : : {
902 : : /* We must be at the beginning. */
903 : 37 : prev_occur = sx->start_date;
904 : 37 : g_date_subtract_days (&prev_occur, 1 );
905 : : }
906 : :
907 : 74 : recurrenceListNextInstance(sx->schedule, &prev_occur, &next_occur);
908 : :
909 : 74 : if ( xaccSchedXactionHasEndDate( sx ) )
910 : : {
911 : 16 : const GDate *end_date = xaccSchedXactionGetEndDate( sx );
912 : 16 : if ( g_date_compare( &next_occur, end_date ) > 0 )
913 : : {
914 : 3 : g_date_clear( &next_occur, 1 );
915 : : }
916 : : }
917 : 58 : else if ( xaccSchedXactionHasOccurDef( sx ) )
918 : : {
919 : 12 : if ((tsd && tsd->num_occur_rem == 0) ||
920 : 0 : (!tsd && sx->num_occurances_remain == 0 ))
921 : : {
922 : 4 : g_date_clear( &next_occur, 1 );
923 : : }
924 : : }
925 : 74 : return next_occur;
926 : : }
927 : :
928 : : gint
929 : 52 : gnc_sx_get_instance_count( const SchedXaction *sx, SXTmpStateData *stateData )
930 : : {
931 : 52 : gint toRet = -1;
932 : : SXTmpStateData *tsd;
933 : :
934 : 52 : if ( stateData )
935 : : {
936 : 46 : tsd = (SXTmpStateData*)stateData;
937 : 46 : toRet = tsd->num_inst;
938 : : }
939 : : else
940 : : {
941 : 6 : toRet = sx->instance_num;
942 : : }
943 : :
944 : 52 : return toRet;
945 : : }
946 : :
947 : : void
948 : 12 : gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
949 : : {
950 : 12 : g_return_if_fail(sx);
951 : 12 : if (sx->instance_num == instance_num)
952 : 0 : return;
953 : 12 : gnc_sx_begin_edit(sx);
954 : 12 : sx->instance_num = instance_num;
955 : 12 : qof_instance_set_dirty(&sx->inst);
956 : 12 : gnc_sx_commit_edit(sx);
957 : : }
958 : :
959 : : GList *
960 : 0 : xaccSchedXactionGetSplits( const SchedXaction *sx )
961 : : {
962 : 0 : g_return_val_if_fail( sx, NULL );
963 : 0 : return xaccAccountGetSplitList(sx->template_acct);
964 : : }
965 : :
966 : : static Split *
967 : 8 : pack_split_info (TTSplitInfoPtr s_info, Account *parent_acct,
968 : : Transaction *parent_trans, QofBook *book)
969 : : {
970 : : Split *split;
971 : : const gchar *credit_formula;
972 : : const gchar *debit_formula;
973 : : const GncGUID *acc_guid;
974 : :
975 : 8 : split = xaccMallocSplit(book);
976 : :
977 : 8 : xaccSplitSetMemo(split, s_info->get_memo());
978 : :
979 : : /* Set split-action with gnc_set_num_action which is the same as
980 : : * xaccSplitSetAction with these arguments */
981 : 8 : gnc_set_num_action(NULL, split, NULL, s_info->get_action());
982 : :
983 : 8 : xaccAccountInsertSplit(parent_acct,
984 : : split);
985 : :
986 : 8 : credit_formula = s_info->get_credit_formula ();
987 : 8 : debit_formula = s_info->get_debit_formula ();
988 : 8 : acc_guid = qof_entity_get_guid(QOF_INSTANCE(s_info->get_account ()));
989 : 8 : qof_instance_set (QOF_INSTANCE (split),
990 : : "sx-credit-formula", credit_formula,
991 : : "sx-debit-formula", debit_formula,
992 : : "sx-account", acc_guid,
993 : : NULL);
994 : :
995 : 8 : return split;
996 : : }
997 : :
998 : :
999 : : void
1000 : 4 : xaccSchedXactionSetTemplateTrans (SchedXaction *sx, const TTInfoVec& tt_vec, QofBook *book)
1001 : : {
1002 : : Transaction *new_trans;
1003 : :
1004 : 4 : g_return_if_fail (book);
1005 : :
1006 : : /* delete any old transactions, if there are any */
1007 : 4 : delete_template_trans( sx );
1008 : :
1009 : 8 : for (auto tti : tt_vec)
1010 : : {
1011 : 4 : new_trans = xaccMallocTransaction(book);
1012 : :
1013 : 4 : xaccTransBeginEdit(new_trans);
1014 : 4 : xaccTransSetDescription(new_trans, tti->get_description());
1015 : 4 : xaccTransSetDatePostedSecsNormalized(new_trans, gnc_time (NULL));
1016 : : /* Set tran-num with gnc_set_num_action which is the same as
1017 : : * xaccTransSetNum with these arguments */
1018 : 4 : gnc_set_num_action (new_trans, NULL, tti->get_num(), NULL);
1019 : 4 : xaccTransSetNotes (new_trans, tti->get_notes ());
1020 : 4 : xaccTransSetCurrency (new_trans, tti->get_currency ());
1021 : :
1022 : 12 : for (auto s_info : tti->get_template_splits())
1023 : : {
1024 : 8 : auto new_split = pack_split_info(s_info, sx->template_acct, new_trans, book);
1025 : 8 : xaccTransAppendSplit(new_trans, new_split);
1026 : 8 : }
1027 : 4 : xaccTransCommitEdit(new_trans);
1028 : 4 : }
1029 : : }
1030 : :
1031 : : SXTmpStateData*
1032 : 21 : gnc_sx_create_temporal_state(const SchedXaction *sx )
1033 : : {
1034 : 21 : auto toRet = g_new0 (SXTmpStateData, 1);
1035 : 21 : if (g_date_valid (&(sx->last_date)))
1036 : 0 : toRet->last_date = sx->last_date;
1037 : : else
1038 : 21 : g_date_set_dmy (&toRet->last_date, 1, static_cast<GDateMonth>(1), 1970);
1039 : 21 : toRet->num_occur_rem = sx->num_occurances_remain;
1040 : 21 : toRet->num_inst = sx->instance_num;
1041 : 21 : return toRet;
1042 : : }
1043 : :
1044 : : void
1045 : 37 : gnc_sx_incr_temporal_state(const SchedXaction *sx, SXTmpStateData *tsd )
1046 : : {
1047 : 37 : g_return_if_fail(tsd != NULL);
1048 : 37 : tsd->last_date = xaccSchedXactionGetNextInstance (sx, tsd);
1049 : 37 : if (xaccSchedXactionHasOccurDef (sx))
1050 : : {
1051 : 4 : --tsd->num_occur_rem;
1052 : : }
1053 : 37 : ++tsd->num_inst;
1054 : : }
1055 : :
1056 : : void
1057 : 37 : gnc_sx_destroy_temporal_state (SXTmpStateData *tsd)
1058 : : {
1059 : 37 : g_free(tsd);
1060 : 37 : }
1061 : :
1062 : : SXTmpStateData*
1063 : 21 : gnc_sx_clone_temporal_state (SXTmpStateData *tsd)
1064 : : {
1065 : 21 : SXTmpStateData *toRet = NULL;
1066 : :
1067 : 21 : if(tsd)
1068 : : {
1069 : 21 : toRet = g_new(SXTmpStateData, 1);
1070 : 21 : *toRet = *tsd;
1071 : : }
1072 : :
1073 : 21 : return toRet;
1074 : : }
1075 : :
1076 : : static gint
1077 : 0 : _temporal_state_data_cmp( gconstpointer a, gconstpointer b )
1078 : : {
1079 : 0 : const SXTmpStateData *tsd_a = (SXTmpStateData*)a;
1080 : 0 : const SXTmpStateData *tsd_b = (SXTmpStateData*)b;
1081 : :
1082 : 0 : if ( !tsd_a && !tsd_b )
1083 : 0 : return 0;
1084 : 0 : if (tsd_a == tsd_b)
1085 : 0 : return 0;
1086 : 0 : if ( !tsd_a )
1087 : 0 : return 1;
1088 : 0 : if ( !tsd_b )
1089 : 0 : return -1;
1090 : 0 : return g_date_compare( &tsd_a->last_date,
1091 : 0 : &tsd_b->last_date );
1092 : : }
1093 : :
1094 : : /**
1095 : : * Adds an instance to the deferred list of the SX. Added instances are
1096 : : * added in (date-)sorted order.
1097 : : **/
1098 : : void
1099 : 0 : gnc_sx_add_defer_instance( SchedXaction *sx, void *deferStateData )
1100 : : {
1101 : 0 : sx->deferredList = g_list_insert_sorted( sx->deferredList,
1102 : : deferStateData,
1103 : : _temporal_state_data_cmp );
1104 : 0 : }
1105 : :
1106 : : /**
1107 : : * Removes an instance from the deferred list. The saved SXTmpStateData existed
1108 : : * for comparison only, so destroy it.
1109 : : **/
1110 : : void
1111 : 0 : gnc_sx_remove_defer_instance( SchedXaction *sx, void *deferStateData )
1112 : : {
1113 : : GList *found_by_value;
1114 : :
1115 : 0 : found_by_value = g_list_find_custom(
1116 : : sx->deferredList, deferStateData, _temporal_state_data_cmp);
1117 : 0 : if (found_by_value == NULL)
1118 : : {
1119 : 0 : g_warning("unable to find deferred instance");
1120 : 0 : return;
1121 : : }
1122 : :
1123 : 0 : g_free (found_by_value->data);
1124 : 0 : sx->deferredList = g_list_delete_link(sx->deferredList, found_by_value);
1125 : : }
1126 : :
1127 : : /**
1128 : : * Returns the defer list from the SX; this is a (date-)sorted
1129 : : * temporal-state-data instance list. The list should not be modified by the
1130 : : * caller; use the gnc_sx_{add,remove}_defer_instance() functions to modify
1131 : : * the list.
1132 : : *
1133 : : * @param sx Scheduled transaction
1134 : : * @return Defer list which must not be modified by the caller
1135 : : **/
1136 : : GList*
1137 : 18 : gnc_sx_get_defer_instances( SchedXaction *sx )
1138 : : {
1139 : 18 : return sx->deferredList;
1140 : : }
1141 : :
1142 : : static void
1143 : 8 : destroy_sx_on_book_close(QofInstance *ent, gpointer data)
1144 : : {
1145 : 8 : SchedXaction* sx = GNC_SCHEDXACTION(ent);
1146 : :
1147 : 8 : gnc_sx_begin_edit(sx);
1148 : 8 : xaccSchedXactionDestroy(sx);
1149 : 8 : }
1150 : :
1151 : : /**
1152 : : * Destroys all SXes in the book because the book is being destroyed.
1153 : : *
1154 : : * @param book Book being destroyed
1155 : : */
1156 : : static void
1157 : 153 : gnc_sx_book_end(QofBook* book)
1158 : : {
1159 : : QofCollection *col;
1160 : :
1161 : 153 : col = qof_book_get_collection(book, GNC_ID_SCHEDXACTION);
1162 : 153 : qof_collection_foreach(col, destroy_sx_on_book_close, NULL);
1163 : :
1164 : : // Now destroy the template root account
1165 : 153 : gnc_book_set_template_root (book, NULL);
1166 : 153 : }
1167 : :
1168 : : #ifdef _MSC_VER
1169 : : /* MSVC compiler doesn't have C99 "designated initializers"
1170 : : * so we wrap them in a macro that is empty on MSVC. */
1171 : : # define DI(x) /* */
1172 : : #else
1173 : : # define DI(x) x
1174 : : #endif
1175 : : static QofObject SXDesc =
1176 : : {
1177 : : DI(.interface_version = ) QOF_OBJECT_VERSION,
1178 : : DI(.e_type = ) GNC_SX_ID,
1179 : : DI(.type_label = ) "Scheduled Transaction",
1180 : : DI(.create = ) (void* (*)(QofBook*))xaccSchedXactionMalloc,
1181 : : DI(.book_begin = ) NULL,
1182 : : DI(.book_end = ) gnc_sx_book_end,
1183 : : DI(.is_dirty = ) qof_collection_is_dirty,
1184 : : DI(.mark_clean = ) qof_collection_mark_clean,
1185 : : DI(.foreach = ) qof_collection_foreach,
1186 : : DI(.printable = ) NULL,
1187 : : DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
1188 : : };
1189 : :
1190 : : gboolean
1191 : 81 : SXRegister(void)
1192 : : {
1193 : : static QofParam params[] =
1194 : : {
1195 : : {
1196 : : GNC_SX_NAME, QOF_TYPE_STRING, (QofAccessFunc)xaccSchedXactionGetName,
1197 : : (QofSetterFunc)xaccSchedXactionSetName
1198 : : },
1199 : : {
1200 : : GNC_SX_START_DATE, QOF_TYPE_DATE, (QofAccessFunc)xaccSchedXactionGetStartDateTT,
1201 : : (QofSetterFunc)xaccSchedXactionSetStartDateTT
1202 : : },
1203 : : {
1204 : : GNC_SX_LAST_DATE, QOF_TYPE_DATE, (QofAccessFunc)xaccSchedXactionGetLastOccurDateTT,
1205 : : (QofSetterFunc)xaccSchedXactionSetLastOccurDateTT
1206 : : },
1207 : : {
1208 : : GNC_SX_NUM_OCCUR, QOF_TYPE_INT64, (QofAccessFunc)xaccSchedXactionGetNumOccur,
1209 : : (QofSetterFunc)xaccSchedXactionSetNumOccur
1210 : : },
1211 : : {
1212 : : GNC_SX_REM_OCCUR, QOF_TYPE_INT64, (QofAccessFunc)xaccSchedXactionGetRemOccur,
1213 : : (QofSetterFunc)xaccSchedXactionSetRemOccur
1214 : : },
1215 : : { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
1216 : : { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
1217 : : { NULL },
1218 : : };
1219 : 81 : qof_class_register(GNC_SX_ID, NULL, params);
1220 : 81 : return qof_object_register(&SXDesc);
1221 : : }
|