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