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