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