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