GnuCash c935c2f+
Loading...
Searching...
No Matches
Transaction.cpp
1/********************************************************************\
2 * Transaction.c -- transaction implementation *
3 * Copyright (C) 1997 Robin D. Clark *
4 * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org> *
5 * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
6 * Copyright (c) 2006 David Hampton <hampton@employees.org> *
7 * *
8 * This program is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU General Public License as *
10 * published by the Free Software Foundation; either version 2 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License*
19 * along with this program; if not, contact: *
20 * *
21 * Free Software Foundation Voice: +1-617-542-5942 *
22 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23 * Boston, MA 02110-1301, USA gnu@gnu.org *
24 * *
25\********************************************************************/
26
27#include "qofinstance.h"
28#include <config.h>
29
30#include <platform.h>
31#if PLATFORM(WINDOWS)
32#include <windows.h>
33#endif
34
35#include <glib.h>
36#include <glib/gi18n.h>
37#include <stdlib.h>
38#include <string.h>
39#include <stdint.h>
40#include <time.h>
41#ifdef HAVE_UNISTD_H
42# include <unistd.h>
43#endif
44
45#include "AccountP.hpp"
46#include "Scrub.h"
47#include "Scrub3.h"
48#include "TransactionP.hpp"
49#include "SplitP.hpp"
50#include "TransLog.h"
51#include "cap-gains.h"
52#include "gnc-commodity.h"
53#include "gnc-engine.h"
54#include "gnc-lot.h"
55#include "gnc-event.h"
56#include <gnc-date.h>
57#include "SchedXaction.h"
58#include "gncBusiness.h"
59#include <qofinstance-p.h>
60#include "gncInvoice.h"
61#include "gncOwner.h"
62
63/* Notes about xaccTransBeginEdit(), xaccTransCommitEdit(), and
64 * xaccTransRollback():
65 *
66 * Why use it:
67 *
68 * Data consistency: Wrapping your changes to financial data inside
69 * a BeginEdit/CommitEdit block allows the engine to verify that
70 * your changes still leave the financial objects in an internally
71 * consistent state. This is true even though you may make a series
72 * of individual changes that are not consistent by themselves. In
73 * this way, it's like telling the engine, "Okay, I've finished my
74 * edits. Please check my work."
75 *
76 * Data integrity: The other benefit of the BeginEdit/CommitEdit
77 * block is that it allows the engine (and the backend) to remember
78 * the last known correct state of your data. This allows you to
79 * undo any changes that you don't want to keep. In this way, it's
80 * like telling the engine telling the back end, "Yes, I really mean
81 * it. Remember this data." or "Nevermind, scratch that." The
82 * important feature here is that if things go bad, for whatever
83 * reason (e.g. the application crashed, you lost the backend), your
84 * data remains in the state it was in just after the previous
85 * xaccTransCommitEdit(). [assuming no nesting, which probably
86 * isn't useful outside the engine.]
87 *
88 * Note that the backend doesn't care about data consistency -
89 * that's the engine's job.
90 *
91 * Example Use:
92 *
93 * xaccTransBeginEdit(trans);
94 *
95 *
96 * split = xaccMallocSplit(book);
97 * xaccSplitSetAccount(split, acc);
98 * xaccSplitSetParent(split, trans); // Adding a new split
99 *
100 * xaccSplitSetValue(split, val); // Changing a split
101 *
102 * xaccSplitDestroy(split); // Removing a split
103 *
104 * xaccTransSetNum(trans, "501"); // Changing the trans
105 *
106 * if (really_do_it)
107 * xaccTransCommitEdit(trans);
108 * else
109 * xaccTransRollbackEdit(trans);
110 *
111 * How it works:
112 *
113 * Calling xaccTransBeginEdit() starts a BeginEdit/CommitEdit block.
114 * Inside the block any changes to the transaction or any splits in
115 * the transaction are considered "pending". What does that mean?
116 *
117 * In general that means that if you set and then get the
118 * transaction's or split's parameters inside the
119 * BeginEdit/CommitEdit block, you'll get the values you just set.
120 * However, if you change an object's many-to-one relationship with
121 * another object, you won't see the change from the "many" side
122 * until the CommitEdit. For example, if you move a split from one
123 * account into another, you can see the change with
124 * xaccSplitGetAccount(), but both Accounts' split lists won't be
125 * updated until the CommitEdit. Correspondingly, no signals
126 * (events) will be generated for those "foreign" objects, or the
127 * Transaction, until the CommitEdit.
128 *
129 * This behavior is important because, when we're finally ready to
130 * commit to the backend, we can't be 100% sure that the backend
131 * will still be available. We have to offer the backend all of the
132 * new state as if it were already "true", but we need to save all of
133 * the old state in case the backend won't accept our commit. If
134 * the backend commit fails, we have to restore all the old state.
135 * If the backend commit succeeds, and *only* after it succeeds, we
136 * can advertise the new state to the rest of the engine (and gui).
137 *
138 * Q: Who owns the ref of an added split if the Transaction is rolled
139 * back?
140 *
141 * A: This is a design decision. If the answer is 'the user',
142 * then the burden is on the api user to check the transaction after
143 * every commit to see if the added split is really in the
144 * transaction. If they don't they risk leaking the split if the
145 * commit was rolled back. Another design is to answer 'the engine'.
146 * In that case the burden is on the engine to free a newly added
147 * split if the commit is rolled back. Unfortunately the engine
148 * objects aren't ref-counted, so this is tricky.
149 *
150 * In the current implementation, the answer is 'the engine', but
151 * that means that you must not add the split to two different
152 * transactions during the begin/commit block, because if one rolls
153 * back, they will both think they own the split. This is one
154 * specific example of the general problem that the outcome of two
155 * parallel begin/commit edit blocks for two transactions where edits
156 * for both transactions involve the same splits and one or more
157 * edit-blocks is rolled-back, is poorly-defined.
158 *
159 *
160 *
161 * Design notes on event-generation: transaction-modified-events
162 * should not be generated until transaction commit or rollback
163 * time. They should not be generated as each field is tweaked.
164 * This for two reasons:
165 * 1) Most editing events make multiple changes to a transaction,
166 * which would generate a flurry of (needless) events, if they
167 * weren't saved up till the commit.
168 * 2) Technically, its incorrect to use transaction data
169 * until the transaction is committed. The GUI element that
170 * is changing the data can look at it, but all of the rest
171 * of the GUI should ignore the data until its committed.
172 */
173
174const char *trans_notes_str = "notes";
175const char *void_reason_str = "void-reason";
176const char *void_time_str = "void-time";
177const char *void_former_notes_str = "void-former-notes";
178const char *trans_is_closing_str = "book_closing";
179const char *doclink_uri_str = "assoc_uri"; // this is the old name for the document link, kept for compatibility
180
181/* KVP entry for date-due value */
182#define TRANS_DATE_DUE_KVP "trans-date-due"
183#define TRANS_TXN_TYPE_KVP "trans-txn-type"
184#define TRANS_READ_ONLY_REASON "trans-read-only"
185#define TRANS_REVERSED_BY "reversed-by"
186#define GNC_SX_FROM "from-sched-xaction"
187
188#define ISO_DATELENGTH 32 /* length of an iso 8601 date string. */
189
190/* This static indicates the debugging module that this .o belongs to. */
191static QofLogModule log_module = GNC_MOD_ENGINE;
192
193enum
194{
195 PROP_0,
196 PROP_CURRENCY, /* Table */
197 PROP_NUM, /* Table */
198 PROP_POST_DATE, /* Table */
199 PROP_ENTER_DATE, /* Table */
200 PROP_DESCRIPTION, /* Table */
201 PROP_INVOICE, /* KVP */
202 PROP_SX_TXN, /* KVP */
203};
204
205void
206check_open (const Transaction *trans)
207{
208 if (trans && 0 >= qof_instance_get_editlevel(trans))
209 PERR ("transaction %p not open for editing", trans);
210}
211/********************************************************************\
212\********************************************************************/
213gboolean
214xaccTransStillHasSplit(const Transaction *trans, const Split *s)
215{
216 return (s && s->parent == trans && !qof_instance_get_destroying(s));
217}
218
219/* Executes 'cmd_block' for each split currently in the transaction,
220 * using the in-edit state. Use the variable 's' for each split. */
221#define FOR_EACH_SPLIT(trans, cmd_block) if (trans->splits) { \
222 GList *splits; \
223 for (splits = (trans)->splits; splits; splits = splits->next) { \
224 Split *s = GNC_SPLIT(splits->data); \
225 if (xaccTransStillHasSplit(trans, s)) { \
226 cmd_block; \
227 } \
228 } \
229 }
230
231static inline void mark_trans (Transaction *trans);
232void mark_trans (Transaction *trans)
233{
234 FOR_EACH_SPLIT(trans, mark_split(s));
235}
236
237static inline void gen_event_trans (Transaction *trans);
238void gen_event_trans (Transaction *trans)
239{
240 GList *node;
241
242 for (node = trans->splits; node; node = node->next)
243 {
244 Split *s = GNC_SPLIT(node->data);
245 Account *account = s->acc;
246 GNCLot *lot = s->lot;
247 if (account)
248 qof_event_gen (&account->inst, GNC_EVENT_ITEM_CHANGED, s);
249
250 if (lot)
251 {
252 /* A change of transaction date might affect opening date of lot */
253 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, nullptr);
254 }
255 }
256}
257
258/* GObject Initialization */
259G_DEFINE_TYPE(Transaction, gnc_transaction, QOF_TYPE_INSTANCE)
260
261static void
262gnc_transaction_init(Transaction* trans)
263{
264 ENTER ("trans=%p", trans);
265 /* Fill in some sane defaults */
266 trans->num = CACHE_INSERT("");
267 trans->description = CACHE_INSERT("");
268 trans->common_currency = nullptr;
269 trans->splits = nullptr;
270 trans->date_entered = 0;
271 trans->date_posted = 0;
272 trans->marker = 0;
273 trans->orig = nullptr;
274 trans->txn_type = TXN_TYPE_UNCACHED;
275 LEAVE (" ");
276}
277
278static void
279gnc_transaction_dispose(GObject *txnp)
280{
281 G_OBJECT_CLASS(gnc_transaction_parent_class)->dispose(txnp);
282}
283
284static void
285gnc_transaction_finalize(GObject* txnp)
286{
287 G_OBJECT_CLASS(gnc_transaction_parent_class)->finalize(txnp);
288}
289
290/* Note that g_value_set_object() refs the object, as does
291 * g_object_get(). But g_object_get() only unrefs once when it disgorges
292 * the object, leaving an unbalanced ref, which leaks. So instead of
293 * using g_value_set_object(), use g_value_take_object() which doesn't
294 * ref the object when used in get_property().
295 */
296static void
297gnc_transaction_get_property(GObject* object,
298 guint prop_id,
299 GValue* value,
300 GParamSpec* pspec)
301{
302 Transaction* tx;
303 Time64 time;
304
305 g_return_if_fail(GNC_IS_TRANSACTION(object));
306
307 tx = GNC_TRANSACTION(object);
308 switch (prop_id)
309 {
310 case PROP_NUM:
311 g_value_set_string(value, tx->num);
312 break;
313 case PROP_DESCRIPTION:
314 g_value_set_string(value, tx->description);
315 break;
316 case PROP_CURRENCY:
317 g_value_take_object(value, tx->common_currency);
318 break;
319 case PROP_POST_DATE:
320 time.t = tx->date_posted;
321 g_value_set_boxed(value, &time);
322 break;
323 case PROP_ENTER_DATE:
324 time.t = tx->date_entered;
325 g_value_set_boxed(value, &time);
326 break;
327 case PROP_INVOICE:
328 qof_instance_get_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
329 break;
330 case PROP_SX_TXN:
331 qof_instance_get_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
332 break;
333 default:
334 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
335 break;
336 }
337}
338
339static void
340gnc_transaction_set_property(GObject* object,
341 guint prop_id,
342 const GValue* value,
343 GParamSpec* pspec)
344{
345 Transaction* tx;
346 Time64 *t;
347
348 g_return_if_fail(GNC_IS_TRANSACTION(object));
349
350 tx = GNC_TRANSACTION(object);
351 g_assert (qof_instance_get_editlevel(tx));
352
353 switch (prop_id)
354 {
355 case PROP_NUM:
356 xaccTransSetNum( tx, g_value_get_string(value));
357 break;
358 case PROP_DESCRIPTION:
359 xaccTransSetDescription(tx, g_value_get_string(value));
360 break;
361 case PROP_CURRENCY:
362 xaccTransSetCurrency(tx, GNC_COMMODITY(g_value_get_object(value)));
363 break;
364 case PROP_POST_DATE:
365 t = (Time64*)g_value_get_boxed(value);
367 break;
368 case PROP_ENTER_DATE:
369 t = (Time64*)g_value_get_boxed(value);
371 break;
372 case PROP_INVOICE:
373 qof_instance_set_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
374 break;
375 case PROP_SX_TXN:
376 qof_instance_set_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
377 break;
378 default:
379 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
380 break;
381 }
382}
383
384static void
385gnc_transaction_class_init(TransactionClass* klass)
386{
387 GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
388
389 gobject_class->dispose = gnc_transaction_dispose;
390 gobject_class->finalize = gnc_transaction_finalize;
391 gobject_class->set_property = gnc_transaction_set_property;
392 gobject_class->get_property = gnc_transaction_get_property;
393
394 g_object_class_install_property
395 (gobject_class,
396 PROP_NUM,
397 g_param_spec_string("num",
398 "Transaction Number",
399 "The transactionNumber is an arbitrary string "
400 "assigned by the user. It is intended to be "
401 "a short 1-6 character string that is displayed "
402 "by the register. For checks, it is usually the "
403 "check number. For other types of transactions, "
404 "it can be any string.",
405 nullptr,
406 G_PARAM_READWRITE));
407
408 g_object_class_install_property
409 (gobject_class,
410 PROP_DESCRIPTION,
411 g_param_spec_string("description",
412 "Transaction Description",
413 "The transaction description is an arbitrary string "
414 "assigned by the user. It is usually the customer, "
415 "vendor or other organization associated with the "
416 "transaction.",
417 nullptr,
418 G_PARAM_READWRITE));
419
420 g_object_class_install_property
421 (gobject_class,
422 PROP_CURRENCY,
423 g_param_spec_object ("currency",
424 "Currency",
425 "The base currency for this transaction.",
426 GNC_TYPE_COMMODITY,
427 G_PARAM_READWRITE));
428
429 g_object_class_install_property
430 (gobject_class,
431 PROP_POST_DATE,
432 g_param_spec_boxed("post-date",
433 "Post Date",
434 "The date the transaction occurred.",
435 GNC_TYPE_TIME64,
436 G_PARAM_READWRITE));
437
438 g_object_class_install_property
439 (gobject_class,
440 PROP_ENTER_DATE,
441 g_param_spec_boxed("enter-date",
442 "Enter Date",
443 "The date the transaction was entered.",
444 GNC_TYPE_TIME64,
445 G_PARAM_READWRITE));
446
447 g_object_class_install_property(
448 gobject_class,
449 PROP_INVOICE,
450 g_param_spec_boxed("invoice",
451 "Invoice attached to lot",
452 "Used by GncInvoice",
453 GNC_TYPE_GUID,
454 G_PARAM_READWRITE));
455
456 g_object_class_install_property(
457 gobject_class,
458 PROP_SX_TXN,
459 g_param_spec_boxed("from-sched-xaction",
460 "From Scheduled Transaction",
461 "Used by Scheduled Transastions to record the "
462 "originating template transaction for created "
463 "transactions",
464 GNC_TYPE_GUID,
465 G_PARAM_READWRITE));
466}
467
468/********************************************************************\
469 * xaccInitTransaction
470 * Initialize a transaction structure
471\********************************************************************/
472
473static void
474xaccInitTransaction (Transaction * trans, QofBook *book)
475{
476 ENTER ("trans=%p", trans);
477 qof_instance_init_data (&trans->inst, GNC_ID_TRANS, book);
478 LEAVE (" ");
479}
480
481/********************************************************************\
482\********************************************************************/
483
484Transaction *
486{
487 Transaction *trans;
488
489 g_return_val_if_fail (book, nullptr);
490
491 trans = GNC_TRANSACTION(g_object_new(GNC_TYPE_TRANSACTION, nullptr));
492 xaccInitTransaction (trans, book);
493 qof_event_gen (&trans->inst, QOF_EVENT_CREATE, nullptr);
494
495 return trans;
496}
497
498#ifdef DUMP_FUNCTIONS
499/* Please don't delete this function. Although it is not called by
500 any other code in GnuCash, it is useful when debugging. For example
501 it can be called using the gdb "call" command when stopped at a
502 breakpoint. */
503void
504xaccTransDump (const Transaction *trans, const char *tag)
505{
506 GList *node;
507 char datebuff[MAX_DATE_LENGTH + 1];
508
509 printf("%s Trans %p", tag, trans);
510 memset(datebuff, 0, sizeof(datebuff));
511 qof_print_date_buff(datebuff, MAX_DATE_LENGTH, trans->date_entered);
512 printf(" Entered: %s\n", datebuff);
513 memset(datebuff, 0, sizeof(datebuff));
514 qof_print_date_buff(datebuff, MAX_DATE_LENGTH, trans->date_posted);
515 printf(" Posted: %s\n", datebuff);
516 printf(" Num: %s\n", trans->num ? trans->num : "(null)");
517 printf(" Description: %s\n",
518 trans->description ? trans->description : "(null)");
519 printf(" Currency: %s\n",
520 gnc_commodity_get_printname(trans->common_currency));
521 printf(" version: %x\n", qof_instance_get_version(trans));
522 printf(" version_chk: %x\n", qof_instance_get_version_check(trans));
523 printf(" editlevel: %x\n", qof_instance_get_editlevel(trans));
524 printf(" orig: %p\n", trans->orig);
525 printf(" idata: %x\n", qof_instance_get_idata(trans));
526 printf(" splits: ");
527 for (node = trans->splits; node; node = node->next)
528 {
529 printf("%p ", node->data);
530 }
531 printf("\n");
532 for (node = trans->splits; node; node = node->next)
533 {
534 xaccSplitDump(GNC_SPLIT(node->data), tag);
535 }
536 printf("\n");
537}
538#endif
539
540void
541xaccTransSortSplits (Transaction *trans)
542{
543 GList *node, *new_list = nullptr;
544 Split *split;
545
546 /* first debits */
547 for (node = trans->splits; node; node = node->next)
548 {
549 split = GNC_SPLIT(node->data);
551 continue;
552 new_list = g_list_prepend (new_list, split);
553 }
554
555 /* then credits */
556 for (node = trans->splits; node; node = node->next)
557 {
558 split = GNC_SPLIT(node->data);
560 continue;
561 new_list = g_list_prepend (new_list, split);
562 }
563
564 /* install newly sorted list */
565 g_list_free(trans->splits);
566 trans->splits = g_list_reverse (new_list);
567}
568
569
570/********************************************************************\
571\********************************************************************/
572/* This routine is not exposed externally, since it does weird things,
573 * like not really owning the splits correctly, and other weirdnesses.
574 * This routine is prone to programmer snafu if not used correctly.
575 * It is used only by the edit-rollback code.
576 */
577static Transaction *
578dupe_trans (const Transaction *from)
579{
580 Transaction *to;
581 to = GNC_TRANSACTION(g_object_new (GNC_TYPE_TRANSACTION, nullptr));
582
583 CACHE_REPLACE (to->num, from->num);
584 CACHE_REPLACE (to->description, from->description);
585
586 to->splits = g_list_copy_deep (from->splits, (GCopyFunc)xaccDupeSplit, nullptr);
587 to->date_entered = from->date_entered;
588 to->date_posted = from->date_posted;
589 qof_instance_copy_version(to, from);
590 to->orig = nullptr;
591
592 to->common_currency = from->common_currency;
593
594 /* Trash the guid and entity table. We don't want to mistake
595 * the cloned transaction as something official. If we ever
596 * use this transaction, we'll have to fix this up.
597 */
598 to->inst.e_type = nullptr;
599 qof_instance_set_guid(to, guid_null());
600 qof_instance_copy_book(to, from);
601 qof_instance_copy_kvp (QOF_INSTANCE(to), QOF_INSTANCE(from));
602
603 return to;
604}
605
606/********************************************************************\
607 * Use this routine to externally duplicate a transaction. It creates
608 * a full fledged transaction with unique guid, splits, etc. and
609 * writes it to the database.
610\********************************************************************/
611static gpointer
612copy_split (gconstpointer from_split, gpointer to)
613{
614 auto split = xaccSplitCloneNoKvp(GNC_SPLIT(from_split));
615 split->parent = GNC_TRANSACTION(to);
616 return split;
617}
618
619Transaction *
620xaccTransCloneNoKvp (const Transaction *from)
621{
622 Transaction *to;
623
625 to = GNC_TRANSACTION(g_object_new (GNC_TYPE_TRANSACTION, nullptr));
626
627 to->date_entered = from->date_entered;
628 to->date_posted = from->date_posted;
629 CACHE_REPLACE (to->num, from->num);
630 CACHE_REPLACE (to->description, from->description);
631 to->common_currency = from->common_currency;
632 qof_instance_copy_version(to, from);
633 qof_instance_copy_version_check(to, from);
634
635 to->orig = nullptr;
636
637 qof_instance_init_data (&to->inst, GNC_ID_TRANS,
639
641 to->splits = g_list_copy_deep (from->splits, copy_split, to);
642 qof_instance_set_dirty(QOF_INSTANCE(to));
645
646 return to;
647}
648
649Transaction *
650xaccTransClone (const Transaction *from)
651{
652 Transaction *to = xaccTransCloneNoKvp (from);
653
654 if (g_list_length (to->splits) != g_list_length (from->splits))
655 {
656 PERR ("Cloned transaction has different number of splits from original");
657 xaccTransDestroy (to);
658 return nullptr;
659 }
660
662 qof_instance_copy_kvp (QOF_INSTANCE (to), QOF_INSTANCE (from));
663
664 for (GList* lfrom = from->splits, *lto = to->splits; lfrom && lto;
665 lfrom = g_list_next (lfrom), lto = g_list_next (lto))
666 xaccSplitCopyKvp (GNC_SPLIT(lfrom->data), GNC_SPLIT(lto->data));
667
669 return to;
670}
671
672/*################## Added for Reg2 #################*/
673
674/********************************************************************\
675 * Copy a transaction to the 'clipboard' transaction using
676 * dupe_trans. The 'clipboard' transaction must never
677 * be dereferenced.
678\********************************************************************/
679Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans)
680{
681 Transaction *to_trans;
682
683 if (!from_trans)
684 return nullptr;
685
686 to_trans = dupe_trans(from_trans);
687 return to_trans;
688}
689
690/********************************************************************\
691 * Copy a transaction to another using the function below without
692 * changing any account information.
693\********************************************************************/
694void
695xaccTransCopyOnto(const Transaction *from_trans, Transaction *to_trans)
696{
697 xaccTransCopyFromClipBoard(from_trans, to_trans, nullptr, nullptr, TRUE);
698}
699
700/********************************************************************\
701 * This function explicitly must robustly handle some unusual input.
702 *
703 * 'from_trans' may be a duped trans (see dupe_trans), so its
704 * splits may not really belong to the accounts that they say they do.
705 *
706 * 'from_acc' need not be a valid account. It may be an already freed
707 * Account. Therefore, it must not be dereferenced at all.
708 *
709 * Neither 'from_trans', nor 'from_acc', nor any of 'from's splits may
710 * be modified in any way.
711 *
712 * 'no_date' if TRUE will not copy the date posted.
713 *
714 * The 'to_trans' transaction will end up with valid copies of from's
715 * splits. In addition, the copies of any of from's splits that were
716 * in from_acc (or at least claimed to be) will end up in to_acc.
717\********************************************************************/
718void
719xaccTransCopyFromClipBoard(const Transaction *from_trans, Transaction *to_trans,
720 const Account *from_acc, Account *to_acc, gboolean no_date)
721{
722 gboolean change_accounts = FALSE;
723 GList *node;
724
725 if (!from_trans || !to_trans)
726 return;
727
728 change_accounts = from_acc && GNC_IS_ACCOUNT(to_acc) && from_acc != to_acc;
729 xaccTransBeginEdit(to_trans);
730
731 xaccTransClearSplits(to_trans);
732 xaccTransSetCurrency(to_trans, xaccTransGetCurrency(from_trans));
734
735 if ((xaccTransGetNum(to_trans) == nullptr) || (g_strcmp0 (xaccTransGetNum(to_trans), "") == 0))
736 xaccTransSetNum(to_trans, xaccTransGetNum(from_trans));
737
738 xaccTransSetNotes(to_trans, xaccTransGetNotes(from_trans));
739 xaccTransSetDocLink(to_trans, xaccTransGetDocLink (from_trans));
740 if(!no_date)
741 {
743 }
744
745 /* Each new split will be parented to 'to' */
746 for (node = from_trans->splits; node; node = node->next)
747 {
748 Split *new_split = xaccMallocSplit( qof_instance_get_book(QOF_INSTANCE(from_trans)));
749 xaccSplitCopyOnto(GNC_SPLIT(node->data), new_split);
750 if (change_accounts && xaccSplitGetAccount(GNC_SPLIT(node->data)) == from_acc)
751 xaccSplitSetAccount(new_split, to_acc);
752 xaccSplitSetParent(new_split, to_trans);
753 }
754 xaccTransCommitEdit(to_trans);
755}
756
757/*################## Added for Reg2 #################*/
758
759/********************************************************************\
760 Free the transaction.
761\********************************************************************/
762static void
763xaccFreeTransaction (Transaction *trans)
764{
765 if (!trans) return;
766
767 ENTER ("(addr=%p)", trans);
768 if (((char *) 1) == trans->num)
769 {
770 PERR ("double-free %p", trans);
771 LEAVE (" ");
772 return;
773 }
774
775 /* free up the destination splits */
776 g_list_free_full (trans->splits, (GDestroyNotify)xaccFreeSplit);
777 trans->splits = nullptr;
778
779 /* free up transaction strings */
780 CACHE_REMOVE(trans->num);
781 CACHE_REMOVE(trans->description);
782
783 /* Just in case someone looks up freed memory ... */
784 trans->num = (char *) 1;
785 trans->description = nullptr;
786 trans->date_entered = 0;
787 trans->date_posted = 0;
788 if (trans->orig)
789 {
790 xaccFreeTransaction (trans->orig);
791 trans->orig = nullptr;
792 }
793
794 /* qof_instance_release (&trans->inst); */
795 g_object_unref(trans);
796
797 LEAVE ("(addr=%p)", trans);
798}
799
800/********************************************************************
801 xaccTransEqual
802
803 Compare two transactions for equality. We don't pay any attention to
804 rollback issues here, and we only care about equality of "permanent
805 fields", basically the things that would survive a file save/load
806 cycle.
807
808 ********************************************************************/
809
810/* return 0 when splits have equal guids */
811static gint
812compare_split_guids (gconstpointer a, gconstpointer b)
813{
814 const Split *sa = GNC_SPLIT(a);
815 const Split *sb = GNC_SPLIT(b);
816
817 if (sa == sb) return 0;
818 if (!sa || !sb) return 1;
819
820 return guid_compare (xaccSplitGetGUID (sa), xaccSplitGetGUID (sb));
821}
822
823gboolean
824xaccTransEqual(const Transaction *ta, const Transaction *tb,
825 gboolean check_guids,
826 gboolean check_splits,
827 gboolean check_balances,
828 gboolean assume_ordered)
829{
830 gboolean same_book;
831
832 if (!ta && !tb) return TRUE; /* Arguable. FALSE may be better. */
833
834 if (!ta || !tb)
835 {
836 PINFO ("one is nullptr");
837 return FALSE;
838 }
839
840 if (ta == tb) return TRUE;
841
842 same_book = qof_instance_get_book(QOF_INSTANCE(ta)) == qof_instance_get_book(QOF_INSTANCE(tb));
843
844 if (check_guids)
845 {
846 if (qof_instance_guid_compare(ta, tb) != 0)
847 {
848 PINFO ("GUIDs differ");
849 return FALSE;
850 }
851 }
852
853 if (!gnc_commodity_equal(ta->common_currency, tb->common_currency))
854 {
855 PINFO ("commodities differ %s vs %s",
856 gnc_commodity_get_unique_name (ta->common_currency),
857 gnc_commodity_get_unique_name (tb->common_currency));
858 return FALSE;
859 }
860
861 if (ta->date_entered != tb->date_entered)
862 {
863 char buf1[100];
864 char buf2[100];
865
866 (void)gnc_time64_to_iso8601_buff(ta->date_entered, buf1);
867 (void)gnc_time64_to_iso8601_buff(tb->date_entered, buf2);
868 PINFO ("date entered differs: '%s' vs '%s'", buf1, buf2);
869 return FALSE;
870 }
871
872 if (ta->date_posted != tb->date_posted)
873 {
874 char buf1[100];
875 char buf2[100];
876
877 (void)gnc_time64_to_iso8601_buff(ta->date_posted, buf1);
878 (void)gnc_time64_to_iso8601_buff(tb->date_posted, buf2);
879 PINFO ("date posted differs: '%s' vs '%s'", buf1, buf2);
880 return FALSE;
881 }
882
883 /* If the same book, since we use cached strings, we can just compare pointer
884 * equality for num and description
885 */
886 if ((same_book && ta->num != tb->num) || (!same_book && g_strcmp0(ta->num, tb->num) != 0))
887 {
888 PINFO ("num differs: %s vs %s", ta->num, tb->num);
889 return FALSE;
890 }
891
892 if ((same_book && ta->description != tb->description)
893 || (!same_book && g_strcmp0(ta->description, tb->description)))
894 {
895 PINFO ("descriptions differ: %s vs %s", ta->description, tb->description);
896 return FALSE;
897 }
898
899 if (qof_instance_compare_kvp (QOF_INSTANCE (ta), QOF_INSTANCE (tb)) != 0)
900 {
901 char *frame_a;
902 char *frame_b;
903
904 frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (ta));
905 frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (tb));
906
907
908 PINFO ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
909
910 g_free (frame_a);
911 g_free (frame_b);
912
913 return FALSE;
914 }
915
916 if (check_splits)
917 {
918 if ((!ta->splits && tb->splits) || (!tb->splits && ta->splits))
919 {
920 PINFO ("only one has splits");
921 return FALSE;
922 }
923
924 if (ta->splits && tb->splits)
925 {
926 GList *node_a, *node_b;
927
928 for (node_a = ta->splits, node_b = tb->splits;
929 node_a;
930 node_a = node_a->next, node_b = node_b->next)
931 {
932 Split *split_a = GNC_SPLIT(node_a->data);
933 Split *split_b;
934
935 /* don't presume that the splits are in the same order */
936 if (!assume_ordered)
937 node_b = g_list_find_custom (tb->splits, split_a,
938 compare_split_guids);
939
940 if (!node_b)
941 {
942 gchar guidstr[GUID_ENCODING_LENGTH+1];
943 guid_to_string_buff (xaccSplitGetGUID (split_a),guidstr);
944
945 PINFO ("first has split %s and second does not",guidstr);
946 return FALSE;
947 }
948
949 split_b = GNC_SPLIT(node_b->data);
950
951 if (!xaccSplitEqual (split_a, split_b, check_guids, check_balances,
952 FALSE))
953 {
954 char str_a[GUID_ENCODING_LENGTH + 1];
955 char str_b[GUID_ENCODING_LENGTH + 1];
956
957 guid_to_string_buff (xaccSplitGetGUID (split_a), str_a);
958 guid_to_string_buff (xaccSplitGetGUID (split_b), str_b);
959
960 PINFO ("splits %s and %s differ", str_a, str_b);
961 return FALSE;
962 }
963 }
964
965 if (g_list_length (ta->splits) != g_list_length (tb->splits))
966 {
967 PINFO ("different number of splits");
968 return FALSE;
969 }
970 }
971 }
972
973 return TRUE;
974}
975
976/********************************************************************\
977xaccTransUseTradingAccounts
978
979Returns true if the transaction should include trading account splits if
980it involves more than one commodity.
981\********************************************************************/
982
983gboolean xaccTransUseTradingAccounts(const Transaction *trans)
984{
986}
987
988/********************************************************************\
989\********************************************************************/
990
991Transaction *
992xaccTransLookup (const GncGUID *guid, QofBook *book)
993{
994 QofCollection *col;
995 if (!guid || !book) return nullptr;
996 col = qof_book_get_collection (book, GNC_ID_TRANS);
997 return (Transaction *) qof_collection_lookup_entity (col, guid);
998}
999
1000/********************************************************************\
1001\********************************************************************/
1002
1003gnc_numeric
1004xaccTransGetImbalanceValue (const Transaction * trans)
1005{
1006 gnc_numeric imbal = gnc_numeric_zero();
1007 if (!trans) return imbal;
1008
1009 ENTER("(trans=%p)", trans);
1010 /* Could use xaccSplitsComputeValue, except that we want to use
1011 GNC_HOW_DENOM_EXACT */
1012 FOR_EACH_SPLIT(trans, imbal =
1015 LEAVE("(trans=%p) imbal=%s", trans, gnc_num_dbg_to_string(imbal));
1016 return imbal;
1017}
1018
1019MonetaryList *
1020xaccTransGetImbalance (const Transaction * trans)
1021{
1022 /* imbal_value is used if either (1) the transaction has a non currency
1023 split or (2) all the splits are in the same currency. If there are
1024 no non-currency splits and not all splits are in the same currency then
1025 imbal_list is used to compute the imbalance. */
1026 MonetaryList *imbal_list = nullptr;
1027 gnc_numeric imbal_value = gnc_numeric_zero();
1028 gboolean trading_accts;
1029
1030 if (!trans) return imbal_list;
1031
1032 ENTER("(trans=%p)", trans);
1033
1034 trading_accts = xaccTransUseTradingAccounts (trans);
1035
1036 /* If using trading accounts and there is at least one split that is not
1037 in the transaction currency or a split that has a price or exchange
1038 rate other than 1, then compute the balance in each commodity in the
1039 transaction. Otherwise (all splits are in the transaction's currency)
1040 then compute the balance using the value fields.
1041
1042 Optimize for the common case of only one currency and a balanced
1043 transaction. */
1044 FOR_EACH_SPLIT(trans,
1045 {
1046 gnc_commodity *commodity;
1048 if (trading_accts &&
1049 (imbal_list ||
1050 ! gnc_commodity_equiv(commodity, trans->common_currency) ||
1052 {
1053 /* Need to use (or already are using) a list of imbalances in each of
1054 the currencies used in the transaction. */
1055 if (! imbal_list)
1056 {
1057 /* All previous splits have been in the transaction's common
1058 currency, so imbal_value is in this currency. */
1059 imbal_list = gnc_monetary_list_add_value(imbal_list,
1060 trans->common_currency,
1061 imbal_value);
1062 }
1063 imbal_list = gnc_monetary_list_add_value(imbal_list, commodity,
1065 }
1066
1067 /* Add it to the value accumulator in case we need it. */
1068 imbal_value = gnc_numeric_add(imbal_value, xaccSplitGetValue(s),
1070 } );
1071
1072
1073 if (!imbal_list && !gnc_numeric_zero_p(imbal_value))
1074 {
1075 /* Not balanced and no list, create one. If we found multiple currencies
1076 and no non-currency commodity then imbal_list will already exist and
1077 we won't get here. */
1078 imbal_list = gnc_monetary_list_add_value(imbal_list,
1079 trans->common_currency,
1080 imbal_value);
1081 }
1082
1083 /* Delete all the zero entries from the list, perhaps leaving an
1084 empty list */
1085 imbal_list = gnc_monetary_list_delete_zeros(imbal_list);
1086
1087 LEAVE("(trans=%p), imbal=%p", trans, imbal_list);
1088 return imbal_list;
1089}
1090
1091gboolean
1092xaccTransIsBalanced (const Transaction *trans)
1093{
1094 MonetaryList *imbal_list;
1095 gboolean result;
1096 gnc_numeric imbal = gnc_numeric_zero();
1097 gnc_numeric imbal_trading = gnc_numeric_zero();
1098
1099 if (trans == nullptr) return FALSE;
1100
1101 if (xaccTransUseTradingAccounts(trans))
1102 {
1103 /* Transaction is imbalanced if the value is imbalanced in either
1104 trading or non-trading splits. One can't be used to balance
1105 the other. */
1106 FOR_EACH_SPLIT(trans,
1107 {
1108 Account *acc = xaccSplitGetAccount(s);
1109 if (!acc || xaccAccountGetType(acc) != ACCT_TYPE_TRADING)
1110 {
1111 imbal = gnc_numeric_add(imbal, xaccSplitGetValue(s),
1113 }
1114 else
1115 {
1116 imbal_trading = gnc_numeric_add(imbal_trading, xaccSplitGetValue(s),
1118 }
1119 }
1120 );
1121 }
1122 else
1123 imbal = xaccTransGetImbalanceValue(trans);
1124
1125 if (! gnc_numeric_zero_p(imbal) || ! gnc_numeric_zero_p(imbal_trading))
1126 return FALSE;
1127
1128 if (!xaccTransUseTradingAccounts (trans))
1129 return TRUE;
1130
1131 imbal_list = xaccTransGetImbalance(trans);
1132 result = imbal_list == nullptr;
1133 gnc_monetary_list_free(imbal_list);
1134 return result;
1135}
1136
1137gnc_numeric
1138xaccTransGetAccountValue (const Transaction *trans,
1139 const Account *acc)
1140{
1141 gnc_numeric total = gnc_numeric_zero ();
1142 if (!trans || !acc) return total;
1143
1144 FOR_EACH_SPLIT(trans, if (acc == xaccSplitGetAccount(s))
1145{
1146 total = gnc_numeric_add (total, xaccSplitGetValue (s),
1149 });
1150 return total;
1151}
1152
1153gnc_numeric
1154xaccTransGetAccountAmount (const Transaction *trans, const Account *acc)
1155{
1156 gnc_numeric total = gnc_numeric_zero ();
1157 if (!trans || !acc) return total;
1158
1159 total = gnc_numeric_convert (total, xaccAccountGetCommoditySCU (acc),
1161 FOR_EACH_SPLIT(trans, if (acc == xaccSplitGetAccount(s))
1162 total = gnc_numeric_add_fixed(
1163 total, xaccSplitGetAmount(s)));
1164 return total;
1165}
1166
1167gnc_numeric
1168xaccTransGetAccountConvRate(const Transaction *txn, const Account *acc)
1169{
1170 gnc_numeric amount, value, convrate;
1171 GList *splits;
1172 Split *s;
1173 gboolean found_acc_match = FALSE;
1174 gnc_commodity *acc_commod = xaccAccountGetCommodity(acc);
1175
1176 /* We need to compute the conversion rate into _this account_. So,
1177 * find the first split into this account, compute the conversion
1178 * rate (based on amount/value), and then return this conversion
1179 * rate.
1180 */
1181 if (gnc_commodity_equal(acc_commod, xaccTransGetCurrency(txn)))
1182 return gnc_numeric_create(1, 1);
1183
1184 for (splits = txn->splits; splits; splits = splits->next)
1185 {
1186 Account *split_acc;
1187 gnc_commodity *split_commod;
1188
1189 s = GNC_SPLIT(splits->data);
1190
1191 if (!xaccTransStillHasSplit(txn, s))
1192 continue;
1193 split_acc = xaccSplitGetAccount (s);
1194 split_commod = xaccAccountGetCommodity (split_acc);
1195 if (! (split_acc == acc ||
1196 gnc_commodity_equal (split_commod, acc_commod)))
1197 continue;
1198
1199 found_acc_match = TRUE;
1200 amount = xaccSplitGetAmount (s);
1201
1202 /* Ignore splits with "zero" amount */
1203 if (gnc_numeric_zero_p (amount))
1204 continue;
1205
1206 value = xaccSplitGetValue (s);
1207 if (gnc_numeric_zero_p (value))
1208 PWARN("How can amount be nonzero and value be zero?");
1209
1210 convrate = gnc_numeric_div(amount, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
1211 return convrate;
1212 }
1213
1214 if (acc)
1215 {
1216 /* If we did find a matching account but its amount was zero,
1217 * then perhaps this is a "special" income/loss transaction
1218 */
1219 if (found_acc_match)
1220 return gnc_numeric_zero();
1221 else
1222 PERR("Cannot convert transaction -- no splits with proper conversion ratio");
1223 }
1224 return gnc_numeric_create (100, 100);
1225}
1226
1227gnc_numeric
1228xaccTransGetAccountBalance (const Transaction *trans,
1229 const Account *account)
1230{
1231 GList *node;
1232 Split *last_split = nullptr;
1233
1234 // Not really the appropriate error value.
1235 g_return_val_if_fail(account && trans, gnc_numeric_error(GNC_ERROR_ARG));
1236
1237 for (node = trans->splits; node; node = node->next)
1238 {
1239 Split *split = GNC_SPLIT(node->data);
1240
1241 if (!xaccTransStillHasSplit(trans, split))
1242 continue;
1243 if (xaccSplitGetAccount(split) != account)
1244 continue;
1245
1246 if (!last_split)
1247 {
1248 last_split = split;
1249 continue;
1250 }
1251
1252 /* This test needs to correspond to the comparison function used when
1253 sorting the splits for computing the running balance. */
1254 if (xaccSplitOrder (last_split, split) < 0)
1255 last_split = split;
1256 }
1257
1258 return xaccSplitGetBalance (last_split);
1259}
1260
1261/********************************************************************\
1262\********************************************************************/
1263/* The new routine for setting the common currency */
1264
1265gnc_commodity *
1266xaccTransGetCurrency (const Transaction *trans)
1267{
1268 return trans ? trans->common_currency : nullptr;
1269}
1270
1271/* Helper functions for xaccTransSetCurrency */
1272static gnc_numeric
1273find_new_rate(Transaction *trans, gnc_commodity *curr)
1274{
1275 GList *node;
1276 gnc_numeric rate = gnc_numeric_zero();
1277 for (node = trans->splits; node != nullptr; node = g_list_next (node))
1278 {
1279 Split *split = GNC_SPLIT(node->data);
1280 gnc_commodity *split_com =
1282 if (gnc_commodity_equal(curr, split_com))
1283 {
1284/* This looks backwards, but the amount of the balancing transaction
1285 * that we're going to use it on is in the value's currency. */
1286 rate = gnc_numeric_div(xaccSplitGetAmount(split),
1287 xaccSplitGetValue(split),
1288 GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
1289 break;
1290 }
1291 }
1292 return rate;
1293}
1294
1295static void
1296split_set_new_value(Split* split, gnc_commodity *curr, gnc_commodity *old_curr,
1297 gnc_numeric rate)
1298{
1299 gnc_commodity *split_com =
1301 if (gnc_commodity_equal(curr, split_com))
1303 else if (gnc_commodity_equal(old_curr, split_com))
1304 xaccSplitSetSharePrice(split, rate);
1305 else
1306 {
1307 gnc_numeric old_rate = gnc_numeric_div(xaccSplitGetValue(split),
1308 xaccSplitGetAmount(split),
1310 GNC_HOW_RND_NEVER);
1311 gnc_numeric new_rate = gnc_numeric_div(old_rate, rate, GNC_DENOM_AUTO,
1312 GNC_HOW_RND_NEVER);
1313 xaccSplitSetSharePrice(split, new_rate);
1314 }
1315}
1316
1324void
1325xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr)
1326{
1327 gnc_commodity *old_curr = trans->common_currency;
1328 if (!trans || !curr || trans->common_currency == curr) return;
1329 xaccTransBeginEdit(trans);
1330
1331 trans->common_currency = curr;
1332 if (old_curr != nullptr && trans->splits != nullptr)
1333 {
1334 gnc_numeric rate = find_new_rate(trans, curr);
1335 if (!gnc_numeric_zero_p (rate))
1336 {
1337 FOR_EACH_SPLIT(trans, split_set_new_value(s, curr, old_curr, rate));
1338 }
1339 else
1340 {
1341 FOR_EACH_SPLIT(trans, xaccSplitSetValue(s, xaccSplitGetValue(s)));
1342 }
1343 }
1344
1345 qof_instance_set_dirty(QOF_INSTANCE(trans));
1346 mark_trans(trans); /* Dirty balance of every account in trans */
1347 xaccTransCommitEdit(trans);
1348}
1349
1350/********************************************************************\
1351\********************************************************************/
1352
1353void
1354xaccTransBeginEdit (Transaction *trans)
1355{
1356 if (!trans) return;
1357 if (!qof_begin_edit(&trans->inst)) return;
1358
1360
1362 {
1363 xaccOpenLog ();
1364 xaccTransWriteLog (trans, 'B');
1365 }
1366
1367 /* Make a clone of the transaction; we will use this
1368 * in case we need to roll-back the edit. */
1369 trans->orig = dupe_trans (trans);
1370}
1371
1372/********************************************************************\
1373\********************************************************************/
1374
1375void
1376xaccTransDestroy (Transaction *trans)
1377{
1378 if (!trans) return;
1379
1380 if (!xaccTransGetReadOnly (trans) ||
1382 {
1383 xaccTransBeginEdit(trans);
1384 qof_instance_set_destroying(trans, TRUE);
1385 xaccTransCommitEdit(trans);
1386 }
1387}
1388
1389static void
1390destroy_gains (Transaction *trans)
1391{
1392 SplitList *node;
1393 for (node = trans->splits; node; node = node->next)
1394 {
1395 Split *s = GNC_SPLIT(node->data);
1396 if (!xaccTransStillHasSplit(trans, s))
1397 continue;
1398
1399 if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s);
1400 if (s->gains_split && (GAINS_STATUS_GAINS & s->gains_split->gains))
1401 {
1402 Transaction *t = s->gains_split->parent;
1403 xaccTransDestroy (t);
1404 s->gains_split = nullptr;
1405 }
1406 }
1407}
1408
1409static void
1410do_destroy (QofInstance* inst)
1411{
1412 Transaction *trans{GNC_TRANSACTION (inst)};
1413 gboolean shutting_down = qof_book_shutting_down(qof_instance_get_book(trans));
1414
1415 /* If there are capital-gains transactions associated with this,
1416 * they need to be destroyed too unless we're shutting down in
1417 * which case all transactions will be destroyed. */
1418 if (!shutting_down)
1419 destroy_gains (trans);
1420
1421 /* Make a log in the journal before destruction. */
1422 if (!shutting_down && !qof_book_is_readonly(qof_instance_get_book(trans)))
1423 xaccTransWriteLog (trans, 'D');
1424
1425 qof_event_gen (&trans->inst, QOF_EVENT_DESTROY, nullptr);
1426 /* xaccFreeTransaction will also clean up the splits but without
1427 * emitting GNC_EVENT_ITEM_REMOVED.
1428 */
1429 xaccTransClearSplits(trans);
1430 xaccFreeTransaction (trans);
1431}
1432
1433/********************************************************************\
1434\********************************************************************/
1435
1436/* Temporary hack for data consistency */
1437static int scrub_data = 1;
1438void xaccEnableDataScrubbing(void)
1439{
1440 scrub_data = 1;
1441}
1442void xaccDisableDataScrubbing(void)
1443{
1444 scrub_data = 0;
1445}
1446
1447/* Check for an implicitly deleted transaction */
1448static gboolean was_trans_emptied(Transaction *trans)
1449{
1450 FOR_EACH_SPLIT(trans, return FALSE);
1451 return TRUE;
1452}
1453
1454static void trans_on_error(QofInstance *inst, QofBackendError errcode)
1455{
1456 Transaction *trans{GNC_TRANSACTION(inst)};
1457
1458 /* If the backend puked, then we must roll-back
1459 * at this point, and let the user know that we failed.
1460 * The GUI should check for error conditions ...
1461 */
1462 if (ERR_BACKEND_MODIFIED == errcode)
1463 {
1464 PWARN("Another user has modified this transaction\n"
1465 "\tjust a moment ago. Please look at their changes,\n"
1466 "\tand try again, if needed.\n");
1467 }
1468
1469 xaccTransRollbackEdit(trans);
1470 gnc_engine_signal_commit_error( errcode );
1471}
1472
1473static void trans_cleanup_commit(QofInstance *inst)
1474{
1475 Transaction *trans{GNC_TRANSACTION(inst)};
1476 GList *slist, *node;
1477
1478 /* ------------------------------------------------- */
1479 /* Make sure all associated splits are in proper order
1480 * in their accounts with the correct balances. */
1481
1482 /* Iterate over existing splits */
1483 slist = g_list_copy(trans->splits);
1484 for (node = slist; node; node = node->next)
1485 {
1486 Split *s = GNC_SPLIT(node->data);
1487 if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
1488 continue;
1489
1490 if ((s->parent != trans) || qof_instance_get_destroying(s))
1491 {
1492 /* Existing split either moved to another transaction or
1493 was destroyed, drop from list */
1494 GncEventData ed;
1495 ed.node = trans;
1496 ed.idx = g_list_index(trans->splits, s);
1497 trans->splits = g_list_remove(trans->splits, s);
1498 qof_event_gen(&s->inst, QOF_EVENT_REMOVE, &ed);
1499 }
1500
1501 if (s->parent == trans)
1502 {
1503 /* Split was either added, destroyed or just changed */
1505 qof_event_gen(&s->inst, QOF_EVENT_DESTROY, nullptr);
1506 else qof_event_gen(&s->inst, QOF_EVENT_MODIFY, nullptr);
1507 xaccSplitCommitEdit(s);
1508 }
1509 }
1510 g_list_free(slist);
1511
1513 xaccTransWriteLog (trans, 'C');
1514
1515 /* Get rid of the copy we made. We won't be rolling back,
1516 * so we don't need it any more. */
1517 PINFO ("get rid of rollback trans=%p", trans->orig);
1518 xaccFreeTransaction (trans->orig);
1519 trans->orig = nullptr;
1520
1521 /* Sort the splits. Why do we need to do this ?? */
1522 /* Good question. Who knows? */
1523 xaccTransSortSplits(trans);
1524
1525 /* Put back to zero. */
1526 qof_instance_decrease_editlevel(trans);
1527 g_assert(qof_instance_get_editlevel(trans) == 0);
1528
1529 gen_event_trans (trans); //TODO: could be conditional
1530 qof_event_gen (&trans->inst, QOF_EVENT_MODIFY, nullptr);
1531}
1532
1533void
1534xaccTransCommitEdit (Transaction *trans)
1535{
1536 if (!trans) return;
1537 ENTER ("(trans=%p)", trans);
1538
1539 if (!qof_commit_edit (QOF_INSTANCE(trans)))
1540 {
1541 LEAVE("editlevel non-zero");
1542 return;
1543 }
1544
1545 /* We increment this for the duration of the call
1546 * so other functions don't result in a recursive
1547 * call to xaccTransCommitEdit. */
1548 qof_instance_increase_editlevel(trans);
1549
1550 if (was_trans_emptied(trans))
1551 qof_instance_set_destroying(trans, TRUE);
1552
1553 /* Before committing the transaction, we are going to enforce certain
1554 * constraints. In particular, we want to enforce the cap-gains
1555 * and the balanced lot constraints. These constraints might
1556 * change the number of splits in this transaction, and the
1557 * transaction itself might be deleted. This is also why
1558 * we can't really enforce these constraints elsewhere: they
1559 * can cause pointers to splits and transactions to disappear out
1560 * from under the holder.
1561 */
1562 if (!qof_instance_get_destroying(trans) && scrub_data &&
1564 {
1565 /* If scrubbing gains recurses through here, don't call it again. */
1566 scrub_data = 0;
1567 /* The total value of the transaction should sum to zero.
1568 * Call the trans scrub routine to fix it. Indirectly, this
1569 * routine also performs a number of other transaction fixes too.
1570 */
1571 xaccTransScrubImbalance (trans, nullptr, nullptr);
1572 /* Get the cap gains into a consistent state as well. */
1573
1574 /* Lot Scrubbing is temporarily disabled. */
1575 if (g_getenv("GNC_AUTO_SCRUB_LOTS") != nullptr)
1576 xaccTransScrubGains (trans, nullptr);
1577
1578 /* Allow scrubbing in transaction commit again */
1579 scrub_data = 1;
1580 }
1581
1582 /* Record the time of last modification */
1583 if (0 == trans->date_entered)
1584 {
1585 trans->date_entered = gnc_time(nullptr);
1586 qof_instance_set_dirty(QOF_INSTANCE(trans));
1587 }
1588
1589 trans->txn_type = TXN_TYPE_UNCACHED;
1590 qof_commit_edit_part2(QOF_INSTANCE(trans), trans_on_error,
1591 trans_cleanup_commit, do_destroy);
1592 LEAVE ("(trans=%p)", trans);
1593}
1594
1595/* Ughhh. The Rollback function is terribly complex, and, what's worse,
1596 * it only rolls back the basics. The TransCommit functions did a bunch
1597 * of Lot/Cap-gains scrubbing that don't get addressed/undone here, and
1598 * so the rollback can potentially leave a bit of a mess behind. We
1599 * really need a more robust undo capability. Part of the problem is
1600 * that the biggest user of the undo is the multi-user backend, which
1601 * also adds complexity.
1602 */
1603void
1604xaccTransRollbackEdit (Transaction *trans)
1605{
1606 GList *node, *onode;
1607 QofBackend *be;
1608 Transaction *orig;
1609 GList *slist;
1610 int num_preexist, i;
1611
1612/* FIXME: This isn't quite the right way to handle nested edits --
1613 * there should be a stack of transaction states that are popped off
1614 * and restored at each level -- but it does prevent restoring to the
1615 * editlevel 0 state until one is returning to editlevel 0, and
1616 * thereby prevents a crash caused by trans->orig getting nullptred too
1617 * soon.
1618 */
1619 if (!qof_instance_get_editlevel (QOF_INSTANCE (trans))) return;
1620 if (qof_instance_get_editlevel (QOF_INSTANCE (trans)) > 1) {
1621 qof_instance_decrease_editlevel (QOF_INSTANCE (trans));
1622 return;
1623 }
1624
1625 ENTER ("trans addr=%p\n", trans);
1626
1627 check_open(trans);
1628
1629 /* copy the original values back in. */
1630
1631 orig = trans->orig;
1632 std::swap (trans->num, orig->num);
1633 std::swap (trans->description, orig->description);
1634 trans->date_entered = orig->date_entered;
1635 trans->date_posted = orig->date_posted;
1636 std::swap (trans->common_currency, orig->common_currency);
1637 qof_instance_swap_kvp (QOF_INSTANCE (trans), QOF_INSTANCE (orig));
1638
1639 /* The splits at the front of trans->splits are exactly the same
1640 splits as in the original, but some of them may have changed, so
1641 we restore only those. */
1642/* FIXME: Runs off the transaction's splits, so deleted splits are not
1643 * restored!
1644 */
1645 num_preexist = g_list_length(orig->splits);
1646 slist = g_list_copy(trans->splits);
1647 for (i = 0, node = slist, onode = orig->splits; node;
1648 i++, node = node->next, onode = onode ? onode->next : nullptr)
1649 {
1650 Split *s = GNC_SPLIT(node->data);
1651
1652 if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
1653 continue;
1654
1655 if (i < num_preexist && onode)
1656 {
1657 Split *so = GNC_SPLIT(onode->data);
1658
1659 xaccSplitRollbackEdit(s);
1660 std::swap (s->action, so->action);
1661 std::swap (s->memo, so->memo);
1662 qof_instance_copy_kvp (QOF_INSTANCE (s), QOF_INSTANCE (so));
1663 s->reconciled = so->reconciled;
1664 s->amount = so->amount;
1665 s->value = so->value;
1666 s->lot = so->lot;
1667 s->gains_split = so->gains_split;
1668 //SET_GAINS_A_VDIRTY(s);
1669 s->date_reconciled = so->date_reconciled;
1670 qof_instance_mark_clean(QOF_INSTANCE(s));
1671 }
1672 else
1673 {
1674 /* Potentially added splits */
1675 if (trans != xaccSplitGetParent(s))
1676 {
1677 trans->splits = g_list_remove(trans->splits, s);
1678 /* New split added, but then moved to another
1679 transaction */
1680 continue;
1681 }
1682 xaccSplitRollbackEdit(s);
1683 trans->splits = g_list_remove(trans->splits, s);
1684 g_assert(trans != xaccSplitGetParent(s));
1685 /* NB: our memory management policy here is that a new split
1686 added to the transaction which is then rolled-back still
1687 belongs to the engine. Specifically, it's freed by the
1688 transaction to which it was added. Don't add the Split to
1689 more than one transaction during the begin/commit block! */
1690 if (nullptr == xaccSplitGetParent(s))
1691 {
1692 xaccFreeSplit(s); // a newly malloc'd split
1693 }
1694 }
1695 }
1696 g_list_free(slist);
1697
1698 // orig->splits may still have duped splits so free them
1699 g_list_free_full (orig->splits, (GDestroyNotify)xaccFreeSplit);
1700 orig->splits = nullptr;
1701
1702 /* Now that the engine copy is back to its original version,
1703 * get the backend to fix it in the database */
1707 if (qof_backend_can_rollback (be))
1708 {
1709 QofBackendError errcode;
1710
1711 /* clear errors */
1712 do
1713 {
1714 errcode = qof_backend_get_error (be);
1715 }
1716 while (ERR_BACKEND_NO_ERR != errcode);
1717
1718 qof_backend_rollback_instance (be, &(trans->inst));
1719
1720 errcode = qof_backend_get_error (be);
1721 if (ERR_BACKEND_MOD_DESTROY == errcode)
1722 {
1723 /* The backend is asking us to delete this transaction.
1724 * This typically happens because another (remote) user
1725 * has deleted this transaction, and we haven't found
1726 * out about it until this user tried to edit it.
1727 */
1728 xaccTransDestroy (trans);
1729 do_destroy (QOF_INSTANCE(trans));
1730
1731 /* push error back onto the stack */
1732 qof_backend_set_error (be, errcode);
1733 LEAVE ("deleted trans addr=%p\n", trans);
1734 return;
1735 }
1736 if (ERR_BACKEND_NO_ERR != errcode)
1737 {
1738 PERR ("Rollback Failed. Ouch!");
1739 /* push error back onto the stack */
1740 qof_backend_set_error (be, errcode);
1741 }
1742 }
1743
1745 xaccTransWriteLog (trans, 'R');
1746
1747 xaccFreeTransaction (trans->orig);
1748
1749 trans->orig = nullptr;
1750 qof_instance_set_destroying(trans, FALSE);
1751
1752 /* Put back to zero. */
1753 qof_instance_decrease_editlevel(trans);
1754 /* FIXME: The register code seems to depend on the engine to
1755 generate an event during rollback, even though the state is just
1756 reverting to what it was. */
1757 gen_event_trans (trans);
1758
1759 LEAVE ("trans addr=%p\n", trans);
1760}
1761
1762gboolean
1763xaccTransIsOpen (const Transaction *trans)
1764{
1765 return trans ? (0 < qof_instance_get_editlevel(trans)) : FALSE;
1766}
1767
1768#define SECS_PER_DAY 86400
1769
1770int
1771xaccTransOrder (const Transaction *ta, const Transaction *tb)
1772{
1773 return xaccTransOrder_num_action (ta, nullptr, tb, nullptr);
1774}
1775
1776/* Order a pair of potentially numeric string as numbers if both
1777 * strings begin with numbers, ordering the remainder of the string
1778 * lexically if the numeric parts are equal, and the whole strings
1779 * lexically otherwise.
1780 *
1781 * Note that this won't work well for numbers > 10^18 and that
1782 * negative numbers are treated as strings and will cause the pair to
1783 * be ordered lexically.
1784 */
1785
1786static int
1787order_by_int64_or_string (const char* a, const char* b)
1788{
1789 char *end_a = nullptr, *end_b = nullptr;
1790 int cmp = 0;
1791 uint64_t na = strtoull(a, &end_a, 10);
1792 uint64_t nb = strtoull(b, &end_b, 10);
1793 if (na && nb)
1794 {
1795 if (na != nb)
1796 return na < nb ? -1 : 1;
1797 cmp = g_utf8_collate(end_a, end_b);
1798 }
1799 else
1800 {
1801 cmp = g_utf8_collate(a, b);
1802 }
1803 return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
1804}
1805
1806int
1807xaccTransOrder_num_action (const Transaction *ta, const char *actna,
1808 const Transaction *tb, const char *actnb)
1809{
1810 const char *da, *db;
1811 int retval;
1812
1813 if (ta == tb) return 0;
1814 if (!tb) return -1;
1815 if (!ta) return +1;
1816
1817 if (ta->date_posted != tb->date_posted)
1818 return (ta->date_posted > tb->date_posted) - (ta->date_posted < tb->date_posted);
1819
1820 /* Always sort closing transactions after normal transactions */
1821 {
1822 gboolean ta_is_closing = xaccTransGetIsClosingTxn (ta);
1823 gboolean tb_is_closing = xaccTransGetIsClosingTxn (tb);
1824 if (ta_is_closing != tb_is_closing)
1825 return (ta_is_closing - tb_is_closing);
1826 }
1827
1828 /* otherwise, sort on number string */
1829 if (actna && actnb) /* split action string, if not nullptr */
1830 {
1831 retval = order_by_int64_or_string (actna, actnb);
1832 }
1833 else /* else transaction num string */
1834 {
1835 retval = order_by_int64_or_string (ta->num, tb->num);
1836 }
1837 if (retval)
1838 return retval;
1839
1840 if (ta->date_entered != tb->date_entered)
1841 return (ta->date_entered > tb->date_entered) - (ta->date_entered < tb->date_entered);
1842
1843 /* otherwise, sort on description string */
1844 da = ta->description ? ta->description : "";
1845 db = tb->description ? tb->description : "";
1846 retval = g_utf8_collate (da, db);
1847 if (retval)
1848 return retval;
1849
1850 /* else, sort on guid - keeps sort stable. */
1851 return qof_instance_guid_compare(ta, tb);
1852}
1853
1854/********************************************************************\
1855\********************************************************************/
1856
1857static void
1858set_kvp_string_path (Transaction *txn, const Path& path, const char *value)
1859{
1860 g_return_if_fail (GNC_IS_TRANSACTION(txn));
1861 xaccTransBeginEdit(txn);
1862 auto val = value && *value ? std::make_optional<const char*>(g_strdup(value)) : std::nullopt;
1863 qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(txn), val, path);
1864 qof_instance_set_dirty (QOF_INSTANCE(txn));
1866}
1867
1868static const char*
1869get_kvp_string_path (const Transaction *txn, const Path& path)
1870{
1871 auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(txn), path)};
1872 return rv ? *rv : nullptr;
1873}
1874
1875static inline void
1876xaccTransSetDateInternal(Transaction *trans, time64 *dadate, time64 val)
1877{
1878 xaccTransBeginEdit(trans);
1879
1880#if 0 /* gnc_ctime is expensive so change to 1 only if you need to debug setting
1881 * dates. */
1882 {
1883 time64 secs = (time64) val.tv_sec;
1884 gchar *tstr = gnc_ctime (&secs);
1885 PINFO ("addr=%p set date to %" G_GUINT64_FORMAT ".%09ld %s\n",
1886 trans, val.tv_sec, val.tv_nsec, tstr ? tstr : "(null)");
1887 g_free(tstr);
1888 }
1889#endif
1890 *dadate = val;
1891 qof_instance_set_dirty(QOF_INSTANCE(trans));
1892 mark_trans(trans);
1893 xaccTransCommitEdit(trans);
1894
1895 /* Because the date has changed, we need to make sure that each of
1896 * the splits is properly ordered in each of their accounts. We
1897 * could do that here, simply by reinserting each split into its
1898 * account. However, in some ways this is bad behaviour, and it
1899 * seems much better/nicer to defer that until the commit phase,
1900 * i.e. until the user has called the xaccTransCommitEdit()
1901 * routine. So, for now, we are done. */
1902}
1903
1904static inline void
1905set_gains_date_dirty (Transaction *trans)
1906{
1907 FOR_EACH_SPLIT(trans, s->gains |= GAINS_STATUS_DATE_DIRTY);
1908}
1909
1910void
1911xaccTransSetDatePostedSecs (Transaction *trans, time64 secs)
1912{
1913 if (!trans) return;
1914 xaccTransSetDateInternal(trans, &trans->date_posted, secs);
1915 set_gains_date_dirty(trans);
1916}
1917
1918void
1920{
1921 GDate date;
1922 gnc_gdate_set_time64(&date, time);
1923 xaccTransSetDatePostedGDate(trans, date);
1924}
1925
1926void
1927xaccTransSetDatePostedGDate (Transaction *trans, GDate date)
1928{
1929 if (!trans) return;
1930
1931 /* We additionally save this date into a kvp frame to ensure in
1932 * the future a date which was set as *date* (without time) can
1933 * clearly be distinguished from the time64. */
1934 qof_instance_set_path_kvp<GDate> (QOF_INSTANCE(trans), date, {TRANS_DATE_POSTED});
1935 qof_instance_set_dirty (QOF_INSTANCE(trans));
1936 /* mark dirty and commit handled by SetDateInternal */
1937 xaccTransSetDateInternal(trans, &trans->date_posted,
1938 gdate_to_time64(date));
1939 set_gains_date_dirty (trans);
1940}
1941
1942void
1943xaccTransSetDateEnteredSecs (Transaction *trans, time64 secs)
1944{
1945 if (!trans) return;
1946 xaccTransSetDateInternal(trans, &trans->date_entered, secs);
1947}
1948
1949void
1950xaccTransSetDate (Transaction *trans, int day, int mon, int year)
1951{
1952 if (!trans) return;
1953 GDate date;
1954 g_date_clear (&date, 1);
1955 if (g_date_valid_dmy (day, static_cast<GDateMonth>(mon), year))
1956 g_date_set_dmy (&date, day, static_cast<GDateMonth>(mon), year);
1957 else
1958 {
1959 PWARN("Attempted to set invalid date %d-%d-%d; set today's date instead.",
1960 year, mon, day);
1961 gnc_gdate_set_today (&date);
1962 }
1963 xaccTransSetDatePostedGDate(trans, date);
1964}
1965
1966void
1967xaccTransSetDateDue (Transaction * trans, time64 time)
1968{
1969 if (!trans) return;
1970 xaccTransBeginEdit(trans);
1971 qof_instance_set_path_kvp<Time64> (QOF_INSTANCE (trans), Time64{time}, {TRANS_DATE_DUE_KVP});
1972 qof_instance_set_dirty(QOF_INSTANCE(trans));
1973 xaccTransCommitEdit(trans);
1974}
1975
1976void
1977xaccTransSetTxnType (Transaction *trans, char type)
1978{
1979 char s[2] = {type, '\0'};
1980 set_kvp_string_path (trans, {TRANS_TXN_TYPE_KVP}, s);
1981}
1982
1983void xaccTransClearReadOnly (Transaction *trans)
1984{
1985 set_kvp_string_path (trans, {TRANS_READ_ONLY_REASON}, nullptr);
1986}
1987
1988void
1989xaccTransSetReadOnly (Transaction *trans, const char *reason)
1990{
1991 if (trans && reason)
1992 set_kvp_string_path (trans, {TRANS_READ_ONLY_REASON}, reason);
1993}
1994
1995/********************************************************************\
1996\********************************************************************/
1997
1998/* QOF does not open the trans before setting a parameter,
1999but the call uses check_open so we cannot use the call directly. */
2000static void
2001qofTransSetNum (Transaction *trans, const char *xnum)
2002{
2003 if (!qof_begin_edit(&trans->inst)) return;
2004 xaccTransSetNum(trans, xnum);
2005 qof_commit_edit(&trans->inst);
2006}
2007
2008void
2009xaccTransSetNum (Transaction *trans, const char *xnum)
2010{
2011 if (!trans || !xnum) return;
2012 xaccTransBeginEdit(trans);
2013
2014 CACHE_REPLACE(trans->num, xnum);
2015 qof_instance_set_dirty(QOF_INSTANCE(trans));
2016 mark_trans(trans); /* Dirty balance of every account in trans */
2017 xaccTransCommitEdit(trans);
2018}
2019
2020static void
2021qofTransSetDescription (Transaction *trans, const char *desc)
2022{
2023 if (!qof_begin_edit(&trans->inst)) return;
2024 xaccTransSetDescription(trans, desc);
2025 qof_commit_edit(&trans->inst);
2026}
2027
2028void
2029xaccTransSetDescription (Transaction *trans, const char *desc)
2030{
2031 if (!trans || !desc) return;
2032 xaccTransBeginEdit(trans);
2033
2034 CACHE_REPLACE(trans->description, desc);
2035 qof_instance_set_dirty(QOF_INSTANCE(trans));
2036 xaccTransCommitEdit(trans);
2037}
2038
2039void
2040xaccTransSetDocLink (Transaction *trans, const char *doclink)
2041{
2042 if (!trans || !doclink) return;
2043 set_kvp_string_path (trans, {doclink_uri_str}, doclink);
2044}
2045
2046static void
2047qofTransSetNotes (Transaction *trans, const char *notes)
2048{
2049 if (!qof_begin_edit(&trans->inst)) return;
2050 xaccTransSetNotes(trans, notes);
2051 qof_commit_edit(&trans->inst);
2052}
2053
2054void
2055xaccTransSetNotes (Transaction *trans, const char *notes)
2056{
2057 if (!trans || !notes) return;
2058 set_kvp_string_path (trans, {trans_notes_str}, notes);
2059}
2060
2061void
2062xaccTransSetIsClosingTxn (Transaction *trans, gboolean is_closing)
2063{
2064 xaccTransBeginEdit(trans);
2065 auto val = is_closing ? std::make_optional<int64_t>(1) : std::nullopt;
2066 qof_instance_set_path_kvp<int64_t> (QOF_INSTANCE(trans), val, {trans_is_closing_str});
2067 xaccTransCommitEdit(trans);
2068}
2069
2070
2071/********************************************************************\
2072\********************************************************************/
2073void
2074xaccTransClearSplits(Transaction* trans)
2075{
2076 xaccTransBeginEdit(trans);
2077 /* We only own the splits that still think they belong to us. This is done
2078 in 2 steps. In the first, the splits are marked as being destroyed, but they
2079 are not destroyed yet. In the second, the destruction is committed which will
2080 do the actual destruction. If both steps are done for a split before they are
2081 done for the next split, then a split will still be on the split list after it
2082 has been freed. This can cause other parts of the code (e.g. in xaccSplitDestroy())
2083 to reference the split after it has been freed. */
2084 for (auto node = trans->splits; node; node = node->next)
2085 {
2086 auto s = GNC_SPLIT(node->data);
2087 if (s && s->parent == trans)
2088 {
2090 }
2091 }
2092 for (auto node = trans->splits; node; node = node->next)
2093 {
2094 auto s = GNC_SPLIT(node->data);
2095 if (s && s->parent == trans)
2096 {
2097 xaccSplitCommitEdit(s);
2098 }
2099 }
2100 g_list_free (trans->splits);
2101 trans->splits = nullptr;
2102
2103 xaccTransCommitEdit(trans);
2104}
2105
2106Split *
2107xaccTransGetSplit (const Transaction *trans, int i)
2108{
2109 int j = 0;
2110 if (!trans || i < 0) return nullptr;
2111
2112 FOR_EACH_SPLIT(trans, { if (i == j) return s; j++; });
2113 return nullptr;
2114}
2115
2116int
2117xaccTransGetSplitIndex(const Transaction *trans, const Split *split)
2118{
2119 int j = 0;
2120 g_return_val_if_fail(trans && split, -1);
2121
2122 FOR_EACH_SPLIT(trans, { if (s == split) return j; j++; });
2123 return -1;
2124}
2125
2126SplitList *
2127xaccTransGetSplitList (const Transaction *trans)
2128{
2129 return trans ? trans->splits : nullptr;
2130}
2131
2132SplitList *
2133xaccTransGetPaymentAcctSplitList (const Transaction *trans)
2134{
2135 GList *pay_splits = nullptr;
2136 FOR_EACH_SPLIT (trans,
2137 const Account *account = xaccSplitGetAccount(s);
2138 if (account && gncBusinessIsPaymentAcctType(xaccAccountGetType(account)))
2139 pay_splits = g_list_prepend (pay_splits, s);
2140 );
2141
2142 pay_splits = g_list_reverse (pay_splits);
2143 return pay_splits;
2144}
2145
2146SplitList *
2147xaccTransGetAPARAcctSplitList (const Transaction *trans, gboolean strict)
2148{
2149 GList *apar_splits = nullptr;
2150 if (!trans) return nullptr;
2151
2152 FOR_EACH_SPLIT (trans,
2153 const Account *account = xaccSplitGetAccount(s);
2154 if (account && xaccAccountIsAPARType(xaccAccountGetType(account)))
2155 {
2156
2157 if (!strict)
2158 apar_splits = g_list_prepend (apar_splits, s);
2159 else
2160 {
2161 GncOwner owner;
2162 GNCLot *lot = xaccSplitGetLot(s);
2163 if (lot &&
2165 gncOwnerGetOwnerFromLot (lot, &owner)))
2166 apar_splits = g_list_prepend (apar_splits, s);
2167 }
2168 }
2169 );
2170
2171 apar_splits = g_list_reverse (apar_splits);
2172 return apar_splits;
2173}
2174
2175Split *xaccTransGetFirstPaymentAcctSplit(const Transaction *trans)
2176{
2177 FOR_EACH_SPLIT (trans,
2178 const Account *account = xaccSplitGetAccount(s);
2179 if (account && gncBusinessIsPaymentAcctType(xaccAccountGetType(account)))
2180 return s;
2181 );
2182
2183 return nullptr;
2184}
2185
2186Split *xaccTransGetFirstAPARAcctSplit (const Transaction *trans, gboolean strict)
2187{
2188 FOR_EACH_SPLIT (trans,
2189 const Account *account = xaccSplitGetAccount(s);
2190 if (account && xaccAccountIsAPARType(xaccAccountGetType(account)))
2191 {
2192 GNCLot *lot;
2193 GncOwner owner;
2194
2195 if (!strict)
2196 return s;
2197
2198 lot = xaccSplitGetLot(s);
2199 if (lot &&
2201 gncOwnerGetOwnerFromLot (lot, &owner)))
2202 return s;
2203 }
2204 );
2205
2206 return nullptr;
2207}
2208
2209int
2210xaccTransCountSplits (const Transaction *trans)
2211{
2212 gint i = 0;
2213 g_return_val_if_fail (trans != nullptr, 0);
2214 FOR_EACH_SPLIT(trans, i++);
2215 return i;
2216}
2217
2218const char *
2219xaccTransGetNum (const Transaction *trans)
2220{
2221 return trans ? trans->num : nullptr;
2222}
2223
2224const char *
2225xaccTransGetDescription (const Transaction *trans)
2226{
2227 return trans ? trans->description : nullptr;
2228}
2229
2230const char *
2231xaccTransGetDocLink (const Transaction *trans)
2232{
2233 return get_kvp_string_path (trans, {doclink_uri_str});
2234}
2235
2236const char *
2237xaccTransGetNotes (const Transaction *trans)
2238{
2239 return get_kvp_string_path (trans, {trans_notes_str});
2240}
2241
2242gboolean
2243xaccTransGetIsClosingTxn (const Transaction *trans)
2244{
2245 auto rv{qof_instance_get_path_kvp<int64_t> (QOF_INSTANCE(trans), {trans_is_closing_str})};
2246 return rv ? *rv != 0 : FALSE;
2247}
2248
2249/********************************************************************\
2250\********************************************************************/
2251
2252time64
2253xaccTransGetDate (const Transaction *trans)
2254{
2255 return trans ? trans->date_posted : 0;
2256}
2257
2258/*################## Added for Reg2 #################*/
2259time64
2260xaccTransGetDateEntered (const Transaction *trans)
2261{
2262 return trans ? trans->date_entered : 0;
2263}
2264/*################## Added for Reg2 #################*/
2265
2266time64
2267xaccTransRetDatePosted (const Transaction *trans)
2268{
2269 return trans ? trans->date_posted : 0;
2270}
2271
2272GDate
2273xaccTransGetDatePostedGDate (const Transaction *trans)
2274{
2275 GDate result;
2276 g_date_clear (&result, 1);
2277 if (trans)
2278 {
2279 /* Can we look up this value in the kvp slot? If yes, use it
2280 * from there because it doesn't suffer from time zone
2281 * shifts. */
2282 if (auto res = qof_instance_get_path_kvp<GDate> (QOF_INSTANCE(trans), {TRANS_DATE_POSTED}))
2283 result = *res;
2284 if (! g_date_valid (&result) || gdate_to_time64 (result) == INT64_MAX)
2285 {
2286 /* Well, this txn doesn't have a valid GDate saved in a slot.
2287 * time64_to_gdate() uses local time and we want UTC so we have
2288 * to write it out.
2289 */
2290 time64 time = xaccTransGetDate(trans);
2291 struct tm *stm = gnc_gmtime(&time);
2292 if (stm)
2293 {
2294 g_date_set_dmy(&result, stm->tm_mday,
2295 (GDateMonth)(stm->tm_mon + 1),
2296 stm->tm_year + 1900);
2297 free(stm);
2298 }
2299 }
2300 }
2301 return result;
2302}
2303
2304time64
2305xaccTransRetDateEntered (const Transaction *trans)
2306{
2307 return trans ? trans->date_entered : 0;
2308}
2309
2310time64
2311xaccTransRetDateDue(const Transaction *trans)
2312{
2313 if (!trans) return 0;
2314 auto res = qof_instance_get_path_kvp<Time64> (QOF_INSTANCE (trans), {TRANS_DATE_DUE_KVP});
2315 return res ? res->t : xaccTransRetDatePosted (trans);
2316}
2317
2318char
2319xaccTransGetTxnType (Transaction *trans)
2320{
2321 gboolean has_nonAPAR_split = FALSE;
2322
2323 if (!trans) return TXN_TYPE_NONE;
2324
2325 if (trans->txn_type != TXN_TYPE_UNCACHED)
2326 return trans->txn_type;
2327
2328 trans->txn_type = TXN_TYPE_NONE;
2329 for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
2330 {
2331 Account *acc = xaccSplitGetAccount (GNC_SPLIT(n->data));
2332
2333 if (!acc)
2334 continue;
2335
2337 has_nonAPAR_split = TRUE;
2338 else if (trans->txn_type == TXN_TYPE_NONE)
2339 {
2340 GNCLot *lot = xaccSplitGetLot (GNC_SPLIT(n->data));
2341 GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
2342 GncOwner owner;
2343
2344 if (invoice && trans == gncInvoiceGetPostedTxn (invoice))
2345 trans->txn_type = TXN_TYPE_INVOICE;
2346 else if (invoice || gncOwnerGetOwnerFromLot (lot, &owner))
2347 trans->txn_type = TXN_TYPE_PAYMENT;
2348 }
2349 }
2350
2351 if (!has_nonAPAR_split && (trans->txn_type == TXN_TYPE_PAYMENT))
2352 trans->txn_type = TXN_TYPE_LINK;
2353
2354 return trans->txn_type;
2355}
2356
2357const char *
2358xaccTransGetReadOnly (Transaction *trans)
2359{
2360 return get_kvp_string_path (trans, {TRANS_READ_ONLY_REASON});
2361}
2362
2363static gboolean
2364xaccTransIsSXTemplate (const Transaction * trans)
2365{
2366 Split *split0 = xaccTransGetSplit (trans, 0);
2367 if (split0 != nullptr)
2368 {
2369 char* formula = nullptr;
2370 g_object_get (split0, "sx-debit-formula", &formula, nullptr);
2371 if (formula != nullptr)
2372 {
2373 g_free (formula);
2374 return TRUE;
2375 }
2376 g_object_get (split0, "sx-credit-formula", &formula, nullptr);
2377 if (formula != nullptr)
2378 {
2379 g_free (formula);
2380 return TRUE;
2381 }
2382 }
2383 return FALSE;
2384}
2385
2386gboolean xaccTransIsReadonlyByPostedDate(const Transaction *trans)
2387{
2388 GDate *threshold_date;
2389 GDate trans_date;
2390 const QofBook *book = xaccTransGetBook (trans);
2391 gboolean result;
2392 g_assert(trans);
2393
2394 if (!qof_book_uses_autoreadonly(book))
2395 {
2396 return FALSE;
2397 }
2398
2399 if (xaccTransIsSXTemplate (trans))
2400 return FALSE;
2401
2402 threshold_date = qof_book_get_autoreadonly_gdate(book);
2403 g_assert(threshold_date); // ok because we checked uses_autoreadonly before
2404 trans_date = xaccTransGetDatePostedGDate(trans);
2405
2406// g_warning("there is auto-read-only with days=%d, trans_date_day=%d, threshold_date_day=%d",
2407// qof_book_get_num_days_autofreeze(book),
2408// g_date_get_day(&trans_date),
2409// g_date_get_day(threshold_date));
2410
2411 if (g_date_compare(&trans_date, threshold_date) < 0)
2412 {
2413 //g_warning("we are auto-read-only");
2414 result = TRUE;
2415 }
2416 else
2417 {
2418 result = FALSE;
2419 }
2420 g_date_free(threshold_date);
2421 return result;
2422}
2423
2424gboolean
2426 const Account *account)
2427{
2428 GList *node;
2429
2430 for (node = xaccTransGetSplitList (trans); node; node = node->next)
2431 {
2432 Split *split = GNC_SPLIT(node->data);
2433
2434 if (!xaccTransStillHasSplit(trans, split))
2435 continue;
2436 if (account && (xaccSplitGetAccount(split) != account))
2437 continue;
2438
2439 switch (xaccSplitGetReconcile (split))
2440 {
2441 case YREC:
2442 case FREC:
2443 return TRUE;
2444
2445 default:
2446 break;
2447 }
2448 }
2449
2450 return FALSE;
2451}
2452
2453gboolean
2454xaccTransHasReconciledSplits (const Transaction *trans)
2455{
2456 return xaccTransHasReconciledSplitsByAccount (trans, nullptr);
2457}
2458
2459
2460gboolean
2461xaccTransHasSplitsInStateByAccount (const Transaction *trans,
2462 const char state,
2463 const Account *account)
2464{
2465 GList *node;
2466
2467 for (node = xaccTransGetSplitList (trans); node; node = node->next)
2468 {
2469 Split *split = GNC_SPLIT(node->data);
2470
2471 if (!xaccTransStillHasSplit(trans, split))
2472 continue;
2473 if (account && (xaccSplitGetAccount(split) != account))
2474 continue;
2475
2476 if (split->reconciled == state)
2477 return TRUE;
2478 }
2479
2480 return FALSE;
2481}
2482
2483gboolean
2484xaccTransHasSplitsInState (const Transaction *trans, const char state)
2485{
2486 return xaccTransHasSplitsInStateByAccount (trans, state, nullptr);
2487}
2488
2489
2490/********************************************************************\
2491\********************************************************************/
2492
2493
2494/* ====================================================================== */
2495
2496static int
2497counter_thunk(Transaction *t, void *data)
2498{
2499 (*((guint*)data))++;
2500 return 0;
2501}
2502
2503guint
2505{
2506 guint count = 0;
2507 xaccAccountTreeForEachTransaction(gnc_book_get_root_account(book),
2508 counter_thunk, (void*)&count);
2509 return count;
2510}
2511
2512/********************************************************************\
2513\********************************************************************/
2514
2515void
2516xaccTransVoid(Transaction *trans, const char *reason)
2517{
2518 g_return_if_fail(trans && reason);
2519
2520 /* Prevent voiding transactions that are already marked
2521 * read only, for example generated by the business features.
2522 */
2523 if (xaccTransGetReadOnly (trans))
2524 {
2525 PWARN ("Refusing to void a read-only transaction!");
2526 return;
2527 }
2528 xaccTransBeginEdit(trans);
2529
2530 char iso8601_str[ISO_DATELENGTH + 1] = "";
2531 gnc_time64_to_iso8601_buff (gnc_time(nullptr), iso8601_str);
2532
2533 if (auto s = get_kvp_string_path (trans, {trans_notes_str}))
2534 set_kvp_string_path (trans, {void_former_notes_str}, s);
2535 set_kvp_string_path (trans, {trans_notes_str}, _("Voided transaction"));
2536 set_kvp_string_path (trans, {void_reason_str}, reason);
2537 set_kvp_string_path (trans, {void_time_str}, iso8601_str);
2538
2539 FOR_EACH_SPLIT(trans, xaccSplitVoid(s));
2540
2541 /* Dirtying taken care of by SetReadOnly */
2542 xaccTransSetReadOnly(trans, _("Transaction Voided"));
2543 xaccTransCommitEdit(trans);
2544}
2545
2546gboolean
2547xaccTransGetVoidStatus(const Transaction *trans)
2548{
2549 auto t = xaccTransGetVoidTime (trans);
2550 return t != INT64_MAX;
2551}
2552
2553const char *
2554xaccTransGetVoidReason(const Transaction *trans)
2555{
2556 return get_kvp_string_path (trans, {void_reason_str});
2557}
2558
2559time64
2560xaccTransGetVoidTime(const Transaction *tr)
2561{
2562 auto void_str{get_kvp_string_path (tr, {void_time_str})};
2563 return void_str ? gnc_iso8601_to_time64_gmt (void_str) : INT64_MAX;
2564}
2565
2566void
2567xaccTransUnvoid (Transaction *trans)
2568{
2569 g_return_if_fail(trans);
2570
2571 if (!xaccTransGetVoidStatus (trans))
2572 return; /* Transaction isn't voided. Bail. */
2573
2574 xaccTransBeginEdit(trans);
2575
2576 set_kvp_string_path (trans, {trans_notes_str}, get_kvp_string_path (trans, {void_former_notes_str}));
2577 set_kvp_string_path (trans, {void_former_notes_str}, nullptr);
2578 set_kvp_string_path (trans, {void_reason_str}, nullptr);
2579 set_kvp_string_path (trans, {void_time_str}, nullptr);
2580
2581 FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
2582
2583 /* Dirtying taken care of by ClearReadOnly */
2584 xaccTransClearReadOnly(trans);
2585 xaccTransCommitEdit(trans);
2586}
2587
2588Transaction *
2589xaccTransReverse (Transaction *orig)
2590{
2591 Transaction *trans;
2592 g_return_val_if_fail(orig, nullptr);
2593
2594 /* First edit, dirty, and commit orig to ensure that any trading
2595 * splits are correctly balanced.
2596 */
2597 xaccTransBeginEdit (orig);
2598 qof_instance_set_dirty (QOF_INSTANCE (orig));
2599 xaccTransCommitEdit (orig);
2600
2601 trans = xaccTransClone(orig);
2602 g_return_val_if_fail (trans, nullptr);
2603 xaccTransBeginEdit(trans);
2604
2605 /* Reverse the values on each split. Clear per-split info. */
2606 FOR_EACH_SPLIT(trans,
2607 {
2611 });
2612
2613 /* Now update the original with a pointer to the new one */
2614 qof_instance_set_path_kvp<GncGUID*> (QOF_INSTANCE (orig), guid_copy(xaccTransGetGUID(trans)),
2615 {TRANS_REVERSED_BY});
2616
2617 /* Make sure the reverse transaction is not read-only */
2618 xaccTransClearReadOnly(trans);
2619
2620 qof_instance_set_dirty(QOF_INSTANCE(trans));
2621 xaccTransCommitEdit(trans);
2622 return trans;
2623}
2624
2625Transaction *
2626xaccTransGetReversedBy(const Transaction *trans)
2627{
2628 g_return_val_if_fail(trans, nullptr);
2629 auto g = qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(trans), {TRANS_REVERSED_BY});
2630 return g ? xaccTransLookup (*g, qof_instance_get_book (trans)) : nullptr;
2631}
2632
2633/* ============================================================== */
2646static void
2647xaccTransScrubGainsDate (Transaction *trans)
2648{
2649 SplitList *node;
2650 SplitList *splits_copy = g_list_copy(trans->splits);
2651 for (node = splits_copy; node; node = node->next)
2652 {
2653 Split *s = GNC_SPLIT(node->data);
2654
2655 if (!xaccTransStillHasSplit(trans, s)) continue;
2656 xaccSplitDetermineGainStatus(s);
2657
2658 if ((GAINS_STATUS_GAINS & s->gains) &&
2659 s->gains_split &&
2660 ((s->gains_split->gains & GAINS_STATUS_DATE_DIRTY) ||
2661 (s->gains & GAINS_STATUS_DATE_DIRTY)))
2662 {
2663 Transaction *source_trans = s->gains_split->parent;
2664 s->gains &= ~GAINS_STATUS_DATE_DIRTY;
2665 s->gains_split->gains &= ~GAINS_STATUS_DATE_DIRTY;
2666 xaccTransSetDatePostedSecs(trans, source_trans->date_posted);
2667 FOR_EACH_SPLIT(trans, s->gains &= ~GAINS_STATUS_DATE_DIRTY);
2668 }
2669 }
2670 g_list_free(splits_copy);
2671}
2672
2673/* ============================================================== */
2674
2675void
2676xaccTransScrubGains (Transaction *trans, Account *gain_acc)
2677{
2678 SplitList *node;
2679
2680 ENTER("(trans=%p)", trans);
2681 /* Lock down posted date, its to be synced to the posted date
2682 * for the source of the cap gains. */
2683 xaccTransScrubGainsDate(trans);
2684
2685 /* Fix up the split amount */
2686restart:
2687 for (node = trans->splits; node; node = node->next)
2688 {
2689 Split *s = GNC_SPLIT(node->data);
2690
2691 if (!xaccTransStillHasSplit(trans, s)) continue;
2692
2693 xaccSplitDetermineGainStatus(s);
2694 if (s->gains & GAINS_STATUS_ADIRTY)
2695 {
2696 gboolean altered = FALSE;
2697 s->gains &= ~GAINS_STATUS_ADIRTY;
2698 if (s->lot)
2699 altered = xaccScrubLot(s->lot);
2700 else
2701 altered = xaccSplitAssign(s);
2702 if (altered) goto restart;
2703 }
2704 }
2705
2706 /* Fix up gains split value */
2707 FOR_EACH_SPLIT(trans,
2708 if ((s->gains & GAINS_STATUS_VDIRTY) ||
2709 (s->gains_split &&
2710 (s->gains_split->gains & GAINS_STATUS_VDIRTY)))
2711 xaccSplitComputeCapGains(s, gain_acc);
2712 );
2713
2714 LEAVE("(trans=%p)", trans);
2715}
2716
2717Split *
2718xaccTransFindSplitByAccount(const Transaction *trans, const Account *acc)
2719{
2720 if (!trans || !acc) return nullptr;
2721 FOR_EACH_SPLIT(trans, if (xaccSplitGetAccount(s) == acc) return s);
2722 return nullptr;
2723}
2724
2725static void
2726record_price (Split *split,
2727 PriceSource source)
2728{
2729 Transaction *trans;
2730 Account *account;
2731 QofBook* book;
2732 GNCPriceDB* pricedb;
2733 gnc_commodity* comm;
2734 gnc_commodity* curr;
2735 GNCPrice* price;
2736 gnc_numeric price_value, value, amount;
2737 int scu;
2738 time64 time;
2739 gboolean swap;
2740
2741 account = xaccSplitGetAccount (split);
2742 if (!xaccAccountIsPriced (account))
2743 {
2744 return;
2745 }
2746 amount = xaccSplitGetAmount (split);
2747 if (gnc_numeric_zero_p (amount))
2748 {
2749 return;
2750 }
2751 trans = xaccSplitGetParent (split);
2752 value = gnc_numeric_div (xaccSplitGetValue (split), amount,
2754 GNC_HOW_DENOM_EXACT);
2755 book = qof_instance_get_book (QOF_INSTANCE (account));
2756 pricedb = gnc_pricedb_get_db (book);
2757 comm = xaccAccountGetCommodity (account);
2758 curr = xaccTransGetCurrency (trans);
2759 scu = gnc_commodity_get_fraction (curr);
2760 swap = FALSE;
2761 time = xaccTransGetDate (trans);
2762 price = gnc_pricedb_lookup_day_t64 (pricedb, comm, curr, time);
2763 if (gnc_commodity_equiv (comm, gnc_price_get_currency (price)))
2764 swap = TRUE;
2765
2766 if (price)
2767 {
2768 PriceSource oldsource = gnc_price_get_source (price);
2769 price_value = gnc_price_get_value (price);
2770 if (gnc_numeric_equal (swap ? gnc_numeric_invert (value) : value,
2771 price_value))
2772 {
2773 gnc_price_unref (price);
2774 return;
2775 }
2776 if (oldsource < source &&
2777 !(oldsource == PRICE_SOURCE_XFER_DLG_VAL &&
2778 source == PRICE_SOURCE_SPLIT_REG))
2779 {
2780 /* Existing price is preferred over this one. */
2781 gnc_price_unref (price);
2782 return;
2783 }
2784 if (swap)
2785 {
2786 value = gnc_numeric_invert (value);
2787 scu = gnc_commodity_get_fraction (comm);
2788 }
2789 value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT,
2790 GNC_HOW_RND_ROUND_HALF_UP);
2791 gnc_price_begin_edit (price);
2792 gnc_price_set_time64 (price, time);
2793 gnc_price_set_source (price, source);
2794 gnc_price_set_typestr (price, PRICE_TYPE_TRN);
2795 gnc_price_set_value (price, value);
2796 gnc_price_commit_edit (price);
2797 gnc_price_unref (price);
2798 return;
2799 }
2800
2801 value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT,
2802 GNC_HOW_RND_ROUND_HALF_UP);
2803 price = gnc_price_create (book);
2804 gnc_price_begin_edit (price);
2805 gnc_price_set_commodity (price, comm);
2806 gnc_price_set_currency (price, curr);
2807 gnc_price_set_time64 (price, time);
2808 gnc_price_set_source (price, source);
2809 gnc_price_set_typestr (price, PRICE_TYPE_TRN);
2810 gnc_price_set_value (price, value);
2811 gnc_pricedb_add_price (pricedb, price);
2812 gnc_price_commit_edit (price);
2813}
2814
2815void
2816xaccTransRecordPrice (Transaction *trans, PriceSource source)
2817{
2818 /* XXX: This should have been part of xaccSplitCommitEdit. */
2819 g_list_foreach (xaccTransGetSplitList (trans), (GFunc)record_price, (gpointer)source);
2820}
2821
2822/********************************************************************\
2823\********************************************************************/
2824/* QofObject function implementation */
2825
2826static void
2827destroy_tx_on_book_close(QofInstance *ent, gpointer data)
2828{
2829 Transaction* tx = GNC_TRANSACTION(ent);
2830
2831 xaccTransDestroy(tx);
2832}
2833
2834static int
2835trans_reverse_order (const Transaction* a, const Transaction* b)
2836{
2837 return xaccTransOrder (b, a);
2838}
2839
2844static void
2845gnc_transaction_book_end(QofBook* book)
2846{
2847 QofCollection *col;
2848
2849 col = qof_book_get_collection(book, GNC_ID_TRANS);
2850
2851 // destroy all transactions from latest to earliest, because
2852 // accounts' splits are stored chronologically and removing from
2853 // the end is faster than from the middle.
2854 qof_collection_foreach_sorted (col, destroy_tx_on_book_close, nullptr,
2855 (GCompareFunc)trans_reverse_order);
2856}
2857
2858#ifdef _MSC_VER
2859/* MSVC compiler doesn't have C99 "designated initializers"
2860 * so we wrap them in a macro that is empty on MSVC. */
2861# define DI(x) /* */
2862#else
2863# define DI(x) x
2864#endif
2865
2866/* Hook into the QofObject registry */
2867static QofObject trans_object_def =
2868{
2869 DI(.interface_version = ) QOF_OBJECT_VERSION,
2870 DI(.e_type = ) GNC_ID_TRANS,
2871 DI(.type_label = ) "Transaction",
2872 DI(.create = ) (void* (*)(QofBook*))xaccMallocTransaction,
2873 DI(.book_begin = ) nullptr,
2874 DI(.book_end = ) gnc_transaction_book_end,
2875 DI(.is_dirty = ) qof_collection_is_dirty,
2876 DI(.mark_clean = ) qof_collection_mark_clean,
2877 DI(.foreach = ) qof_collection_foreach,
2878 DI(.printable = ) (const char * (*)(gpointer)) xaccTransGetDescription,
2879 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
2880};
2881
2882static gboolean
2883trans_is_balanced_p (const Transaction *trans)
2884{
2885 return trans ? xaccTransIsBalanced(trans) : FALSE;
2886}
2887
2888gboolean xaccTransRegister (void)
2889{
2890 static QofParam params[] =
2891 {
2892 {
2893 TRANS_NUM, QOF_TYPE_STRING,
2895 (QofSetterFunc)qofTransSetNum,
2897 },
2898 {
2899 TRANS_DESCRIPTION, QOF_TYPE_STRING,
2901 (QofSetterFunc)qofTransSetDescription
2902 },
2903 {
2904 TRANS_DATE_ENTERED, QOF_TYPE_DATE,
2907 },
2908 {
2909 TRANS_DATE_POSTED, QOF_TYPE_DATE,
2912 },
2913 {
2914 TRANS_DATE_DUE, QOF_TYPE_DATE,
2916 },
2917 {
2918 TRANS_IMBALANCE, QOF_TYPE_NUMERIC,
2920 },
2921 {
2922 TRANS_NOTES, QOF_TYPE_STRING,
2924 (QofSetterFunc)qofTransSetNotes
2925 },
2926 {
2927 TRANS_DOCLINK, QOF_TYPE_STRING,
2930 },
2931 {
2932 TRANS_IS_CLOSING, QOF_TYPE_BOOLEAN,
2934 },
2935 {
2936 TRANS_IS_BALANCED, QOF_TYPE_BOOLEAN,
2937 (QofAccessFunc)trans_is_balanced_p, nullptr
2938 },
2939 {
2940 TRANS_TYPE, QOF_TYPE_CHAR,
2943 },
2944 {
2945 TRANS_VOID_STATUS, QOF_TYPE_BOOLEAN,
2947 },
2948 {
2949 TRANS_VOID_REASON, QOF_TYPE_STRING,
2951 },
2952 {
2953 TRANS_VOID_TIME, QOF_TYPE_DATE,
2955 },
2956 {
2957 TRANS_SPLITLIST, GNC_ID_SPLIT,
2959 },
2960 {
2961 QOF_PARAM_BOOK, QOF_ID_BOOK,
2963 },
2964 {
2965 QOF_PARAM_GUID, QOF_TYPE_GUID,
2967 },
2968 { nullptr },
2969 };
2970
2971 qof_class_register (GNC_ID_TRANS, (QofSortFunc)xaccTransOrder, params);
2972
2973 return qof_object_register (&trans_object_def);
2974}
2975
2977_utest_trans_fill_functions (void)
2978{
2979 TransTestFunctions *func = g_new (TransTestFunctions, 1);
2980
2981 func->mark_trans = mark_trans;
2982 func->gen_event_trans = gen_event_trans;
2983 func->xaccFreeTransaction = xaccFreeTransaction;
2984 func->destroy_gains = destroy_gains;
2985 func->do_destroy = do_destroy;
2986 func->was_trans_emptied = was_trans_emptied;
2987 func->trans_on_error = trans_on_error;
2988 func->trans_cleanup_commit = trans_cleanup_commit;
2989 func->xaccTransScrubGainsDate = xaccTransScrubGainsDate;
2990 func->dupe_trans = dupe_trans;
2991 return func;
2992}
2993
2994/************************ END OF ************************************\
2995\************************* FILE *************************************/
This is the private header for the account structure.
Scheduled Transactions public handling routines.
High-Level API for imposing Lot constraints.
convert single-entry accounts to clean double-entry
API for the transaction logger.
Utilities to Automatically Compute Capital Gains/Losses.
Commodity handling public routines.
Date and Time handling routines.
All type declarations for the whole Gnucash engine.
Additional event handling code.
-- Business Helper Functions
Business Invoice Interface.
Business Interface: Object OWNERs.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2745
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false.
Definition Account.cpp:4551
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
@ ACCT_TYPE_TRADING
Account used to record multiple commodity transactions.
Definition Account.h:155
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition qofbackend.h:58
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
Definition qofbook.cpp:440
QofBackendError qof_backend_get_error(QofBackend *qof_be)
Get the last backend error.
@ ERR_BACKEND_MOD_DESTROY
commit of object update failed because another user has deleted the object
Definition qofbackend.h:77
@ ERR_BACKEND_MODIFIED
commit of object update failed because another user has modified the object
Definition qofbackend.h:75
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition qofbook.cpp:521
gboolean qof_book_shutting_down(const QofBook *book)
Is the book shutting down?
Definition qofbook.cpp:447
GDate * qof_book_get_autoreadonly_gdate(const QofBook *book)
Returns the GDate that is the threshold for auto-read-only.
Definition qofbook.cpp:1006
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
gboolean qof_book_uses_autoreadonly(const QofBook *book)
Returns TRUE if the auto-read-only feature should be used, otherwise FALSE.
Definition qofbook.cpp:974
gboolean qof_book_use_trading_accounts(const QofBook *book)
Returns flag indicating whether this book uses trading accounts.
Definition qofbook.cpp:921
gboolean xaccSplitAssign(Split *split)
The`xaccSplitAssign() routine will take the indicated split and, if it doesn't already belong to a lo...
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
The xaccSplitComputeCapGains() routine computes the cap gains or losses for the indicated split.
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition qofclass.cpp:86
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition qofclass.h:168
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
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_printname(const gnc_commodity *cm)
Retrieve the 'print' name for the specified commodity.
MonetaryList * gnc_monetary_list_delete_zeros(MonetaryList *list)
Delete all entries in the list that have zero value.
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the 'unique' name for the specified commodity.
char * gnc_time64_to_iso8601_buff(time64 time, char *buff)
The gnc_time64_to_iso8601_buff() routine takes the input UTC time64 value and prints it as an ISO-860...
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition gnc-date.h:108
char * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
Definition gnc-date.cpp:256
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
size_t qof_print_date_buff(char *buff, const size_t len, time64 t)
Convenience: calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:574
void gnc_gdate_set_today(GDate *gd)
Set a GDate to the current day.
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
struct tm * gnc_gmtime(const time64 *secs)
fill out a time struct from a 64-bit time value
Definition gnc-date.cpp:178
gboolean xaccTransHasSplitsInStateByAccount(const Transaction *trans, const char state, const Account *account)
FIXME: document me.
void xaccTransSetDate(Transaction *trans, int day, int mon, int year)
The xaccTransSetDate() method does the same thing as xaccTransSetDate[Posted]Secs(),...
gboolean xaccTransGetIsClosingTxn(const Transaction *trans)
Returns whether this transaction is a "closing transaction".
Transaction * xaccTransReverse(Transaction *orig)
xaccTransReverse creates a Transaction that reverses the given transaction by inverting all the numer...
gboolean xaccTransHasSplitsInState(const Transaction *trans, const char state)
FIXME: document me.
SplitList * xaccTransGetPaymentAcctSplitList(const Transaction *trans)
The xaccTransGetPaymentAcctSplitList() method returns a GList of the splits in a transaction that bel...
const char * xaccTransGetVoidReason(const Transaction *trans)
Returns the user supplied textual reason why a transaction was voided.
guint gnc_book_count_transactions(QofBook *book)
int xaccTransOrder(const Transaction *ta, const Transaction *tb)
The xaccTransOrder(ta,tb) method is useful for sorting.
Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans)
Copy a transaction to the 'clipboard' transaction using dupe_transaction.
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
time64 xaccTransRetDateEntered(const Transaction *trans)
Retrieve the date of when the transaction was entered.
Transaction * xaccTransGetReversedBy(const Transaction *trans)
Returns the transaction that reversed the given transaction.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
void xaccTransSetIsClosingTxn(Transaction *trans, gboolean is_closing)
Sets whether or not this transaction is a "closing transaction".
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
char xaccTransGetTxnType(Transaction *trans)
Returns the Transaction Type: note this type will be derived from the transaction splits,...
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccTransSetDatePostedGDate(Transaction *trans, GDate date)
This method modifies posted date of the transaction, specified by a GDate.
gboolean xaccTransHasReconciledSplitsByAccount(const Transaction *trans, const Account *account)
FIXME: document me.
void xaccTransSetNum(Transaction *trans, const char *xnum)
Sets the transaction Number (or ID) field; rather than use this function directly,...
const char * xaccTransGetDocLink(const Transaction *trans)
Gets the transaction Document Link.
GDate xaccTransGetDatePostedGDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void xaccTransSetTxnType(Transaction *trans, char type)
Set the Transaction Type: note the type will be saved into the Transaction kvp property as a backward...
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.
MonetaryList * xaccTransGetImbalance(const Transaction *trans)
The xaccTransGetImbalance method returns a list giving the value of the transaction in each currency ...
Split * xaccTransGetFirstPaymentAcctSplit(const Transaction *trans)
The xaccTransGetFirstPaymentAcctSplit() method returns a pointer to the first split in this transacti...
#define GNC_INVOICE_ID
STRING CONSTANTS ********************************************** Used to declare constant KVP keys use...
Definition gnc-engine.h:257
void xaccTransCopyFromClipBoard(const Transaction *from_trans, Transaction *to_trans, const Account *from_acc, Account *to_acc, gboolean no_date)
This function explicitly must robustly handle some unusual input.
void xaccTransClearSplits(Transaction *trans)
Remove all splits from the transaction.
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition Account.cpp:4527
#define TXN_TYPE_NONE
No transaction type
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing.
gboolean xaccTransIsBalanced(const Transaction *trans)
Returns true if the transaction is balanced according to the rules currently in effect.
#define xaccTransGetGUID(X)
#define TXN_TYPE_INVOICE
Transaction is an invoice.
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id,...
int xaccTransGetSplitIndex(const Transaction *trans, const Split *split)
Inverse of xaccTransGetSplit()
gboolean xaccTransHasReconciledSplits(const Transaction *trans)
FIXME: document me.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction's split list.
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
time64 xaccTransGetVoidTime(const Transaction *tr)
Returns the time that a transaction was voided.
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.
time64 xaccTransGetDateEntered(const Transaction *trans)
Retrieve the date of when the transaction was entered.
void xaccTransUnvoid(Transaction *trans)
xaccTransUnvoid restores a voided transaction to its original state.
void xaccTransCopyOnto(const Transaction *from_trans, Transaction *to_trans)
Copy a transaction to another using the function below without changing any account information.
int xaccTransOrder_num_action(const Transaction *ta, const char *actna, const Transaction *tb, const char *actnb)
The xaccTransOrder_num_action(ta,actna,tb,actnb) method is useful for sorting.
Transaction * xaccTransClone(const Transaction *from)
The xaccTransClone() method will create a complete copy of an existing transaction.
void xaccTransSetDatePostedSecs(Transaction *trans, time64 secs)
The xaccTransSetDatePostedSecs() method will modify the posted date of the transaction,...
void xaccTransSetDocLink(Transaction *trans, const char *doclink)
Sets the transaction Document Link.
gnc_numeric xaccTransGetAccountValue(const Transaction *trans, const Account *acc)
The xaccTransGetAccountValue() method returns the total value applied to a particular account.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3)).
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
void xaccTransScrubGains(Transaction *trans, Account *gain_acc)
The xaccTransScrubGains() routine performs a number of cleanup functions on the indicated transaction...
gnc_numeric xaccTransGetAccountAmount(const Transaction *trans, const Account *acc)
Same as xaccTransGetAccountValue, but uses the Account's commodity.
void xaccTransSortSplits(Transaction *trans)
Sorts the splits in a transaction, putting the debits first, followed by the credits.
#define xaccTransGetBook(X)
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
const char * xaccTransGetNum(const Transaction *trans)
Gets the transaction Number (or ID) field; rather than use this function directly,...
void xaccTransVoid(Transaction *trans, const char *reason)
xaccTransVoid voids a transaction.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
int xaccTransCountSplits(const Transaction *trans)
Returns the number of splits in this transaction.
Transaction * xaccTransCloneNoKvp(const Transaction *from)
The xaccTransCloneNoKvp() method will create a complete copy of an existing transaction except that t...
time64 xaccTransRetDateDue(const Transaction *trans)
Dates and txn-type for A/R and A/P "invoice" postings.
void xaccTransRollbackEdit(Transaction *trans)
The xaccTransRollbackEdit() routine rejects all edits made, and sets the transaction back to where it...
void xaccTransRecordPrice(Transaction *trans, PriceSource source)
The xaccTransRecordPrice() method iterates through the splits and and record the non-currency equival...
void xaccTransSetReadOnly(Transaction *trans, const char *reason)
Set the transaction to be ReadOnly by setting a non-NULL value as "reason".
Split * xaccTransGetFirstAPARAcctSplit(const Transaction *trans, gboolean strict)
The xaccTransGetFirstPaymentAcctSplit() method returns a pointer to the first split in this transacti...
void xaccTransSetDateDue(Transaction *trans, time64 time)
Dates and txn-type for A/R and A/P "invoice" postings.
gboolean xaccTransIsReadonlyByPostedDate(const Transaction *trans)
Returns TRUE if this Transaction is read-only because its posted-date is older than the "auto-readonl...
GList SplitList
GList of Split.
Definition gnc-engine.h:207
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.
SplitList * xaccTransGetAPARAcctSplitList(const Transaction *trans, gboolean strict)
The xaccTransGetAPARSplitList() method returns a GList of the splits in a transaction that belong to ...
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
gnc_numeric xaccTransGetAccountBalance(const Transaction *trans, const Account *account)
Get the account balance for the specified account after the last split in the specified transaction.
#define TXN_TYPE_PAYMENT
Transaction is a payment
#define TXN_TYPE_LINK
Transaction is a link between (invoice and payment) lots
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gboolean xaccTransEqual(const Transaction *ta, const Transaction *tb, gboolean check_guids, gboolean check_splits, gboolean check_balances, gboolean assume_ordered)
Equality.
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition qofid.cpp:209
void qof_collection_foreach_sorted(const QofCollection *col, QofInstanceForeachCB cb_func, gpointer user_data, GCompareFunc sort_fn)
Call the callback for each entity in the collection.
Definition qofid.cpp:283
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
Definition qofid.cpp:232
void qof_event_resume(void)
Resume engine event generation.
Definition qofevent.cpp:156
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition qofevent.cpp:231
void qof_event_suspend(void)
Suspend all engine events.
Definition qofevent.cpp:145
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition guid.cpp:165
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition guid.cpp:155
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
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
void qof_instance_copy_book(gpointer ptr1, gconstpointer ptr2)
Copy the book from one QofInstances to another.
#define qof_instance_is_dirty
Return value of is_dirty flag.
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
guint32 qof_instance_get_idata(gconstpointer inst)
get the instance tag number used for kvp management in sql backends.
const GncGUID * qof_entity_get_guid(gconstpointer ent)
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
void qof_instance_get_kvp(QofInstance *, GValue *value, unsigned count,...)
Retrieves the contents of a KVP slot into a provided GValue.
void qof_instance_set_kvp(QofInstance *, GValue const *value, unsigned count,...)
Sets a KVP slot to a value from a GValue.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
#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...
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
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_ARG
Argument is not a valid number.
@ GNC_HOW_RND_ROUND_HALF_UP
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
@ GNC_HOW_DENOM_EXACT
Use any denominator which gives an exactly correct ratio of numerator to denominator.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition qofid.cpp:238
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition qofobject.h:63
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
gboolean gncOwnerGetOwnerFromLot(GNCLot *lot, GncOwner *owner)
Get the owner from the lot.
Definition gncOwner.c:636
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
GNCPrice * gnc_pricedb_lookup_day_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commodities on the indicated day.
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
PriceSource
Price source enum.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition qofquery.h:108
int qof_string_number_compare_func(gpointer a, gpointer b, gint options, QofParam *getter)
Compare two parameter(strings) as if they are numbers! the two objects, a and b, are the objects bein...
gboolean xaccScrubLot(GNCLot *lot)
The xaccScrubLot() routine makes sure that the indicated lot is self-consistent and properly balanced...
Definition Scrub3.cpp:85
void xaccTransScrubImbalance(Transaction *trans, Account *root, Account *account)
Correct transaction imbalances.
Definition Scrub.cpp:845
void xaccTransWriteLog(Transaction *trans, char flag)
Definition TransLog.cpp:222
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
void xaccSplitSetSharePrice(Split *s, gnc_numeric price)
Definition Split.cpp:1205
gint xaccSplitOrder(const Split *sa, const Split *sb)
The xaccSplitOrder(sa,sb) method is useful for sorting.
Definition Split.cpp:1536
#define xaccSplitGetGUID(X)
Definition Split.h:579
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
Split * xaccMallocSplit(QofBook *book)
Constructor.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Definition Split.cpp:1920
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
gboolean xaccSplitEqual(const Split *sa, const Split *sb, gboolean check_guids, gboolean check_balances, gboolean check_txn_splits)
Equality.
Definition Split.cpp:819
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
void xaccSplitCopyOnto(const Split *from_split, Split *to_split)
This is really a helper for xaccTransCopyOnto.
Definition Split.cpp:648
#define NREC
not reconciled or cleared
Definition Split.h:76
#define FREC
frozen into accounting period
Definition Split.h:75
gnc_numeric xaccSplitGetBalance(const Split *s)
Returns the running balance up to and including the indicated split.
Definition Split.cpp:1316
#define YREC
The Split has been reconciled.
Definition Split.h:74
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
Object instance holds common fields that most gnucash objects use.
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
QofBook reference.
Definition qofbook-p.hpp:47