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