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