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