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