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