Branch data Line data Source code
1 : : /********************************************************************\
2 : : * Split.c -- split 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 : : #ifdef HAVE_SYS_TIME_H
40 : : # include <sys/time.h>
41 : : #endif
42 : : #include <time.h>
43 : : #ifdef HAVE_UNISTD_H
44 : : # include <unistd.h>
45 : : #endif
46 : :
47 : : #include "qof.h"
48 : : #include "qofbook.h"
49 : : #include "Split.h"
50 : : #include "AccountP.hpp"
51 : : #include "Account.hpp"
52 : : #include "Scrub.h"
53 : : #include "TransactionP.hpp"
54 : : #include "TransLog.h"
55 : : #include "cap-gains.h"
56 : : #include "gnc-commodity.h"
57 : : #include "gnc-engine.h"
58 : : #include "gnc-lot.h"
59 : : #include "gnc-event.h"
60 : : #include "qofinstance-p.h"
61 : :
62 : : const char *void_former_amt_str = "void-former-amount";
63 : : const char *void_former_val_str = "void-former-value";
64 : :
65 : : /* This static indicates the debugging module that this .o belongs to. */
66 : : static QofLogModule log_module = GNC_MOD_ENGINE;
67 : :
68 : : /* KVP key values used for SX info stored Split's slots. */
69 : : #define GNC_SX_ID "sched-xaction"
70 : : #define GNC_SX_ACCOUNT "account"
71 : : #define GNC_SX_CREDIT_FORMULA "credit-formula"
72 : : #define GNC_SX_DEBIT_FORMULA "debit-formula"
73 : : #define GNC_SX_CREDIT_NUMERIC "credit-numeric"
74 : : #define GNC_SX_DEBIT_NUMERIC "debit-numeric"
75 : : #define GNC_SX_SHARES "shares"
76 : :
77 : : enum
78 : : {
79 : : PROP_0,
80 : : PROP_TX, /* Table */
81 : : PROP_ACCOUNT, /* Table */
82 : : PROP_MEMO, /* Table */
83 : : PROP_ACTION, /* Table */
84 : : // PROP_RECONCILE_STATE, /* Table */
85 : : PROP_RECONCILE_DATE, /* Table */
86 : : PROP_VALUE, /* Table, in 2 fields */
87 : : PROP_SX_ACCOUNT, /* KVP */
88 : : PROP_SX_CREDIT_FORMULA, /* KVP */
89 : : PROP_SX_CREDIT_NUMERIC, /* KVP */
90 : : PROP_SX_DEBIT_FORMULA, /* KVP */
91 : : PROP_SX_DEBIT_NUMERIC, /* KVP */
92 : : PROP_SX_SHARES, /* KVP */
93 : : PROP_LOT, /* KVP */
94 : : PROP_ONLINE_ACCOUNT, /* KVP */
95 : : PROP_GAINS_SPLIT, /* KVP */
96 : : PROP_GAINS_SOURCE, /* KVP */
97 : : PROP_RUNTIME_0,
98 : : PROP_AMOUNT, /* Runtime */
99 : :
100 : : };
101 : :
102 : : static const char * split_type_normal = "normal";
103 : : static const char * split_type_stock_split = "stock-split";
104 : : static const char * split_online_id = "online_id";
105 : :
106 : : /* GObject Initialization */
107 : 133825 : G_DEFINE_TYPE(Split, gnc_split, QOF_TYPE_INSTANCE)
108 : :
109 : : static void
110 : 10134 : gnc_split_init(Split* split)
111 : : {
112 : : /* fill in some sane defaults */
113 : 10134 : split->acc = nullptr;
114 : 10134 : split->orig_acc = nullptr;
115 : 10134 : split->parent = nullptr;
116 : 10134 : split->lot = nullptr;
117 : :
118 : 10134 : split->action = CACHE_INSERT("");
119 : 10134 : split->memo = CACHE_INSERT("");
120 : 10134 : split->reconciled = NREC;
121 : 10134 : split->amount = gnc_numeric_zero();
122 : 10134 : split->value = gnc_numeric_zero();
123 : :
124 : 10134 : split->date_reconciled = 0;
125 : :
126 : 10134 : split->balance = gnc_numeric_zero();
127 : 10134 : split->cleared_balance = gnc_numeric_zero();
128 : 10134 : split->reconciled_balance = gnc_numeric_zero();
129 : 10134 : split->noclosing_balance = gnc_numeric_zero();
130 : :
131 : 10134 : split->adjusted_amount = gnc_numeric_zero();
132 : 10134 : split->split_type = nullptr;
133 : :
134 : 10134 : split->gains = GAINS_STATUS_UNKNOWN;
135 : 10134 : split->gains_split = nullptr;
136 : 10134 : }
137 : :
138 : : static void
139 : 15568 : gnc_split_dispose(GObject *splitp)
140 : : {
141 : 15568 : G_OBJECT_CLASS(gnc_split_parent_class)->dispose(splitp);
142 : 15568 : }
143 : :
144 : : static void
145 : 7830 : gnc_split_finalize(GObject* splitp)
146 : : {
147 : 7830 : G_OBJECT_CLASS(gnc_split_parent_class)->finalize(splitp);
148 : 7830 : }
149 : : /* Note that g_value_set_object() refs the object, as does
150 : : * g_object_get(). But g_object_get() only unrefs once when it disgorges
151 : : * the object, leaving an unbalanced ref, which leaks. So instead of
152 : : * using g_value_set_object(), use g_value_take_object() which doesn't
153 : : * ref the object when used in get_property().
154 : : */
155 : : static void
156 : 1060 : gnc_split_get_property(GObject *object,
157 : : guint prop_id,
158 : : GValue *value,
159 : : GParamSpec *pspec)
160 : : {
161 : : Split *split;
162 : : Time64 t;
163 : :
164 : 1060 : g_return_if_fail(GNC_IS_SPLIT(object));
165 : :
166 : 1060 : split = GNC_SPLIT(object);
167 : 1060 : switch (prop_id)
168 : : {
169 : 23 : case PROP_ACTION:
170 : 23 : g_value_set_string(value, split->action);
171 : 23 : break;
172 : 24 : case PROP_MEMO:
173 : 24 : g_value_set_string(value, split->memo);
174 : 24 : break;
175 : 23 : case PROP_VALUE:
176 : 23 : g_value_set_boxed(value, &split->value);
177 : 23 : break;
178 : 23 : case PROP_AMOUNT:
179 : 23 : g_value_set_boxed(value, &split->amount);
180 : 23 : break;
181 : 23 : case PROP_RECONCILE_DATE:
182 : 23 : t.t = split->date_reconciled;
183 : 23 : g_value_set_boxed(value, &t);
184 : 23 : break;
185 : 23 : case PROP_TX:
186 : 23 : g_value_take_object(value, split->parent);
187 : 23 : break;
188 : 23 : case PROP_ACCOUNT:
189 : 23 : g_value_take_object(value, split->acc);
190 : 23 : break;
191 : 1 : case PROP_LOT:
192 : 1 : g_value_take_object(value, split->lot);
193 : 1 : break;
194 : 42 : case PROP_SX_CREDIT_FORMULA:
195 : 42 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
196 : 42 : break;
197 : 18 : case PROP_SX_CREDIT_NUMERIC:
198 : 18 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
199 : 18 : break;
200 : 41 : case PROP_SX_DEBIT_FORMULA:
201 : 41 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
202 : 41 : break;
203 : 18 : case PROP_SX_DEBIT_NUMERIC:
204 : 18 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
205 : 18 : break;
206 : 43 : case PROP_SX_ACCOUNT:
207 : 43 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
208 : 43 : break;
209 : 7 : case PROP_SX_SHARES:
210 : 7 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
211 : 7 : break;
212 : 2 : case PROP_ONLINE_ACCOUNT:
213 : 2 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "online_id");
214 : 2 : break;
215 : 307 : case PROP_GAINS_SPLIT:
216 : 307 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
217 : 307 : break;
218 : 419 : case PROP_GAINS_SOURCE:
219 : 419 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
220 : 419 : break;
221 : 0 : default:
222 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
223 : 0 : break;
224 : : }
225 : : }
226 : :
227 : : static void
228 : 1110 : gnc_split_set_property(GObject *object,
229 : : guint prop_id,
230 : : const GValue *value,
231 : : GParamSpec *pspec)
232 : : {
233 : : Split *split;
234 : : gnc_numeric* number;
235 : : Time64 *t;
236 : 1110 : g_return_if_fail(GNC_IS_SPLIT(object));
237 : :
238 : 1110 : split = GNC_SPLIT(object);
239 : 1110 : if (prop_id < PROP_RUNTIME_0 && split->parent != nullptr)
240 : 760 : g_assert (qof_instance_get_editlevel(split->parent));
241 : :
242 : 1110 : switch (prop_id)
243 : : {
244 : 24 : case PROP_ACTION:
245 : 24 : xaccSplitSetAction(split, g_value_get_string(value));
246 : 24 : break;
247 : 230 : case PROP_MEMO:
248 : 230 : xaccSplitSetMemo(split, g_value_get_string(value));
249 : 230 : break;
250 : 230 : case PROP_VALUE:
251 : 230 : number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
252 : 230 : xaccSplitSetValue(split, *number);
253 : 230 : break;
254 : 230 : case PROP_AMOUNT:
255 : 230 : number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
256 : 230 : xaccSplitSetAmount(split, *number);
257 : 230 : break;
258 : 24 : case PROP_RECONCILE_DATE:
259 : 24 : t = static_cast<Time64*>(g_value_get_boxed(value));
260 : 24 : xaccSplitSetDateReconciledSecs(split, t->t);
261 : 24 : break;
262 : 24 : case PROP_TX:
263 : 24 : xaccSplitSetParent(split, GNC_TRANSACTION(g_value_get_object(value)));
264 : 24 : break;
265 : 230 : case PROP_ACCOUNT:
266 : 230 : xaccSplitSetAccount(split, GNC_ACCOUNT(g_value_get_object(value)));
267 : 230 : break;
268 : 1 : case PROP_LOT:
269 : 1 : xaccSplitSetLot(split, GNC_LOT(g_value_get_object(value)));
270 : 1 : break;
271 : 20 : case PROP_SX_CREDIT_FORMULA:
272 : 20 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
273 : 20 : break;
274 : 11 : case PROP_SX_CREDIT_NUMERIC:
275 : 11 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
276 : 11 : break;
277 : 19 : case PROP_SX_DEBIT_FORMULA:
278 : 19 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
279 : 19 : break;
280 : 11 : case PROP_SX_DEBIT_NUMERIC:
281 : 11 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
282 : 11 : break;
283 : 19 : case PROP_SX_ACCOUNT:
284 : 19 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
285 : 19 : break;
286 : 9 : case PROP_SX_SHARES:
287 : 9 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
288 : 9 : break;
289 : 14 : case PROP_ONLINE_ACCOUNT:
290 : 14 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "online_id");
291 : 14 : break;
292 : 7 : case PROP_GAINS_SPLIT:
293 : 7 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
294 : 7 : break;
295 : 7 : case PROP_GAINS_SOURCE:
296 : 7 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
297 : 7 : break;
298 : 0 : default:
299 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
300 : 0 : break;
301 : : }
302 : : }
303 : :
304 : : static void
305 : 43 : gnc_split_class_init(SplitClass* klass)
306 : : {
307 : 43 : GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
308 : :
309 : 43 : gobject_class->dispose = gnc_split_dispose;
310 : 43 : gobject_class->finalize = gnc_split_finalize;
311 : 43 : gobject_class->set_property = gnc_split_set_property;
312 : 43 : gobject_class->get_property = gnc_split_get_property;
313 : :
314 : : g_object_class_install_property
315 : 43 : (gobject_class,
316 : : PROP_ACTION,
317 : : g_param_spec_string("action",
318 : : "Action",
319 : : "The action is an arbitrary string assigned "
320 : : "by the user. It is intended to be a short "
321 : : "string that contains extra information about "
322 : : "this split.",
323 : : nullptr,
324 : : G_PARAM_READWRITE));
325 : :
326 : : g_object_class_install_property
327 : 43 : (gobject_class,
328 : : PROP_MEMO,
329 : : g_param_spec_string("memo",
330 : : "Memo",
331 : : "The action is an arbitrary string assigned "
332 : : "by the user. It is intended to be a short "
333 : : "string that describes the purpose of "
334 : : "this split.",
335 : : nullptr,
336 : : G_PARAM_READWRITE));
337 : :
338 : : g_object_class_install_property
339 : 43 : (gobject_class,
340 : : PROP_VALUE,
341 : : g_param_spec_boxed("value",
342 : : "Split Value",
343 : : "The value for this split in the common currency. "
344 : : "The value and the amount provide enough information to "
345 : : "calculate a conversion rate.",
346 : : GNC_TYPE_NUMERIC,
347 : : G_PARAM_READWRITE));
348 : :
349 : : g_object_class_install_property
350 : 43 : (gobject_class,
351 : : PROP_AMOUNT,
352 : : g_param_spec_boxed("amount",
353 : : "Split Amount",
354 : : "The value for this split in the currency of its account. "
355 : : "The value and the amount provide enough information to "
356 : : "calculate a conversion rate.",
357 : : GNC_TYPE_NUMERIC,
358 : : G_PARAM_READWRITE));
359 : :
360 : : g_object_class_install_property
361 : 43 : (gobject_class,
362 : : PROP_RECONCILE_DATE,
363 : : g_param_spec_boxed("reconcile-date",
364 : : "Reconcile Date",
365 : : "The date this split was reconciled.",
366 : : GNC_TYPE_TIME64,
367 : : G_PARAM_READWRITE));
368 : :
369 : : g_object_class_install_property
370 : 43 : (gobject_class,
371 : : PROP_TX,
372 : : g_param_spec_object ("transaction",
373 : : "Transaction",
374 : : "The transaction that this split belongs to.",
375 : : GNC_TYPE_TRANSACTION,
376 : : G_PARAM_READWRITE));
377 : :
378 : : g_object_class_install_property
379 : 43 : (gobject_class,
380 : : PROP_ACCOUNT,
381 : : g_param_spec_object ("account",
382 : : "Account",
383 : : "The account that this split belongs to.",
384 : : GNC_TYPE_ACCOUNT,
385 : : G_PARAM_READWRITE));
386 : :
387 : : g_object_class_install_property
388 : 43 : (gobject_class,
389 : : PROP_LOT,
390 : : g_param_spec_object ("lot",
391 : : "Lot",
392 : : "The lot that this split belongs to.",
393 : : GNC_TYPE_LOT,
394 : : G_PARAM_READWRITE));
395 : :
396 : : g_object_class_install_property
397 : 43 : (gobject_class,
398 : : PROP_SX_DEBIT_FORMULA,
399 : : g_param_spec_string("sx-debit-formula",
400 : : "Schedule Transaction Debit Formula",
401 : : "The formula used to calculate the actual debit "
402 : : "amount when a real split is generated from this "
403 : : "SX split.",
404 : : nullptr,
405 : : G_PARAM_READWRITE));
406 : :
407 : : g_object_class_install_property
408 : 43 : (gobject_class,
409 : : PROP_SX_DEBIT_NUMERIC,
410 : : g_param_spec_boxed("sx-debit-numeric",
411 : : "Scheduled Transaction Debit Numeric",
412 : : "Numeric value to plug into the Debit Formula when a "
413 : : "real split is generated from this SX split.",
414 : : GNC_TYPE_NUMERIC,
415 : : G_PARAM_READWRITE));
416 : :
417 : : g_object_class_install_property
418 : 43 : (gobject_class,
419 : : PROP_SX_CREDIT_FORMULA,
420 : : g_param_spec_string("sx-credit-formula",
421 : : "Schedule Transaction Credit Formula",
422 : : "The formula used to calculate the actual credit "
423 : : "amount when a real split is generated from this "
424 : : "SX split.",
425 : : nullptr,
426 : : G_PARAM_READWRITE));
427 : :
428 : : g_object_class_install_property
429 : 43 : (gobject_class,
430 : : PROP_SX_CREDIT_NUMERIC,
431 : : g_param_spec_boxed("sx-credit-numeric",
432 : : "Scheduled Transaction Credit Numeric",
433 : : "Numeric value to plug into the Credit Formula when a "
434 : : "real split is generated from this SX split.",
435 : : GNC_TYPE_NUMERIC,
436 : : G_PARAM_READWRITE));
437 : : /* FIXME: PROP_SX_SHARES should be stored as a gnc_numeric, but the function
438 : : * which uses it, gnc_template_register_save_shares_cell, stores a
439 : : * phony string. This is maintained until backwards compatibility can
440 : : * be established.
441 : : */
442 : : g_object_class_install_property
443 : 43 : (gobject_class,
444 : : PROP_SX_SHARES,
445 : : g_param_spec_string("sx-shares",
446 : : "Scheduled Transaction Shares",
447 : : "Numeric value of shares to insert in a new split when "
448 : : "it's generated from this SX split.",
449 : : nullptr,
450 : : G_PARAM_READWRITE));
451 : :
452 : : g_object_class_install_property
453 : 43 : (gobject_class,
454 : : PROP_SX_ACCOUNT,
455 : : g_param_spec_boxed("sx-account",
456 : : "Scheduled Transaction Account",
457 : : "The target account for a scheduled transaction split.",
458 : : GNC_TYPE_GUID,
459 : : G_PARAM_READWRITE));
460 : :
461 : : g_object_class_install_property
462 : 43 : (gobject_class,
463 : : PROP_ONLINE_ACCOUNT,
464 : : g_param_spec_string ("online-id",
465 : : "Online Account ID",
466 : : "The online account which corresponds to this "
467 : : "account for OFX/HCBI import",
468 : : nullptr,
469 : : G_PARAM_READWRITE));
470 : :
471 : : g_object_class_install_property
472 : 43 : (gobject_class,
473 : : PROP_GAINS_SPLIT,
474 : : g_param_spec_boxed ("gains-split",
475 : : "Gains Split",
476 : : "The capital gains split associated with this "
477 : : "split when this split represents the proceeds "
478 : : "from the sale of a commodity inside a Lot.",
479 : : GNC_TYPE_GUID,
480 : : G_PARAM_READWRITE));
481 : :
482 : : g_object_class_install_property
483 : 43 : (gobject_class,
484 : : PROP_GAINS_SOURCE,
485 : : g_param_spec_boxed ("gains-source",
486 : : "Gains Source",
487 : : "The source split for which this split this is "
488 : : "the gains split. ",
489 : : GNC_TYPE_GUID,
490 : : G_PARAM_READWRITE));
491 : 43 : }
492 : :
493 : : /********************************************************************\
494 : : * xaccInitSplit
495 : : * Initialize a Split structure
496 : : \********************************************************************/
497 : :
498 : : static void
499 : 7303 : xaccInitSplit(Split * split, QofBook *book)
500 : : {
501 : 7303 : qof_instance_init_data(&split->inst, GNC_ID_SPLIT, book);
502 : 7303 : }
503 : :
504 : : void
505 : 0 : xaccSplitReinit(Split * split)
506 : : {
507 : : /* fill in some sane defaults */
508 : 0 : split->acc = nullptr;
509 : 0 : split->orig_acc = nullptr;
510 : 0 : split->parent = nullptr;
511 : 0 : split->lot = nullptr;
512 : :
513 : 0 : CACHE_REPLACE(split->action, "");
514 : 0 : CACHE_REPLACE(split->memo, "");
515 : 0 : split->reconciled = NREC;
516 : 0 : split->amount = gnc_numeric_zero();
517 : 0 : split->value = gnc_numeric_zero();
518 : :
519 : 0 : split->date_reconciled = 0;
520 : :
521 : 0 : split->balance = gnc_numeric_zero();
522 : 0 : split->cleared_balance = gnc_numeric_zero();
523 : 0 : split->reconciled_balance = gnc_numeric_zero();
524 : 0 : split->noclosing_balance = gnc_numeric_zero();
525 : :
526 : 0 : split->adjusted_amount = gnc_numeric_zero();
527 : 0 : split->split_type = nullptr;
528 : :
529 : 0 : qof_instance_set_idata(split, 0);
530 : :
531 : 0 : split->gains = GAINS_STATUS_UNKNOWN;
532 : 0 : split->gains_split = nullptr;
533 : 0 : }
534 : :
535 : : /********************************************************************\
536 : : \********************************************************************/
537 : :
538 : : Split *
539 : 7303 : xaccMallocSplit(QofBook *book)
540 : : {
541 : : Split *split;
542 : 7303 : g_return_val_if_fail (book, nullptr);
543 : :
544 : 7303 : split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
545 : 7303 : xaccInitSplit (split, book);
546 : :
547 : 7303 : return split;
548 : : }
549 : :
550 : : /********************************************************************\
551 : : \********************************************************************/
552 : : /* This routine is not exposed externally, since it does weird things,
553 : : * like not really setting up the parent account correctly, and ditto
554 : : * the parent transaction. This routine is prone to programmer error
555 : : * if not used correctly. It is used only by the edit-rollback code.
556 : : * Don't get duped!
557 : : */
558 : :
559 : : Split *
560 : 2805 : xaccDupeSplit (const Split *s)
561 : : {
562 : 2805 : Split *split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
563 : :
564 : : /* Trash the entity table. We don't want to mistake the cloned
565 : : * splits as something official. If we ever use this split, we'll
566 : : * have to fix this up.
567 : : */
568 : 2805 : split->inst.e_type = nullptr;
569 : 2805 : qof_instance_copy_guid(split, s);
570 : 2805 : qof_instance_copy_book(split, s);
571 : :
572 : 2805 : split->parent = s->parent;
573 : 2805 : split->acc = s->acc;
574 : 2805 : split->orig_acc = s->orig_acc;
575 : 2805 : split->lot = s->lot;
576 : :
577 : 2805 : CACHE_REPLACE(split->memo, s->memo);
578 : 2805 : CACHE_REPLACE(split->action, s->action);
579 : :
580 : 2805 : qof_instance_copy_kvp (QOF_INSTANCE (split), QOF_INSTANCE (s));
581 : :
582 : 2805 : split->reconciled = s->reconciled;
583 : 2805 : split->date_reconciled = s->date_reconciled;
584 : :
585 : 2805 : split->value = s->value;
586 : 2805 : split->amount = s->amount;
587 : :
588 : : /* no need to futz with the balances; these get wiped each time ...
589 : : * split->balance = s->balance;
590 : : * split->cleared_balance = s->cleared_balance;
591 : : * split->reconciled_balance = s->reconciled_balance;
592 : : */
593 : :
594 : 2805 : return split;
595 : : }
596 : :
597 : : Split *
598 : 23 : xaccSplitCloneNoKvp (const Split *s)
599 : : {
600 : 23 : Split *split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
601 : :
602 : 23 : split->parent = nullptr;
603 : 23 : split->memo = CACHE_INSERT(s->memo);
604 : 23 : split->action = CACHE_INSERT(s->action);
605 : 23 : split->reconciled = s->reconciled;
606 : 23 : split->date_reconciled = s->date_reconciled;
607 : 23 : split->value = s->value;
608 : 23 : split->amount = s->amount;
609 : 23 : split->balance = s->balance;
610 : 23 : split->cleared_balance = s->cleared_balance;
611 : 23 : split->reconciled_balance = s->reconciled_balance;
612 : 23 : split->noclosing_balance = s->noclosing_balance;
613 : 23 : split->adjusted_amount = s->adjusted_amount;
614 : 23 : split->split_type = s->split_type;
615 : :
616 : 23 : split->gains = GAINS_STATUS_UNKNOWN;
617 : 23 : split->gains_split = nullptr;
618 : :
619 : 23 : qof_instance_init_data(&split->inst, GNC_ID_SPLIT,
620 : : qof_instance_get_book(s));
621 : 23 : xaccAccountInsertSplit(s->acc, split);
622 : 23 : if (s->lot)
623 : : {
624 : : /* CHECKME: Is this right? */
625 : 2 : gnc_lot_add_split(s->lot, split);
626 : : }
627 : 23 : return split;
628 : : }
629 : :
630 : : void
631 : 13 : xaccSplitCopyKvp (const Split *from, Split *to)
632 : : {
633 : 13 : qof_instance_copy_kvp (QOF_INSTANCE (to), QOF_INSTANCE (from));
634 : : /* But not the online-id */
635 : 13 : qof_instance_set (QOF_INSTANCE (to), "online-id", nullptr, nullptr);
636 : 13 : }
637 : :
638 : : /*################## Added for Reg2 #################*/
639 : :
640 : : /* This is really a helper for xaccTransCopyOnto. It doesn't reparent
641 : : the 'to' split to from's transaction, because xaccTransCopyOnto is
642 : : responsible for parenting the split to the correct transaction.
643 : : Also, from's parent transaction may not even be a valid
644 : : transaction, so this function may not modify anything about 'from'
645 : : or from's transaction.
646 : : */
647 : : void
648 : 4 : xaccSplitCopyOnto(const Split *from_split, Split *to_split)
649 : : {
650 : 4 : if (!from_split || !to_split) return;
651 : 4 : xaccTransBeginEdit (to_split->parent);
652 : :
653 : 4 : xaccSplitSetMemo(to_split, xaccSplitGetMemo(from_split));
654 : 4 : xaccSplitSetAction(to_split, xaccSplitGetAction(from_split));
655 : 4 : xaccSplitSetAmount(to_split, xaccSplitGetAmount(from_split));
656 : 4 : xaccSplitSetValue(to_split, xaccSplitGetValue(from_split));
657 : : /* Setting the account is okay here because, even though the from
658 : : split might not really belong to the account it claims to,
659 : : setting the account won't cause any event involving from. */
660 : 4 : xaccSplitSetAccount(to_split, xaccSplitGetAccount(from_split));
661 : : /* N.B. Don't set parent. */
662 : :
663 : 4 : qof_instance_set_dirty(QOF_INSTANCE(to_split));
664 : 4 : xaccTransCommitEdit(to_split->parent);
665 : : }
666 : :
667 : : /*################## Added for Reg2 #################*/
668 : :
669 : :
670 : : #ifdef DUMP_FUNCTIONS
671 : : void
672 : : xaccSplitDump (const Split *split, const char *tag)
673 : : {
674 : : char datebuff[MAX_DATE_LENGTH + 1];
675 : : memset (datebuff, 0, sizeof(datebuff));
676 : : qof_print_date_buff (datebuff, MAX_DATE_LENGTH, split->date_reconciled);
677 : : printf(" %s Split %p", tag, split);
678 : : printf(" Book: %p\n", qof_instance_get_book(split));
679 : : printf(" Account: %p (%s)\n", split->acc,
680 : : split->acc ? xaccAccountGetName(split->acc) : "");
681 : : printf(" Commod: %s\n",
682 : : split->acc ?
683 : : gnc_commodity_get_printname(xaccAccountGetCommodity(split->acc))
684 : : : "");
685 : : printf(" Lot: %p\n", split->lot);
686 : : printf(" Parent: %p\n", split->parent);
687 : : printf(" Gains: %p\n", split->gains_split);
688 : : printf(" Memo: %s\n", split->memo ? split->memo : "(null)");
689 : : printf(" Action: %s\n", split->action ? split->action : "(null)");
690 : : printf(" KVP Data: %s\n", qof_instance_kvp_as_string (QOF_INSTANCE (split)));
691 : : printf(" Recncld: %c (date %s)\n", split->reconciled, datebuff);
692 : :
693 : : printf(" Value: %s\n", gnc_numeric_to_string(split->value));
694 : : printf(" Amount: %s\n", gnc_numeric_to_string(split->amount));
695 : : printf(" Balance: %s\n", gnc_numeric_to_string(split->balance));
696 : : printf(" CBalance: %s\n", gnc_numeric_to_string(split->cleared_balance));
697 : : printf(" RBalance: %s\n",
698 : : gnc_numeric_to_string(split->reconciled_balance));
699 : : printf(" NoClose: %s\n", gnc_numeric_to_string(split->noclosing_balance));
700 : : printf(" AdjAmt: %s\n", gnc_numeric_to_string(split->adjusted_amount));
701 : : printf(" Type: %s\n", split->split_type ? split->split_type : "(null)");
702 : : printf(" idata: %x\n", qof_instance_get_idata(split));
703 : : }
704 : : #endif
705 : :
706 : : /********************************************************************\
707 : : \********************************************************************/
708 : : static void
709 : 4936 : do_destroy (QofInstance *inst)
710 : : {
711 : 4936 : xaccFreeSplit (GNC_SPLIT (inst));
712 : 4936 : }
713 : :
714 : : void
715 : 7737 : xaccFreeSplit (Split *split)
716 : : {
717 : 7737 : if (!split) return;
718 : :
719 : : /* Debug double-free's */
720 : 7737 : if (((char *) 1) == split->memo)
721 : : {
722 : 0 : PERR ("double-free %p", split);
723 : 0 : return;
724 : : }
725 : 7737 : CACHE_REMOVE(split->memo);
726 : 7737 : CACHE_REMOVE(split->action);
727 : :
728 : 7737 : if (split->inst.e_type) /* Don't do this for dupe splits. */
729 : : {
730 : : /* gnc_lot_remove_split needs the account, so do it first. */
731 : 4942 : if (GNC_IS_LOT (split->lot) && !qof_instance_get_destroying (QOF_INSTANCE (split->lot)))
732 : 0 : gnc_lot_remove_split (split->lot, split);
733 : 4942 : if (GNC_IS_ACCOUNT (split->acc)
734 : 4942 : && !qof_instance_get_destroying (QOF_INSTANCE (split->acc)))
735 : : {
736 : 4938 : gnc_account_remove_split (split->acc, split);
737 : : /* gnc_float_split_to_split generates a qof_event_gen via
738 : : * xaccAccountCommitEdit even though it doesn't touch the
739 : : * account. That causes QofQueryViews to notice the split
740 : : * even though it isn't added to the account. We need a
741 : : * countervailing event so that they'll notice it's not
742 : : * there any more.
743 : : */
744 : 4938 : qof_event_gen(&split->acc->inst, QOF_EVENT_MODIFY, nullptr);
745 : : }
746 : : /* We should do the same for split->parent but we might be getting
747 : : * called from xaccFreeTransaction and that would cause trouble.
748 : : */
749 : : }
750 : :
751 : : /* Just in case someone looks up freed memory ... */
752 : 7737 : split->memo = (char *) 1;
753 : 7737 : split->action = nullptr;
754 : 7737 : split->reconciled = NREC;
755 : 7737 : split->amount = gnc_numeric_zero();
756 : 7737 : split->value = gnc_numeric_zero();
757 : 7737 : split->parent = nullptr;
758 : 7737 : split->lot = nullptr;
759 : 7737 : split->acc = nullptr;
760 : 7737 : split->orig_acc = nullptr;
761 : :
762 : 7737 : split->balance = gnc_numeric_zero();
763 : 7737 : split->cleared_balance = gnc_numeric_zero();
764 : 7737 : split->reconciled_balance = gnc_numeric_zero();
765 : 7737 : split->noclosing_balance = gnc_numeric_zero();
766 : 7737 : split->adjusted_amount = gnc_numeric_zero();
767 : 7737 : split->split_type = nullptr;
768 : :
769 : 7737 : split->date_reconciled = 0;
770 : 7737 : G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split));
771 : :
772 : 7737 : if (split->gains_split)
773 : : {
774 : 6 : Split *other = xaccSplitGetOtherSplit(split->gains_split);
775 : 6 : split->gains_split->gains_split = nullptr;
776 : 6 : if (other)
777 : 6 : other->gains_split = nullptr;
778 : : }
779 : :
780 : 7737 : g_object_unref(split);
781 : : }
782 : :
783 : 31545 : void mark_split (Split *s)
784 : : {
785 : 31545 : if (s->acc)
786 : 22073 : g_object_set(s->acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr);
787 : :
788 : : /* set dirty flag on lot too. */
789 : 31545 : if (s->lot)
790 : 229 : gnc_lot_set_closed_unknown(s->lot);
791 : 31545 : }
792 : :
793 : : /*
794 : : * Helper routine for xaccSplitEqual.
795 : : */
796 : : static gboolean
797 : 795 : xaccSplitEqualCheckBal (const char *tag, gnc_numeric a, gnc_numeric b)
798 : : {
799 : : char *str_a, *str_b;
800 : :
801 : 795 : if (gnc_numeric_equal (a, b))
802 : 789 : return TRUE;
803 : :
804 : 6 : str_a = gnc_numeric_to_string (a);
805 : 6 : str_b = gnc_numeric_to_string (b);
806 : :
807 : 6 : PINFO ("%sbalances differ: %s vs %s", tag, str_a, str_b);
808 : :
809 : 6 : g_free (str_a);
810 : 6 : g_free (str_b);
811 : :
812 : 6 : return FALSE;
813 : : }
814 : :
815 : : /********************************************************************
816 : : * xaccSplitEqual
817 : : ********************************************************************/
818 : : gboolean
819 : 189 : xaccSplitEqual(const Split *sa, const Split *sb,
820 : : gboolean check_guids,
821 : : gboolean check_balances,
822 : : gboolean check_txn_splits)
823 : : {
824 : : gboolean same_book;
825 : :
826 : 189 : if (!sa && !sb) return TRUE; /* Arguable. FALSE is better, methinks */
827 : :
828 : 189 : if (!sa || !sb)
829 : : {
830 : 2 : PINFO ("one is nullptr");
831 : 2 : return FALSE;
832 : : }
833 : :
834 : 187 : if (sa == sb) return TRUE;
835 : :
836 : 183 : same_book = qof_instance_get_book(QOF_INSTANCE(sa)) == qof_instance_get_book(QOF_INSTANCE(sb));
837 : :
838 : 183 : if (check_guids)
839 : : {
840 : 156 : if (qof_instance_guid_compare(sa, sb) != 0)
841 : : {
842 : 3 : PINFO ("GUIDs differ");
843 : 3 : return FALSE;
844 : : }
845 : : }
846 : :
847 : : /* If the same book, since these strings are cached we can just use pointer equality */
848 : 180 : if ((same_book && sa->memo != sb->memo) || (!same_book && g_strcmp0(sa->memo, sb->memo) != 0))
849 : : {
850 : 1 : PINFO ("memos differ: (%p)%s vs (%p)%s",
851 : : sa->memo, sa->memo, sb->memo, sb->memo);
852 : 1 : return FALSE;
853 : : }
854 : :
855 : 179 : if ((same_book && sa->action != sb->action) || (!same_book && g_strcmp0(sa->action, sb->action) != 0))
856 : : {
857 : 2 : PINFO ("actions differ: %s vs %s", sa->action, sb->action);
858 : 2 : return FALSE;
859 : : }
860 : :
861 : 177 : if (qof_instance_compare_kvp (QOF_INSTANCE (sa), QOF_INSTANCE (sb)) != 0)
862 : : {
863 : : char *frame_a;
864 : : char *frame_b;
865 : :
866 : 0 : frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (sa));
867 : 0 : frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (sb));
868 : :
869 : 0 : PINFO ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
870 : :
871 : 0 : g_free (frame_a);
872 : 0 : g_free (frame_b);
873 : :
874 : 0 : return FALSE;
875 : : }
876 : :
877 : 177 : if (sa->reconciled != sb->reconciled)
878 : : {
879 : 0 : PINFO ("reconcile flags differ: %c vs %c", sa->reconciled, sb->reconciled);
880 : 0 : return FALSE;
881 : : }
882 : :
883 : 177 : if (sa->date_reconciled != sb->date_reconciled)
884 : : {
885 : 0 : PINFO ("reconciled date differs");
886 : 0 : return FALSE;
887 : : }
888 : :
889 : 177 : if (!gnc_numeric_eq(xaccSplitGetAmount (sa), xaccSplitGetAmount (sb)))
890 : : {
891 : : char *str_a;
892 : : char *str_b;
893 : :
894 : 0 : str_a = gnc_numeric_to_string (xaccSplitGetAmount (sa));
895 : 0 : str_b = gnc_numeric_to_string (xaccSplitGetAmount (sb));
896 : :
897 : 0 : PINFO ("amounts differ: %s vs %s", str_a, str_b);
898 : :
899 : 0 : g_free (str_a);
900 : 0 : g_free (str_b);
901 : :
902 : 0 : return FALSE;
903 : : }
904 : :
905 : 177 : if (!gnc_numeric_eq(xaccSplitGetValue (sa), xaccSplitGetValue (sb)))
906 : : {
907 : : char *str_a;
908 : : char *str_b;
909 : :
910 : 0 : str_a = gnc_numeric_to_string (xaccSplitGetValue (sa));
911 : 0 : str_b = gnc_numeric_to_string (xaccSplitGetValue (sb));
912 : :
913 : 0 : PINFO ("values differ: %s vs %s", str_a, str_b);
914 : :
915 : 0 : g_free (str_a);
916 : 0 : g_free (str_b);
917 : :
918 : 0 : return FALSE;
919 : : }
920 : :
921 : 177 : if (check_balances)
922 : : {
923 : 162 : if (!xaccSplitEqualCheckBal ("", sa->balance, sb->balance))
924 : 3 : return FALSE;
925 : 159 : if (!xaccSplitEqualCheckBal ("cleared ", sa->cleared_balance,
926 : : sb->cleared_balance))
927 : 1 : return FALSE;
928 : 158 : if (!xaccSplitEqualCheckBal ("reconciled ", sa->reconciled_balance,
929 : : sb->reconciled_balance))
930 : 1 : return FALSE;
931 : 157 : if (!xaccSplitEqualCheckBal ("noclosing ", sa->noclosing_balance,
932 : : sb->noclosing_balance))
933 : 0 : return FALSE;
934 : 157 : if (!xaccSplitEqualCheckBal ("adjusted ", sa->adjusted_amount,
935 : : sb->adjusted_amount))
936 : 0 : return FALSE;
937 : : }
938 : :
939 : 172 : if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits,
940 : : check_balances, FALSE))
941 : : {
942 : 1 : PINFO ("transactions differ");
943 : 1 : return FALSE;
944 : : }
945 : :
946 : 171 : return TRUE;
947 : : }
948 : :
949 : :
950 : :
951 : : /********************************************************************
952 : : * Account funcs
953 : : ********************************************************************/
954 : :
955 : : Account *
956 : 124893 : xaccSplitGetAccount (const Split *s)
957 : : {
958 : 124893 : return s ? s->acc : nullptr;
959 : : }
960 : :
961 : : void
962 : 7274 : xaccSplitSetAccount (Split *s, Account *acc)
963 : : {
964 : : Transaction *trans;
965 : :
966 : 7274 : g_return_if_fail(s && acc);
967 : 7274 : g_return_if_fail(qof_instance_books_equal(acc, s));
968 : :
969 : 7274 : trans = s->parent;
970 : 7274 : if (trans)
971 : 5161 : xaccTransBeginEdit(trans);
972 : :
973 : 7274 : s->acc = acc;
974 : 7274 : qof_instance_set_dirty(QOF_INSTANCE(s));
975 : :
976 : 7274 : if (trans)
977 : 5161 : xaccTransCommitEdit(trans);
978 : : }
979 : :
980 : 0 : static void commit_err (QofInstance *inst, QofBackendError errcode)
981 : : {
982 : 0 : PERR("commit error: %d", errcode);
983 : 0 : gnc_engine_signal_commit_error( errcode );
984 : 0 : }
985 : :
986 : : /* An engine-private helper for completing xaccTransCommitEdit(). */
987 : : void
988 : 13127 : xaccSplitCommitEdit(Split *s)
989 : : {
990 : 13127 : Account *acc = nullptr;
991 : 13127 : Account *orig_acc = nullptr;
992 : :
993 : 13127 : g_return_if_fail(s);
994 : 13127 : if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
995 : 0 : return;
996 : :
997 : 13127 : orig_acc = s->orig_acc;
998 : :
999 : 13127 : if (GNC_IS_ACCOUNT(s->acc))
1000 : 13109 : acc = s->acc;
1001 : :
1002 : : /* Remove from lot (but only if it hasn't been moved to
1003 : : new lot already) */
1004 : 13127 : if (s->lot && (gnc_lot_get_account(s->lot) != acc || qof_instance_get_destroying(s)))
1005 : 11 : gnc_lot_remove_split (s->lot, s);
1006 : :
1007 : : /* Possibly remove the split from the original account... */
1008 : 13127 : if (orig_acc && (orig_acc != acc || qof_instance_get_destroying(s)))
1009 : : {
1010 : 5054 : if (!gnc_account_remove_split(orig_acc, s))
1011 : : {
1012 : 2 : PERR("Account lost track of moved or deleted split.");
1013 : : }
1014 : : }
1015 : :
1016 : : /* ... and insert it into the new account if needed */
1017 : 13127 : if (acc && (orig_acc != acc) && !qof_instance_get_destroying(s))
1018 : : {
1019 : 7092 : if (gnc_account_insert_split(acc, s))
1020 : : {
1021 : : /* If the split's lot belonged to some other account, we
1022 : : leave it so. */
1023 : 7091 : if (s->lot && (nullptr == gnc_lot_get_account(s->lot)))
1024 : 0 : xaccAccountInsertLot (acc, s->lot);
1025 : : }
1026 : : else
1027 : : {
1028 : 1 : PERR("Account grabbed split prematurely.");
1029 : : }
1030 : 7092 : xaccSplitSetAmount(s, xaccSplitGetAmount(s));
1031 : : }
1032 : :
1033 : 13127 : if (s->parent != s->orig_parent)
1034 : : {
1035 : : //FIXME: find better event
1036 : 6979 : if (s->orig_parent)
1037 : 3 : qof_event_gen(&s->orig_parent->inst, QOF_EVENT_MODIFY,
1038 : : nullptr);
1039 : : }
1040 : 13127 : if (s->lot)
1041 : : {
1042 : : /* A change of value/amnt affects gains display, etc. */
1043 : 296 : qof_event_gen (QOF_INSTANCE(s->lot), QOF_EVENT_MODIFY, nullptr);
1044 : : }
1045 : :
1046 : : /* Important: we save off the original parent transaction and account
1047 : : so that when we commit, we can generate signals for both the
1048 : : original and new transactions, for the _next_ begin/commit cycle. */
1049 : 13127 : s->orig_acc = s->acc;
1050 : 13127 : s->orig_parent = s->parent;
1051 : 13127 : if (!qof_commit_edit_part2(QOF_INSTANCE(s), commit_err, nullptr, do_destroy))
1052 : 0 : return;
1053 : :
1054 : 13127 : if (acc)
1055 : : {
1056 : 13109 : g_object_set(acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr);
1057 : 13109 : xaccAccountRecomputeBalance(acc);
1058 : : }
1059 : : }
1060 : :
1061 : : /* An engine-private helper for completing xaccTransRollbackEdit(). */
1062 : : void
1063 : 7 : xaccSplitRollbackEdit(Split *s)
1064 : : {
1065 : :
1066 : : /* Don't use setters because we want to allow nullptr. This is legit
1067 : : only because we don't emit events for changing accounts until
1068 : : the final commit. */
1069 : 7 : if (s->acc != s->orig_acc)
1070 : 3 : s->acc = s->orig_acc;
1071 : :
1072 : : /* Undestroy if needed */
1073 : 7 : if (qof_instance_get_destroying(s) && s->parent)
1074 : : {
1075 : : GncEventData ed;
1076 : 1 : qof_instance_set_destroying(s, FALSE);
1077 : 1 : ed.node = s;
1078 : 1 : ed.idx = -1; /* unused */
1079 : 1 : qof_event_gen(&s->parent->inst, GNC_EVENT_ITEM_ADDED, &ed);
1080 : : }
1081 : :
1082 : : /* But for the parent trans, we want the intermediate events, so
1083 : : we use the setter. */
1084 : 7 : xaccSplitSetParent(s, s->orig_parent);
1085 : 7 : }
1086 : :
1087 : : /********************************************************************\
1088 : : \********************************************************************/
1089 : :
1090 : : Split *
1091 : 60 : xaccSplitLookup (const GncGUID *guid, QofBook *book)
1092 : : {
1093 : : QofCollection *col;
1094 : 60 : if (!guid || !book) return nullptr;
1095 : 58 : col = qof_book_get_collection (book, GNC_ID_SPLIT);
1096 : 58 : return (Split *) qof_collection_lookup_entity (col, guid);
1097 : : }
1098 : :
1099 : : /********************************************************************\
1100 : : \********************************************************************/
1101 : : /* Routines for marking splits dirty, and for sending out change
1102 : : * events. Note that we can't just mark-n-generate-event in one
1103 : : * step, since sometimes we need to mark things up before its suitable
1104 : : * to send out a change event.
1105 : : */
1106 : :
1107 : : /* CHECKME: This function modifies the Split without dirtying or
1108 : : checking its parent. Is that correct? */
1109 : : void
1110 : 440 : xaccSplitDetermineGainStatus (Split *split)
1111 : : {
1112 : : Split *other;
1113 : :
1114 : 440 : if (GAINS_STATUS_UNKNOWN != split->gains) return;
1115 : :
1116 : : /* A gains source has a forward "gains-split" link to the split that
1117 : : * records its gains. */
1118 : 307 : other = xaccSplitGetCapGainsSplit (split);
1119 : 307 : if (other)
1120 : : {
1121 : 0 : split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
1122 : 0 : split->gains_split = other;
1123 : : /* Resolve the gains-recording split too, so it reuses this link
1124 : : * rather than being re-created if it is processed first. */
1125 : 0 : if (GAINS_STATUS_UNKNOWN == other->gains)
1126 : : {
1127 : 0 : other->gains = GAINS_STATUS_GAINS;
1128 : 0 : other->gains_split = split;
1129 : : }
1130 : 0 : return;
1131 : : }
1132 : :
1133 : : /* A gains-recording split has a backward "gains-source" link to the
1134 : : * split whose gains it records. */
1135 : 307 : other = xaccSplitGetGainsSourceSplit (split);
1136 : 307 : if (!other)
1137 : : {
1138 : : // CHECKME: We leave split->gains_split alone. Is that correct?
1139 : 305 : split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
1140 : : }
1141 : : else
1142 : : {
1143 : 2 : split->gains = GAINS_STATUS_GAINS;
1144 : 2 : split->gains_split = other;
1145 : : /* Resolve the source too, so it reuses this split rather than
1146 : : * creating a duplicate if the source is processed afterward. */
1147 : 2 : if (GAINS_STATUS_UNKNOWN == other->gains)
1148 : : {
1149 : 2 : other->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
1150 : 2 : other->gains_split = split;
1151 : : }
1152 : : }
1153 : : }
1154 : :
1155 : : /********************************************************************\
1156 : : \********************************************************************/
1157 : :
1158 : : static inline int
1159 : 15076 : get_currency_denom(const Split * s)
1160 : : {
1161 : 15076 : if (!(s && s->parent && s->parent->common_currency))
1162 : : {
1163 : 2857 : return GNC_DENOM_AUTO;
1164 : : }
1165 : : else
1166 : : {
1167 : 12219 : return gnc_commodity_get_fraction (s->parent->common_currency);
1168 : : }
1169 : : }
1170 : :
1171 : : static inline int
1172 : 12858 : get_commodity_denom(const Split * s)
1173 : : {
1174 : 12858 : if (!(s && s->acc))
1175 : : {
1176 : 2 : return GNC_DENOM_AUTO;
1177 : : }
1178 : : else
1179 : : {
1180 : 12856 : return xaccAccountGetCommoditySCU(s->acc);
1181 : : }
1182 : : }
1183 : :
1184 : : /********************************************************************\
1185 : : \********************************************************************/
1186 : :
1187 : : void
1188 : 2 : xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt)
1189 : : {
1190 : 2 : if (!s) return;
1191 : 2 : ENTER (" ");
1192 : 2 : xaccTransBeginEdit (s->parent);
1193 : :
1194 : 2 : s->amount = gnc_numeric_convert(amt, get_commodity_denom(s),
1195 : : GNC_HOW_RND_ROUND_HALF_UP);
1196 : 2 : s->value = gnc_numeric_mul(s->amount, price,
1197 : 2 : get_currency_denom(s), GNC_HOW_RND_ROUND_HALF_UP);
1198 : :
1199 : 2 : SET_GAINS_A_VDIRTY(s);
1200 : 2 : mark_split (s);
1201 : 2 : qof_instance_set_dirty(QOF_INSTANCE(s));
1202 : 2 : xaccTransCommitEdit(s->parent);
1203 : 2 : LEAVE ("");
1204 : : }
1205 : :
1206 : : static void
1207 : 0 : qofSplitSetSharePrice (Split *split, gnc_numeric price)
1208 : : {
1209 : 0 : g_return_if_fail(split);
1210 : 0 : split->value = gnc_numeric_mul(xaccSplitGetAmount(split),
1211 : 0 : price, get_currency_denom(split),
1212 : : GNC_HOW_RND_ROUND_HALF_UP);
1213 : : }
1214 : :
1215 : : void
1216 : 64 : xaccSplitSetSharePrice (Split *s, gnc_numeric price)
1217 : : {
1218 : 64 : if (!s) return;
1219 : :
1220 : 64 : if (gnc_numeric_zero_p (price))
1221 : 0 : return;
1222 : :
1223 : 64 : ENTER (" ");
1224 : 64 : xaccTransBeginEdit (s->parent);
1225 : :
1226 : 64 : s->value = gnc_numeric_mul(xaccSplitGetAmount(s),
1227 : 64 : price, get_currency_denom(s),
1228 : : GNC_HOW_RND_ROUND_HALF_UP);
1229 : :
1230 : 64 : SET_GAINS_VDIRTY(s);
1231 : 64 : mark_split (s);
1232 : 64 : qof_instance_set_dirty(QOF_INSTANCE(s));
1233 : 64 : xaccTransCommitEdit(s->parent);
1234 : 64 : LEAVE ("");
1235 : : }
1236 : :
1237 : : static void
1238 : 0 : qofSplitSetAmount (Split *split, gnc_numeric amt)
1239 : : {
1240 : 0 : g_return_if_fail(split);
1241 : 0 : if (split->acc)
1242 : : {
1243 : 0 : split->amount = gnc_numeric_convert(amt,
1244 : 0 : get_commodity_denom(split), GNC_HOW_RND_ROUND_HALF_UP);
1245 : : }
1246 : : else
1247 : : {
1248 : 0 : split->amount = amt;
1249 : : }
1250 : : }
1251 : :
1252 : : /* The amount of the split in the _account's_ commodity. */
1253 : : void
1254 : 14529 : xaccSplitSetAmount (Split *s, gnc_numeric amt)
1255 : : {
1256 : 14529 : if (!s) return;
1257 : 14529 : g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK);
1258 : 14529 : ENTER ("(split=%p) old amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT
1259 : : " new amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s,
1260 : : s->amount.num, s->amount.denom, amt.num, amt.denom);
1261 : :
1262 : 14529 : xaccTransBeginEdit (s->parent);
1263 : 14529 : if (s->acc)
1264 : : {
1265 : 12700 : s->amount = gnc_numeric_convert(amt, get_commodity_denom(s),
1266 : : GNC_HOW_RND_ROUND_HALF_UP);
1267 : 12700 : g_assert (gnc_numeric_check (s->amount) == GNC_ERROR_OK);
1268 : : }
1269 : : else
1270 : 1829 : s->amount = amt;
1271 : 14529 : s->adjusted_amount = s->amount;
1272 : :
1273 : 14529 : SET_GAINS_ADIRTY(s);
1274 : 14529 : mark_split (s);
1275 : 14529 : qof_instance_set_dirty(QOF_INSTANCE(s));
1276 : 14529 : xaccTransCommitEdit(s->parent);
1277 : 14529 : LEAVE("");
1278 : : }
1279 : :
1280 : : static void
1281 : 0 : qofSplitSetValue (Split *split, gnc_numeric amt)
1282 : : {
1283 : 0 : g_return_if_fail(split);
1284 : 0 : split->value = gnc_numeric_convert(amt,
1285 : 0 : get_currency_denom(split), GNC_HOW_RND_ROUND_HALF_UP);
1286 : 0 : g_assert(gnc_numeric_check (split->value) != GNC_ERROR_OK);
1287 : : }
1288 : :
1289 : : /* The value of the split in the _transaction's_ currency. */
1290 : : void
1291 : 14847 : xaccSplitSetValue (Split *s, gnc_numeric amt)
1292 : : {
1293 : : gnc_numeric new_val;
1294 : 14847 : if (!s) return;
1295 : :
1296 : 14847 : g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK);
1297 : 14847 : ENTER ("(split=%p) old val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT
1298 : : " new val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s,
1299 : : s->value.num, s->value.denom, amt.num, amt.denom);
1300 : :
1301 : 14847 : xaccTransBeginEdit (s->parent);
1302 : 14847 : new_val = gnc_numeric_convert(amt, get_currency_denom(s),
1303 : : GNC_HOW_RND_ROUND_HALF_UP);
1304 : 29694 : if (gnc_numeric_check(new_val) == GNC_ERROR_OK &&
1305 : 14847 : !(gnc_numeric_zero_p (new_val) && !gnc_numeric_zero_p (amt)))
1306 : : {
1307 : 14841 : s->value = new_val;
1308 : : }
1309 : : else
1310 : : {
1311 : 6 : PERR("numeric error %s in converting the split value's denominator with amount %s and denom %d",
1312 : : gnc_numeric_errorCode_to_string(gnc_numeric_check(new_val)),
1313 : : gnc_num_dbg_to_string (amt), get_currency_denom(s));
1314 : : }
1315 : :
1316 : 14847 : SET_GAINS_VDIRTY(s);
1317 : 14847 : mark_split (s);
1318 : 14847 : qof_instance_set_dirty(QOF_INSTANCE(s));
1319 : 14847 : xaccTransCommitEdit(s->parent);
1320 : 14847 : LEAVE ("");
1321 : : }
1322 : :
1323 : : /********************************************************************\
1324 : : \********************************************************************/
1325 : :
1326 : : gnc_numeric
1327 : 991 : xaccSplitGetBalance (const Split *s)
1328 : : {
1329 : 991 : return s ? s->balance : gnc_numeric_zero();
1330 : : }
1331 : :
1332 : : gnc_numeric
1333 : 3913 : xaccSplitGetNoclosingBalance (const Split *s)
1334 : : {
1335 : 3913 : return s ? s->noclosing_balance : gnc_numeric_zero();
1336 : : }
1337 : :
1338 : : gnc_numeric
1339 : 0 : xaccSplitGetClearedBalance (const Split *s)
1340 : : {
1341 : 0 : return s ? s->cleared_balance : gnc_numeric_zero();
1342 : : }
1343 : :
1344 : : gnc_numeric
1345 : 0 : xaccSplitGetReconciledBalance (const Split *s)
1346 : : {
1347 : 0 : return s ? s->reconciled_balance : gnc_numeric_zero();
1348 : : }
1349 : :
1350 : : gnc_numeric
1351 : 2245 : xaccSplitGetAdjustedAmount (const Split *s)
1352 : : {
1353 : 2245 : return s ? s->adjusted_amount : gnc_numeric_zero();
1354 : : }
1355 : :
1356 : : void
1357 : 1213 : xaccSplitSetAdjustedAmount (Split *s, gnc_numeric amt)
1358 : : {
1359 : 1213 : if (!s) return;
1360 : 1213 : if (gnc_numeric_check (amt)) return;
1361 : :
1362 : 1213 : s->adjusted_amount = amt;
1363 : : }
1364 : :
1365 : : void
1366 : 156 : xaccSplitSetBaseValue (Split *s, gnc_numeric value,
1367 : : const gnc_commodity * base_currency)
1368 : : {
1369 : : const gnc_commodity *currency;
1370 : : const gnc_commodity *commodity;
1371 : :
1372 : 156 : if (!s) return;
1373 : 156 : xaccTransBeginEdit (s->parent);
1374 : :
1375 : 156 : if (!s->acc)
1376 : : {
1377 : 1 : PERR ("split must have a parent account");
1378 : 1 : return;
1379 : : }
1380 : :
1381 : 155 : currency = xaccTransGetCurrency (s->parent);
1382 : 155 : commodity = xaccAccountGetCommodity (s->acc);
1383 : :
1384 : : /* If the base_currency is the transaction's commodity ('currency'),
1385 : : * set the value. If it's the account commodity, set the
1386 : : * amount. If both, set both. */
1387 : 155 : if (gnc_commodity_equiv(currency, base_currency))
1388 : : {
1389 : 153 : if (gnc_commodity_equiv(commodity, base_currency))
1390 : : {
1391 : 152 : s->amount = gnc_numeric_convert(value,
1392 : 152 : get_commodity_denom(s),
1393 : : GNC_HOW_RND_ROUND_HALF_UP);
1394 : : }
1395 : 153 : s->value = gnc_numeric_convert(value,
1396 : 153 : get_currency_denom(s),
1397 : : GNC_HOW_RND_ROUND_HALF_UP);
1398 : : }
1399 : 2 : else if (gnc_commodity_equiv(commodity, base_currency))
1400 : : {
1401 : 1 : s->amount = gnc_numeric_convert(value, get_commodity_denom(s),
1402 : : GNC_HOW_RND_ROUND_HALF_UP);
1403 : : }
1404 : : else
1405 : : {
1406 : 1 : PERR ("inappropriate base currency %s "
1407 : : "given split currency=%s and commodity=%s\n",
1408 : : gnc_commodity_get_printname(base_currency),
1409 : : gnc_commodity_get_printname(currency),
1410 : : gnc_commodity_get_printname(commodity));
1411 : 1 : return;
1412 : : }
1413 : :
1414 : 154 : SET_GAINS_A_VDIRTY(s);
1415 : 154 : mark_split (s);
1416 : 154 : qof_instance_set_dirty(QOF_INSTANCE(s));
1417 : 154 : xaccTransCommitEdit(s->parent);
1418 : : }
1419 : :
1420 : : gnc_numeric
1421 : 0 : xaccSplitGetBaseValue (const Split *s, const gnc_commodity * base_currency)
1422 : : {
1423 : 0 : if (!s || !s->acc || !s->parent) return gnc_numeric_zero();
1424 : :
1425 : : /* be more precise -- the value depends on the currency we want it
1426 : : * expressed in. */
1427 : 0 : if (gnc_commodity_equiv(xaccTransGetCurrency(s->parent), base_currency))
1428 : 0 : return xaccSplitGetValue(s);
1429 : 0 : if (gnc_commodity_equiv(xaccAccountGetCommodity(s->acc), base_currency))
1430 : 0 : return xaccSplitGetAmount(s);
1431 : :
1432 : 0 : PERR ("inappropriate base currency %s "
1433 : : "given split currency=%s and commodity=%s\n",
1434 : : gnc_commodity_get_printname(base_currency),
1435 : : gnc_commodity_get_printname(xaccTransGetCurrency (s->parent)),
1436 : : gnc_commodity_get_printname(xaccAccountGetCommodity(s->acc)));
1437 : 0 : return gnc_numeric_zero();
1438 : : }
1439 : :
1440 : : /********************************************************************\
1441 : : \********************************************************************/
1442 : :
1443 : : gnc_numeric
1444 : 154 : xaccSplitConvertAmount (const Split *split, const Account * account)
1445 : : {
1446 : : gnc_commodity *acc_com, *to_commodity;
1447 : : Transaction *txn;
1448 : : gnc_numeric amount, value, convrate;
1449 : : Account * split_acc;
1450 : :
1451 : 154 : amount = xaccSplitGetAmount (split);
1452 : :
1453 : : /* If this split is attached to this account, OR */
1454 : 154 : split_acc = xaccSplitGetAccount (split);
1455 : 154 : if (split_acc == account)
1456 : 105 : return amount;
1457 : :
1458 : : /* If split->account->commodity == to_commodity, return the amount */
1459 : 49 : acc_com = xaccAccountGetCommodity (split_acc);
1460 : 49 : to_commodity = xaccAccountGetCommodity (account);
1461 : 49 : if (acc_com && gnc_commodity_equal (acc_com, to_commodity))
1462 : 45 : return amount;
1463 : :
1464 : : /* Ok, this split is not for the viewed account, and the commodity
1465 : : * does not match. So we need to do some conversion.
1466 : : *
1467 : : * First, we can cheat. If this transaction is balanced and has
1468 : : * exactly two splits, then we can implicitly determine the exchange
1469 : : * rate and just return the 'other' split amount.
1470 : : */
1471 : 4 : txn = xaccSplitGetParent (split);
1472 : 4 : if (txn && xaccTransIsBalanced (txn))
1473 : : {
1474 : 2 : const Split *osplit = xaccSplitGetOtherSplit (split);
1475 : :
1476 : 2 : if (osplit)
1477 : : {
1478 : : gnc_commodity* split_comm =
1479 : 2 : xaccAccountGetCommodity(xaccSplitGetAccount(osplit));
1480 : 2 : if (!gnc_commodity_equal(to_commodity, split_comm))
1481 : : {
1482 : : gchar guidstr[GUID_ENCODING_LENGTH+1];
1483 : 1 : guid_to_string_buff(xaccSplitGetGUID(osplit),guidstr);
1484 : 1 : PERR("The split's (%s) amount can't be converted from %s into %s.",
1485 : : guidstr,
1486 : : gnc_commodity_get_mnemonic(split_comm),
1487 : : gnc_commodity_get_mnemonic(to_commodity)
1488 : : );
1489 : 1 : return gnc_numeric_zero();
1490 : : }
1491 : 1 : return gnc_numeric_neg (xaccSplitGetAmount (osplit));
1492 : : }
1493 : : }
1494 : :
1495 : : /* ... otherwise, we need to compute the amount from the conversion
1496 : : * rate into _this account_. So, find the split into this account,
1497 : : * compute the conversion rate (based on amount/value), and then multiply
1498 : : * this times the split value.
1499 : : */
1500 : 2 : value = xaccSplitGetValue (split);
1501 : :
1502 : 2 : if (gnc_numeric_zero_p (value))
1503 : : {
1504 : 1 : return value;
1505 : : }
1506 : :
1507 : 1 : convrate = xaccTransGetAccountConvRate(txn, account);
1508 : 1 : return gnc_numeric_mul (value, convrate,
1509 : 1 : gnc_commodity_get_fraction (to_commodity),
1510 : 1 : GNC_HOW_RND_ROUND_HALF_UP);
1511 : : }
1512 : :
1513 : : /********************************************************************\
1514 : : \********************************************************************/
1515 : :
1516 : : gboolean
1517 : 4954 : xaccSplitDestroy (Split *split)
1518 : : {
1519 : : Account *acc;
1520 : : Transaction *trans;
1521 : : GncEventData ed;
1522 : :
1523 : 4954 : if (!split) return TRUE;
1524 : :
1525 : 4954 : acc = split->acc;
1526 : 4954 : trans = split->parent;
1527 : 4954 : if (acc && !qof_instance_get_destroying(acc)
1528 : 4935 : && !qof_instance_get_destroying(trans)
1529 : 9908 : && xaccTransGetReadOnly(trans))
1530 : 0 : return FALSE;
1531 : :
1532 : 4954 : xaccTransBeginEdit(trans);
1533 : 4954 : ed.node = split;
1534 : 4954 : ed.idx = xaccTransGetSplitIndex(trans, split);
1535 : 4954 : qof_instance_set_dirty(QOF_INSTANCE(split));
1536 : 4954 : qof_instance_set_destroying(split, TRUE);
1537 : 4954 : qof_event_gen(&trans->inst, GNC_EVENT_ITEM_REMOVED, &ed);
1538 : 4954 : xaccTransCommitEdit(trans);
1539 : :
1540 : 4954 : return TRUE;
1541 : : }
1542 : :
1543 : : /********************************************************************\
1544 : : \********************************************************************/
1545 : :
1546 : : gint
1547 : 686878 : xaccSplitOrder (const Split *sa, const Split *sb)
1548 : : {
1549 : : int retval;
1550 : : int comp;
1551 : : const char *da, *db;
1552 : : gboolean action_for_num;
1553 : :
1554 : 686878 : if (sa == sb) return 0;
1555 : : /* nothing is always less than something */
1556 : 686877 : if (!sa) return -1;
1557 : 686876 : if (!sb) return +1;
1558 : :
1559 : : /* sort in transaction order, but use split action rather than trans num
1560 : : * according to book option */
1561 : : action_for_num = qof_book_use_split_action_for_num_field
1562 : 686875 : (xaccSplitGetBook (sa));
1563 : 686875 : if (action_for_num)
1564 : 2 : retval = xaccTransOrder_num_action (sa->parent, sa->action,
1565 : 2 : sb->parent, sb->action);
1566 : : else
1567 : 686873 : retval = xaccTransOrder (sa->parent, sb->parent);
1568 : 686875 : if (retval) return retval;
1569 : :
1570 : : /* otherwise, sort on memo strings */
1571 : 9141 : da = sa->memo ? sa->memo : "";
1572 : 9141 : db = sb->memo ? sb->memo : "";
1573 : 9141 : retval = g_utf8_collate (da, db);
1574 : 9141 : if (retval)
1575 : 192 : return retval;
1576 : :
1577 : : /* otherwise, sort on action strings */
1578 : 8949 : da = sa->action ? sa->action : "";
1579 : 8949 : db = sb->action ? sb->action : "";
1580 : 8949 : retval = g_utf8_collate (da, db);
1581 : 8949 : if (retval != 0)
1582 : 570 : return retval;
1583 : :
1584 : : /* the reconciled flag ... */
1585 : 8379 : if (sa->reconciled < sb->reconciled) return -1;
1586 : 8378 : if (sa->reconciled > sb->reconciled) return +1;
1587 : :
1588 : : /* compare amounts */
1589 : 8377 : comp = gnc_numeric_compare(xaccSplitGetAmount(sa), xaccSplitGetAmount (sb));
1590 : 8377 : if (comp < 0) return -1;
1591 : 4982 : if (comp > 0) return +1;
1592 : :
1593 : 341 : comp = gnc_numeric_compare(xaccSplitGetValue(sa), xaccSplitGetValue (sb));
1594 : 341 : if (comp < 0) return -1;
1595 : 338 : if (comp > 0) return +1;
1596 : :
1597 : : /* if dates differ, return */
1598 : 335 : if (sa->date_reconciled < sb->date_reconciled)
1599 : 1 : return -1;
1600 : 334 : else if (sa->date_reconciled > sb->date_reconciled)
1601 : 2 : return 1;
1602 : :
1603 : : /* else, sort on guid - keeps sort stable. */
1604 : 332 : retval = qof_instance_guid_compare(sa, sb);
1605 : 332 : if (retval) return retval;
1606 : :
1607 : 0 : return 0;
1608 : : }
1609 : :
1610 : : gint
1611 : 218 : xaccSplitOrderDateOnly (const Split *sa, const Split *sb)
1612 : : {
1613 : : Transaction *ta, *tb;
1614 : :
1615 : 218 : if (sa == sb) return 0;
1616 : : /* nothing is always less than something */
1617 : 217 : if (!sa) return -1;
1618 : 216 : if (!sb) return +1;
1619 : :
1620 : 215 : ta = sa->parent;
1621 : 215 : tb = sb->parent;
1622 : 215 : if ( !ta && !tb ) return 0;
1623 : 214 : if ( !tb ) return -1;
1624 : 213 : if ( !ta ) return +1;
1625 : :
1626 : 212 : if (ta->date_posted == tb->date_posted)
1627 : 110 : return -1; // Keep the same order
1628 : 102 : return (ta->date_posted > tb->date_posted) - (ta->date_posted < tb->date_posted);
1629 : : }
1630 : :
1631 : : static gboolean
1632 : 29 : get_corr_account_split(const Split *sa, const Split **retval)
1633 : : {
1634 : 29 : *retval = nullptr;
1635 : 29 : g_return_val_if_fail(sa, FALSE);
1636 : :
1637 : 28 : if (xaccTransCountSplits (sa->parent) > 2)
1638 : 2 : return FALSE;
1639 : :
1640 : 26 : *retval = xaccSplitGetOtherSplit (sa);
1641 : 26 : if (*retval)
1642 : 23 : return TRUE;
1643 : : else
1644 : 3 : return FALSE;
1645 : : }
1646 : :
1647 : : /* TODO: these static consts can be shared. */
1648 : : const char *
1649 : 0 : xaccSplitGetCorrAccountName(const Split *sa)
1650 : : {
1651 : : static const char *split_const = nullptr;
1652 : : const Split *other_split;
1653 : :
1654 : 0 : if (!get_corr_account_split(sa, &other_split))
1655 : : {
1656 : 0 : if (!split_const)
1657 : 0 : split_const = _("-- Split Transaction --");
1658 : :
1659 : 0 : return split_const;
1660 : : }
1661 : :
1662 : 0 : return xaccAccountGetName(other_split->acc);
1663 : : }
1664 : :
1665 : : char *
1666 : 20 : xaccSplitGetCorrAccountFullName(const Split *sa)
1667 : : {
1668 : : static const char *split_const = nullptr;
1669 : : const Split *other_split;
1670 : :
1671 : 20 : if (!get_corr_account_split(sa, &other_split))
1672 : : {
1673 : 1 : if (!split_const)
1674 : 1 : split_const = _("-- Split Transaction --");
1675 : :
1676 : 2 : return g_strdup(split_const);
1677 : : }
1678 : 19 : return gnc_account_get_full_name(other_split->acc);
1679 : : }
1680 : :
1681 : : const char *
1682 : 4 : xaccSplitGetCorrAccountCode(const Split *sa)
1683 : : {
1684 : : static const char *split_const = nullptr;
1685 : : const Split *other_split;
1686 : :
1687 : 4 : if (!get_corr_account_split(sa, &other_split))
1688 : : {
1689 : 1 : if (!split_const)
1690 : 1 : split_const = C_("Displayed account code of the other account in a multi-split transaction", "Split");
1691 : :
1692 : 1 : return split_const;
1693 : : }
1694 : 3 : return xaccAccountGetCode(other_split->acc);
1695 : : }
1696 : :
1697 : : /* TODO: It's not too hard to make this function avoid the malloc/free. */
1698 : : int
1699 : 1454 : xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb)
1700 : : {
1701 : : Account *aa, *ab;
1702 : 1454 : if (sa == sb) return 0;
1703 : 1453 : if (!sa) return -1;
1704 : 1452 : if (!sb) return 1;
1705 : :
1706 : 1451 : aa = sa->acc;
1707 : 1451 : ab = sb->acc;
1708 : 1451 : if (aa == ab) return 0;
1709 : :
1710 : 99 : auto path_a = gnc_account_get_all_parents (aa);
1711 : 99 : auto path_b = gnc_account_get_all_parents (ab);
1712 : 99 : auto mismatch_pair = std::mismatch (path_a.rbegin(), path_a.rend(),
1713 : 198 : path_b.rbegin(), path_b.rend());
1714 : :
1715 : 198 : return mismatch_pair.first == path_a.rend() ? -1
1716 : 99 : : mismatch_pair.second == path_b.rend() ? 1
1717 : 99 : : g_utf8_collate (xaccAccountGetName (*mismatch_pair.first),
1718 : 99 : xaccAccountGetName (*mismatch_pair.second));
1719 : 0 : }
1720 : :
1721 : :
1722 : : int
1723 : 4 : xaccSplitCompareAccountCodes(const Split *sa, const Split *sb)
1724 : : {
1725 : : Account *aa, *ab;
1726 : 4 : if (!sa && !sb) return 0;
1727 : 3 : if (!sa) return -1;
1728 : 2 : if (!sb) return 1;
1729 : :
1730 : 1 : aa = sa->acc;
1731 : 1 : ab = sb->acc;
1732 : :
1733 : 1 : return g_strcmp0(xaccAccountGetCode(aa), xaccAccountGetCode(ab));
1734 : : }
1735 : :
1736 : : int
1737 : 12 : xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb)
1738 : : {
1739 : : char *ca, *cb;
1740 : : int retval;
1741 : 12 : if (!sa && !sb) return 0;
1742 : 11 : if (!sa) return -1;
1743 : 10 : if (!sb) return 1;
1744 : :
1745 : : /* doesn't matter what separator we use
1746 : : * as long as they are the same
1747 : : */
1748 : :
1749 : 9 : ca = xaccSplitGetCorrAccountFullName(sa);
1750 : 9 : cb = xaccSplitGetCorrAccountFullName(sb);
1751 : 9 : retval = g_strcmp0(ca, cb);
1752 : 9 : g_free(ca);
1753 : 9 : g_free(cb);
1754 : 9 : return retval;
1755 : : }
1756 : :
1757 : : int
1758 : 4 : xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb)
1759 : : {
1760 : : const char *ca, *cb;
1761 : 4 : if (!sa && !sb) return 0;
1762 : 3 : if (!sa) return -1;
1763 : 2 : if (!sb) return 1;
1764 : :
1765 : 1 : ca = xaccSplitGetCorrAccountCode(sa);
1766 : 1 : cb = xaccSplitGetCorrAccountCode(sb);
1767 : 1 : return g_strcmp0(ca, cb);
1768 : : }
1769 : :
1770 : : static void
1771 : 0 : qofSplitSetMemo (Split *split, const char* memo)
1772 : : {
1773 : 0 : g_return_if_fail(split);
1774 : 0 : CACHE_REPLACE(split->memo, memo);
1775 : : }
1776 : :
1777 : : void
1778 : 1883 : xaccSplitSetMemo (Split *split, const char *memo)
1779 : : {
1780 : 1883 : if (!split || !memo) return;
1781 : 1782 : xaccTransBeginEdit (split->parent);
1782 : :
1783 : 1782 : CACHE_REPLACE(split->memo, memo);
1784 : 1782 : qof_instance_set_dirty(QOF_INSTANCE(split));
1785 : 1782 : xaccTransCommitEdit(split->parent);
1786 : :
1787 : : }
1788 : :
1789 : : static void
1790 : 0 : qofSplitSetAction (Split *split, const char *actn)
1791 : : {
1792 : 0 : g_return_if_fail(split);
1793 : 0 : CACHE_REPLACE(split->action, actn);
1794 : : }
1795 : :
1796 : : void
1797 : 1110 : xaccSplitSetAction (Split *split, const char *actn)
1798 : : {
1799 : 1110 : if (!split || !actn) return;
1800 : 1110 : xaccTransBeginEdit (split->parent);
1801 : :
1802 : 1110 : CACHE_REPLACE(split->action, actn);
1803 : 1110 : qof_instance_set_dirty(QOF_INSTANCE(split));
1804 : 1110 : xaccTransCommitEdit(split->parent);
1805 : :
1806 : : }
1807 : :
1808 : : static void
1809 : 0 : qofSplitSetReconcile (Split *split, char recn)
1810 : : {
1811 : 0 : g_return_if_fail(split);
1812 : 0 : switch (recn)
1813 : : {
1814 : 0 : case NREC:
1815 : : case CREC:
1816 : : case YREC:
1817 : : case FREC:
1818 : : case VREC:
1819 : 0 : split->reconciled = recn;
1820 : 0 : mark_split (split);
1821 : 0 : xaccAccountRecomputeBalance (split->acc);
1822 : 0 : break;
1823 : 0 : default:
1824 : 0 : PERR("Bad reconciled flag");
1825 : 0 : break;
1826 : : }
1827 : : }
1828 : :
1829 : : void
1830 : 2932 : xaccSplitSetReconcile (Split *split, char recn)
1831 : : {
1832 : 2932 : if (!split || split->reconciled == recn) return;
1833 : 1143 : xaccTransBeginEdit (split->parent);
1834 : :
1835 : 1143 : switch (recn)
1836 : : {
1837 : 1143 : case NREC:
1838 : : case CREC:
1839 : : case YREC:
1840 : : case FREC:
1841 : : case VREC:
1842 : 1143 : split->reconciled = recn;
1843 : 1143 : mark_split (split);
1844 : 1143 : qof_instance_set_dirty(QOF_INSTANCE(split));
1845 : 1143 : xaccAccountRecomputeBalance (split->acc);
1846 : 1143 : break;
1847 : 0 : default:
1848 : 0 : PERR("Bad reconciled flag");
1849 : 0 : break;
1850 : : }
1851 : 1143 : xaccTransCommitEdit(split->parent);
1852 : :
1853 : : }
1854 : :
1855 : : void
1856 : 788 : xaccSplitSetDateReconciledSecs (Split *split, time64 secs)
1857 : : {
1858 : 788 : if (!split) return;
1859 : 788 : xaccTransBeginEdit (split->parent);
1860 : :
1861 : 788 : split->date_reconciled = secs;
1862 : 788 : qof_instance_set_dirty(QOF_INSTANCE(split));
1863 : 788 : xaccTransCommitEdit(split->parent);
1864 : :
1865 : : }
1866 : :
1867 : :
1868 : : /*################## Added for Reg2 #################*/
1869 : : time64
1870 : 161 : xaccSplitGetDateReconciled (const Split * split)
1871 : : {
1872 : 161 : return split ? split->date_reconciled : 0;
1873 : : }
1874 : : /*################## Added for Reg2 #################*/
1875 : :
1876 : : /********************************************************************\
1877 : : \********************************************************************/
1878 : :
1879 : : /* return the parent transaction of the split */
1880 : : Transaction *
1881 : 114026 : xaccSplitGetParent (const Split *split)
1882 : : {
1883 : 114026 : return split ? split->parent : nullptr;
1884 : : }
1885 : :
1886 : : void
1887 : 7186 : xaccSplitSetParent(Split *s, Transaction *t)
1888 : : {
1889 : : Transaction *old_trans;
1890 : : GncEventData ed;
1891 : :
1892 : 7188 : g_return_if_fail(s);
1893 : 7186 : if (s->parent == t) return;
1894 : :
1895 : 7184 : if (s->parent != s->orig_parent && s->orig_parent != t)
1896 : 1 : PERR("You may not add the split to more than one transaction"
1897 : : " during the BeginEdit/CommitEdit block.");
1898 : 7184 : xaccTransBeginEdit(t);
1899 : 7184 : old_trans = s->parent;
1900 : :
1901 : 7184 : xaccTransBeginEdit(old_trans);
1902 : :
1903 : 7184 : ed.node = s;
1904 : 7184 : if (old_trans)
1905 : : {
1906 : 12 : ed.idx = xaccTransGetSplitIndex(old_trans, s);
1907 : 12 : qof_event_gen(&old_trans->inst, GNC_EVENT_ITEM_REMOVED, &ed);
1908 : : }
1909 : 7184 : s->parent = t;
1910 : 7184 : qof_instance_set_dirty(QOF_INSTANCE(s));
1911 : :
1912 : 7184 : xaccTransCommitEdit(old_trans);
1913 : :
1914 : 7184 : if (t)
1915 : : {
1916 : : /* Convert split to new transaction's commodity denominator */
1917 : 7176 : xaccSplitSetValue(s, xaccSplitGetValue(s));
1918 : :
1919 : : /* add ourselves to the new transaction's list of pending splits. */
1920 : 7176 : if (nullptr == g_list_find(t->splits, s))
1921 : 7176 : t->splits = g_list_append(t->splits, s);
1922 : :
1923 : 7176 : ed.idx = -1; /* unused */
1924 : 7176 : qof_event_gen(&t->inst, GNC_EVENT_ITEM_ADDED, &ed);
1925 : : }
1926 : 7184 : xaccTransCommitEdit(t);
1927 : : }
1928 : :
1929 : :
1930 : : GNCLot *
1931 : 515 : xaccSplitGetLot (const Split *split)
1932 : : {
1933 : 515 : return split ? split->lot : nullptr;
1934 : : }
1935 : :
1936 : : void
1937 : 291 : xaccSplitSetLot(Split* split, GNCLot* lot)
1938 : : {
1939 : 291 : xaccTransBeginEdit (split->parent);
1940 : 291 : split->lot = lot;
1941 : 291 : qof_instance_set_dirty(QOF_INSTANCE(split));
1942 : 291 : xaccTransCommitEdit(split->parent);
1943 : 291 : }
1944 : :
1945 : : const char *
1946 : 2469 : xaccSplitGetMemo (const Split *split)
1947 : : {
1948 : 2469 : return split ? split->memo : nullptr;
1949 : : }
1950 : :
1951 : : void
1952 : 6 : xaccSplitSetOnlineID (Split *split, const char *id)
1953 : : {
1954 : 6 : if (!split) return;
1955 : : /* No xaccTransBeginEdit/CommitEdit here: the online_id is normally set
1956 : : * while many other parameters of a transaction are being changed, so
1957 : : * the caller wraps this in the parent transaction's edit. */
1958 : 6 : std::optional<const char*> val;
1959 : 6 : if (id && *id)
1960 : 3 : val = g_strdup (id);
1961 : 18 : qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(split), val, {split_online_id});
1962 : 6 : qof_instance_set_dirty (QOF_INSTANCE(split));
1963 : 18 : }
1964 : :
1965 : : const char *
1966 : 13 : xaccSplitGetOnlineID (const Split *split)
1967 : : {
1968 : 13 : if (!split) return nullptr;
1969 : 39 : auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(split), {split_online_id})};
1970 : 13 : return rv ? *rv : nullptr;
1971 : 39 : }
1972 : :
1973 : : gboolean
1974 : 6 : xaccSplitHasOnlineID (const Split *split)
1975 : : {
1976 : 6 : auto id = xaccSplitGetOnlineID (split);
1977 : 6 : return (id && *id);
1978 : : }
1979 : :
1980 : : const char *
1981 : 1426 : xaccSplitGetAction (const Split *split)
1982 : : {
1983 : 1426 : return split ? split->action : nullptr;
1984 : : }
1985 : :
1986 : : char
1987 : 33064 : xaccSplitGetReconcile (const Split *split)
1988 : : {
1989 : 33064 : return split ? split->reconciled : ' ';
1990 : : }
1991 : :
1992 : :
1993 : : gnc_numeric
1994 : 160960 : xaccSplitGetAmount (const Split * split)
1995 : : {
1996 : 160960 : return split ? split->amount : gnc_numeric_zero();
1997 : : }
1998 : :
1999 : : gnc_numeric
2000 : 56088 : xaccSplitGetValue (const Split * split)
2001 : : {
2002 : 56088 : return split ? split->value : gnc_numeric_zero();
2003 : : }
2004 : :
2005 : : gnc_numeric
2006 : 416 : xaccSplitGetSharePrice (const Split * split)
2007 : : {
2008 : : gnc_numeric amt, val, price;
2009 : 416 : if (!split) return gnc_numeric_create(0, 1);
2010 : :
2011 : :
2012 : : /* if amount == 0, return 0
2013 : : * otherwise return value/amount
2014 : : */
2015 : :
2016 : 415 : amt = xaccSplitGetAmount(split);
2017 : 415 : val = xaccSplitGetValue(split);
2018 : 415 : if (gnc_numeric_zero_p(amt))
2019 : 48 : return gnc_numeric_create(0, 1);
2020 : :
2021 : 367 : price = gnc_numeric_div(val, amt,
2022 : : GNC_DENOM_AUTO,
2023 : : GNC_HOW_RND_ROUND_HALF_UP);
2024 : :
2025 : : /* During random checks we can get some very weird prices. Let's
2026 : : * handle some overflow and other error conditions by returning
2027 : : * zero. But still print an error to let us know it happened.
2028 : : */
2029 : 367 : if (gnc_numeric_check(price))
2030 : : {
2031 : 2 : PERR("Computing share price failed (%d): [ %" G_GINT64_FORMAT " / %"
2032 : : G_GINT64_FORMAT " ] / [ %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " ]",
2033 : : gnc_numeric_check(price), val.num, val.denom, amt.num, amt.denom);
2034 : 2 : return gnc_numeric_create(0, 1);
2035 : : }
2036 : :
2037 : 365 : return price;
2038 : : }
2039 : :
2040 : : /********************************************************************\
2041 : : \********************************************************************/
2042 : :
2043 : : QofBook *
2044 : 704461 : xaccSplitGetBook (const Split *split)
2045 : : {
2046 : 704461 : return qof_instance_get_book(QOF_INSTANCE(split));
2047 : : }
2048 : :
2049 : : const char *
2050 : 99029 : xaccSplitGetType(Split *s)
2051 : : {
2052 : 99029 : if (!s) return nullptr;
2053 : :
2054 : 99029 : if (!s->split_type)
2055 : : {
2056 : 14112 : auto type{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(s), {"split-type"})};
2057 : :
2058 : 7056 : if (!type || !g_strcmp0 (*type, split_type_normal))
2059 : 7056 : s->split_type = split_type_normal;
2060 : 0 : else if (!g_strcmp0 (*type, split_type_stock_split))
2061 : 0 : s->split_type = split_type_stock_split;
2062 : : else
2063 : : {
2064 : 0 : PERR ("unexpected split-type %s, reset to normal.", *type);
2065 : 0 : s->split_type = split_type_normal;
2066 : : }
2067 : : }
2068 : 99029 : return s->split_type;
2069 : : }
2070 : :
2071 : : /* reconfigure a split to be a stock split - after this, you shouldn't
2072 : : mess with the value, just the amount. */
2073 : : void
2074 : 15 : xaccSplitMakeStockSplit(Split *s)
2075 : : {
2076 : 15 : xaccTransBeginEdit (s->parent);
2077 : :
2078 : 15 : s->value = gnc_numeric_zero();
2079 : 15 : s->split_type = split_type_stock_split;
2080 : 45 : qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(s), g_strdup(split_type_stock_split),
2081 : : {"split-type"});
2082 : 15 : SET_GAINS_VDIRTY(s);
2083 : 15 : mark_split(s);
2084 : 15 : qof_instance_set_dirty(QOF_INSTANCE(s));
2085 : 15 : xaccTransCommitEdit(s->parent);
2086 : 15 : }
2087 : :
2088 : : gboolean
2089 : 99015 : xaccSplitIsStockSplit (Split *s)
2090 : : {
2091 : 99015 : return g_strcmp0(split_type_stock_split, xaccSplitGetType(s)) == 0;
2092 : : }
2093 : :
2094 : : void
2095 : 8 : xaccSplitAddPeerSplit (Split *split, const Split *other_split,
2096 : : time64 timestamp)
2097 : : {
2098 : : const GncGUID* guid;
2099 : :
2100 : 8 : g_return_if_fail (split != nullptr);
2101 : 8 : g_return_if_fail (other_split != nullptr);
2102 : :
2103 : 8 : guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
2104 : 8 : xaccTransBeginEdit (split->parent);
2105 : 8 : qof_instance_kvp_add_guid (QOF_INSTANCE (split), "lot-split",
2106 : 8 : gnc_time(nullptr), "peer_guid", guid_copy(guid));
2107 : 8 : mark_split (split);
2108 : 8 : qof_instance_set_dirty (QOF_INSTANCE (split));
2109 : 8 : xaccTransCommitEdit (split->parent);
2110 : : }
2111 : :
2112 : : gboolean
2113 : 114 : xaccSplitHasPeers (const Split *split)
2114 : : {
2115 : 114 : return qof_instance_has_slot (QOF_INSTANCE (split), "lot-split");
2116 : : }
2117 : :
2118 : : gboolean
2119 : 1 : xaccSplitIsPeerSplit (const Split *split, const Split *other_split)
2120 : : {
2121 : : const GncGUID* guid;
2122 : :
2123 : 1 : g_return_val_if_fail (split != nullptr, FALSE);
2124 : 1 : g_return_val_if_fail (other_split != nullptr, FALSE);
2125 : :
2126 : 1 : guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
2127 : 1 : return qof_instance_kvp_has_guid (QOF_INSTANCE (split), "lot-split",
2128 : 1 : "peer_guid", guid);
2129 : : }
2130 : :
2131 : : void
2132 : 0 : xaccSplitRemovePeerSplit (Split *split, const Split *other_split)
2133 : : {
2134 : : const GncGUID* guid;
2135 : :
2136 : 0 : g_return_if_fail (split != nullptr);
2137 : 0 : g_return_if_fail (other_split != nullptr);
2138 : :
2139 : 0 : guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
2140 : 0 : xaccTransBeginEdit (split->parent);
2141 : 0 : qof_instance_kvp_remove_guid (QOF_INSTANCE (split), "lot-split",
2142 : : "peer_guid", guid);
2143 : 0 : mark_split (split);
2144 : 0 : qof_instance_set_dirty (QOF_INSTANCE (split));
2145 : 0 : xaccTransCommitEdit (split->parent);
2146 : : }
2147 : :
2148 : : void
2149 : 0 : xaccSplitMergePeerSplits (Split *split, const Split *other_split)
2150 : : {
2151 : 0 : xaccTransBeginEdit (split->parent);
2152 : 0 : qof_instance_kvp_merge_guids (QOF_INSTANCE (split),
2153 : 0 : QOF_INSTANCE (other_split), "lot-split");
2154 : 0 : mark_split (split);
2155 : 0 : qof_instance_set_dirty (QOF_INSTANCE (split));
2156 : 0 : xaccTransCommitEdit (split->parent);
2157 : 0 : }
2158 : :
2159 : : /********************************************************************\
2160 : : \********************************************************************/
2161 : : /* In the old world, the 'other split' was the other split of a
2162 : : * transaction that contained only two splits. In the new world,
2163 : : * a split may have been cut up between multiple lots, although
2164 : : * in a conceptual sense, if lots hadn't been used, there would be
2165 : : * only a pair. So we handle this conceptual case: we can still
2166 : : * identify, unambiguously, the 'other' split when 'this' split
2167 : : * as been cut up across lots. We do this by looking for the
2168 : : * 'lot-split' keyword, which occurs only in cut-up splits.
2169 : : */
2170 : :
2171 : : Split *
2172 : 222 : xaccSplitGetOtherSplit (const Split *split)
2173 : : {
2174 : : Transaction *trans;
2175 : 222 : Split *other = nullptr;
2176 : :
2177 : 222 : if (!split) return nullptr;
2178 : 221 : trans = split->parent;
2179 : 221 : if (!trans) return nullptr;
2180 : :
2181 : 643 : for (GList *n = xaccTransGetSplitList (trans); n; n = n->next)
2182 : : {
2183 : 462 : Split *s = GNC_SPLIT(n->data);
2184 : 926 : if ((s == split) ||
2185 : 257 : (!xaccTransStillHasSplit(trans, s)) ||
2186 : 975 : (xaccAccountGetType (xaccSplitGetAccount (s)) == ACCT_TYPE_TRADING) ||
2187 : 256 : (qof_instance_has_slot (QOF_INSTANCE (s), "lot-split")))
2188 : 207 : continue;
2189 : :
2190 : 255 : if (other)
2191 : 39 : return nullptr;
2192 : :
2193 : 216 : other = s;
2194 : : }
2195 : 181 : return other;
2196 : : }
2197 : :
2198 : : /********************************************************************\
2199 : : \********************************************************************/
2200 : :
2201 : : gnc_numeric
2202 : 65 : xaccSplitVoidFormerAmount(const Split *split)
2203 : : {
2204 : 65 : g_return_val_if_fail(split, gnc_numeric_zero());
2205 : 195 : auto num{qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {void_former_amt_str})};
2206 : 65 : return num ? *num : gnc_numeric_zero();
2207 : 195 : }
2208 : :
2209 : : gnc_numeric
2210 : 12 : xaccSplitVoidFormerValue(const Split *split)
2211 : : {
2212 : 12 : g_return_val_if_fail(split, gnc_numeric_zero());
2213 : 36 : auto num{qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {void_former_val_str})};
2214 : 12 : return num ? *num : gnc_numeric_zero();
2215 : 36 : }
2216 : :
2217 : : void
2218 : 205 : xaccSplitVoid(Split *split)
2219 : : {
2220 : 205 : g_return_if_fail (GNC_IS_SPLIT(split));
2221 : 615 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), xaccSplitGetAmount(split), {void_former_amt_str});
2222 : 615 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), xaccSplitGetValue(split), {void_former_val_str});
2223 : 205 : qof_instance_set_dirty (QOF_INSTANCE(split));
2224 : :
2225 : 205 : static gnc_numeric zero = gnc_numeric_zero();
2226 : 205 : xaccSplitSetAmount (split, zero);
2227 : 205 : xaccSplitSetValue (split, zero);
2228 : 205 : xaccSplitSetReconcile(split, VREC);
2229 : 1025 : }
2230 : :
2231 : : void
2232 : 7 : xaccSplitUnvoid(Split *split)
2233 : : {
2234 : 7 : g_return_if_fail (GNC_IS_SPLIT(split));
2235 : 7 : xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split));
2236 : 7 : xaccSplitSetValue (split, xaccSplitVoidFormerValue(split));
2237 : 7 : xaccSplitSetReconcile(split, NREC);
2238 : 21 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {}, {void_former_amt_str});
2239 : 21 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {}, {void_former_val_str});
2240 : 7 : qof_instance_set_dirty (QOF_INSTANCE(split));
2241 : 35 : }
2242 : :
2243 : : /********************************************************************\
2244 : : \********************************************************************/
2245 : : /* QofObject function implementation */
2246 : :
2247 : : /* Hook into the QofObject registry */
2248 : :
2249 : : #ifdef _MSC_VER
2250 : : /* MSVC compiler doesn't have C99 "designated initializers"
2251 : : * so we wrap them in a macro that is empty on MSVC. */
2252 : : # define DI(x) /* */
2253 : : #else
2254 : : # define DI(x) x
2255 : : #endif
2256 : : static QofObject split_object_def =
2257 : : {
2258 : : DI(.interface_version = ) QOF_OBJECT_VERSION,
2259 : : DI(.e_type = ) GNC_ID_SPLIT,
2260 : : DI(.type_label = ) "Split",
2261 : : DI(.create = ) (void* (*)(QofBook*))xaccMallocSplit,
2262 : : DI(.book_begin = ) nullptr,
2263 : : DI(.book_end = ) nullptr,
2264 : : DI(.is_dirty = ) qof_collection_is_dirty,
2265 : : DI(.mark_clean = ) qof_collection_mark_clean,
2266 : : DI(.foreach = ) qof_collection_foreach,
2267 : : DI(.printable = ) (const char * (*)(gpointer)) xaccSplitGetMemo,
2268 : : DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
2269 : : };
2270 : :
2271 : : static gpointer
2272 : 613 : split_account_guid_getter (gpointer obj, const QofParam *p)
2273 : : {
2274 : 613 : Split *s = GNC_SPLIT(obj);
2275 : : Account *acc;
2276 : :
2277 : 613 : if (!s) return nullptr;
2278 : 613 : acc = xaccSplitGetAccount (s);
2279 : 613 : if (!acc) return nullptr;
2280 : 613 : return ((gpointer)xaccAccountGetGUID (acc));
2281 : : }
2282 : :
2283 : : static double /* internal use only */
2284 : 0 : DxaccSplitGetShareAmount (const Split * split)
2285 : : {
2286 : 0 : return split ? gnc_numeric_to_double(xaccSplitGetAmount(split)) : 0.0;
2287 : : }
2288 : :
2289 : : static gpointer
2290 : 2916 : no_op (gpointer obj, const QofParam *p)
2291 : : {
2292 : 2916 : return obj;
2293 : : }
2294 : :
2295 : : static void
2296 : 0 : qofSplitSetParentTrans(Split *s, QofInstance *ent)
2297 : : {
2298 : 0 : Transaction *trans = (Transaction*)ent;
2299 : :
2300 : 0 : g_return_if_fail(trans);
2301 : 0 : xaccSplitSetParent(s, trans);
2302 : : }
2303 : :
2304 : : static void
2305 : 0 : qofSplitSetAccount(Split *s, QofInstance *ent)
2306 : : {
2307 : 0 : Account *acc = (Account*)ent;
2308 : :
2309 : 0 : g_return_if_fail(acc);
2310 : 0 : xaccSplitSetAccount(s, acc);
2311 : : }
2312 : :
2313 : 83 : gboolean xaccSplitRegister (void)
2314 : : {
2315 : : static const QofParam params[] =
2316 : : {
2317 : : {
2318 : : SPLIT_DATE_RECONCILED, QOF_TYPE_DATE,
2319 : : (QofAccessFunc)xaccSplitGetDateReconciled,
2320 : : (QofSetterFunc)xaccSplitSetDateReconciledSecs
2321 : : },
2322 : :
2323 : : /* d-* are deprecated query params, should not be used in new
2324 : : * queries, should be removed from old queries. */
2325 : : {
2326 : : "d-share-amount", QOF_TYPE_DOUBLE,
2327 : : (QofAccessFunc)DxaccSplitGetShareAmount, nullptr
2328 : : },
2329 : : {
2330 : : "d-share-int64", QOF_TYPE_INT64,
2331 : : (QofAccessFunc)qof_entity_get_guid, nullptr
2332 : : },
2333 : : {
2334 : : SPLIT_BALANCE, QOF_TYPE_NUMERIC,
2335 : : (QofAccessFunc)xaccSplitGetBalance, nullptr
2336 : : },
2337 : : {
2338 : : SPLIT_CLEARED_BALANCE, QOF_TYPE_NUMERIC,
2339 : : (QofAccessFunc)xaccSplitGetClearedBalance, nullptr
2340 : : },
2341 : : {
2342 : : SPLIT_RECONCILED_BALANCE, QOF_TYPE_NUMERIC,
2343 : : (QofAccessFunc)xaccSplitGetReconciledBalance, nullptr
2344 : : },
2345 : : {
2346 : : SPLIT_MEMO, QOF_TYPE_STRING,
2347 : : (QofAccessFunc)xaccSplitGetMemo, (QofSetterFunc)qofSplitSetMemo
2348 : : },
2349 : : {
2350 : : SPLIT_ACTION, QOF_TYPE_STRING,
2351 : : (QofAccessFunc)xaccSplitGetAction, (QofSetterFunc)qofSplitSetAction
2352 : : },
2353 : : {
2354 : : SPLIT_RECONCILE, QOF_TYPE_CHAR,
2355 : : (QofAccessFunc)xaccSplitGetReconcile,
2356 : : (QofSetterFunc)qofSplitSetReconcile
2357 : : },
2358 : : {
2359 : : SPLIT_AMOUNT, QOF_TYPE_NUMERIC,
2360 : : (QofAccessFunc)xaccSplitGetAmount, (QofSetterFunc)qofSplitSetAmount
2361 : : },
2362 : : {
2363 : : SPLIT_SHARE_PRICE, QOF_TYPE_NUMERIC,
2364 : : (QofAccessFunc)xaccSplitGetSharePrice,
2365 : : (QofSetterFunc)qofSplitSetSharePrice
2366 : : },
2367 : : {
2368 : : SPLIT_VALUE, QOF_TYPE_DEBCRED,
2369 : : (QofAccessFunc)xaccSplitGetValue, (QofSetterFunc)qofSplitSetValue
2370 : : },
2371 : : { SPLIT_TYPE, QOF_TYPE_STRING, (QofAccessFunc)xaccSplitGetType, nullptr },
2372 : : {
2373 : : SPLIT_VOIDED_AMOUNT, QOF_TYPE_NUMERIC,
2374 : : (QofAccessFunc)xaccSplitVoidFormerAmount, nullptr
2375 : : },
2376 : : {
2377 : : SPLIT_VOIDED_VALUE, QOF_TYPE_NUMERIC,
2378 : : (QofAccessFunc)xaccSplitVoidFormerValue, nullptr
2379 : : },
2380 : : { SPLIT_LOT, GNC_ID_LOT, (QofAccessFunc)xaccSplitGetLot, nullptr },
2381 : : {
2382 : : SPLIT_TRANS, GNC_ID_TRANS,
2383 : : (QofAccessFunc)xaccSplitGetParent,
2384 : : (QofSetterFunc)qofSplitSetParentTrans
2385 : : },
2386 : : {
2387 : : SPLIT_ACCOUNT, GNC_ID_ACCOUNT,
2388 : : (QofAccessFunc)xaccSplitGetAccount, (QofSetterFunc)qofSplitSetAccount
2389 : : },
2390 : : { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, nullptr },
2391 : : /* these are no-ops to register the parameter names (for sorting) but
2392 : : they return an allocated object which getters cannot do. */
2393 : : { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, nullptr },
2394 : : { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, nullptr },
2395 : : { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, nullptr },
2396 : : { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, nullptr },
2397 : : {
2398 : : QOF_PARAM_GUID, QOF_TYPE_GUID,
2399 : : (QofAccessFunc)qof_entity_get_guid, nullptr
2400 : : },
2401 : : { nullptr },
2402 : : };
2403 : :
2404 : 83 : qof_class_register (GNC_ID_SPLIT, (QofSortFunc)xaccSplitOrder, params);
2405 : 83 : qof_class_register (SPLIT_ACCT_FULLNAME,
2406 : : (QofSortFunc)xaccSplitCompareAccountFullNames, nullptr);
2407 : 83 : qof_class_register (SPLIT_CORR_ACCT_NAME,
2408 : : (QofSortFunc)xaccSplitCompareOtherAccountFullNames,
2409 : : nullptr);
2410 : 83 : qof_class_register (SPLIT_CORR_ACCT_CODE,
2411 : : (QofSortFunc)xaccSplitCompareOtherAccountCodes, nullptr);
2412 : :
2413 : 83 : return qof_object_register (&split_object_def);
2414 : : }
2415 : :
2416 : : SplitTestFunctions*
2417 : 31 : _utest_split_fill_functions (void)
2418 : : {
2419 : 31 : SplitTestFunctions *func = g_new (SplitTestFunctions, 1);
2420 : :
2421 : 31 : func->xaccSplitEqualCheckBal = xaccSplitEqualCheckBal;
2422 : 31 : func->get_currency_denom = get_currency_denom;
2423 : 31 : func->get_commodity_denom = get_commodity_denom;
2424 : 31 : func->get_corr_account_split = get_corr_account_split;
2425 : 31 : return func;
2426 : : }
2427 : :
2428 : : /************************ END OF ************************************\
2429 : : \************************* FILE *************************************/
|