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