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