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 : 141994 : G_DEFINE_TYPE(Split, gnc_split, QOF_TYPE_INSTANCE)
108 : :
109 : : static void
110 : 10116 : gnc_split_init(Split* split)
111 : : {
112 : : /* fill in some sane defaults */
113 : 10116 : split->acc = nullptr;
114 : 10116 : split->orig_acc = nullptr;
115 : 10116 : split->parent = nullptr;
116 : 10116 : split->lot = nullptr;
117 : :
118 : 10116 : split->action = CACHE_INSERT("");
119 : 10116 : split->memo = CACHE_INSERT("");
120 : 10116 : split->reconciled = NREC;
121 : 10116 : split->amount = gnc_numeric_zero();
122 : 10116 : split->value = gnc_numeric_zero();
123 : :
124 : 10116 : split->date_reconciled = 0;
125 : :
126 : 10116 : split->balance = gnc_numeric_zero();
127 : 10116 : split->cleared_balance = gnc_numeric_zero();
128 : 10116 : split->reconciled_balance = gnc_numeric_zero();
129 : 10116 : split->noclosing_balance = gnc_numeric_zero();
130 : :
131 : 10116 : split->adjusted_amount = gnc_numeric_zero();
132 : 10116 : split->split_type = nullptr;
133 : :
134 : 10116 : split->gains = GAINS_STATUS_UNKNOWN;
135 : 10116 : split->gains_split = nullptr;
136 : 10116 : }
137 : :
138 : : static void
139 : 15544 : gnc_split_dispose(GObject *splitp)
140 : : {
141 : 15544 : G_OBJECT_CLASS(gnc_split_parent_class)->dispose(splitp);
142 : 15544 : }
143 : :
144 : : static void
145 : 7818 : gnc_split_finalize(GObject* splitp)
146 : : {
147 : 7818 : G_OBJECT_CLASS(gnc_split_parent_class)->finalize(splitp);
148 : 7818 : }
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 : 747 : 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 : 747 : g_return_if_fail(GNC_IS_SPLIT(object));
165 : :
166 : 747 : split = GNC_SPLIT(object);
167 : 747 : 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 : 305 : case PROP_GAINS_SPLIT:
216 : 305 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
217 : 305 : break;
218 : 108 : case PROP_GAINS_SOURCE:
219 : 108 : qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
220 : 108 : 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 : 1108 : 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 : 1108 : g_return_if_fail(GNC_IS_SPLIT(object));
237 : :
238 : 1108 : split = GNC_SPLIT(object);
239 : 1108 : if (prop_id < PROP_RUNTIME_0 && split->parent != nullptr)
240 : 758 : g_assert (qof_instance_get_editlevel(split->parent));
241 : :
242 : 1108 : 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 : 6 : case PROP_GAINS_SPLIT:
293 : 6 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
294 : 6 : break;
295 : 6 : case PROP_GAINS_SOURCE:
296 : 6 : qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
297 : 6 : 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 : 7297 : xaccInitSplit(Split * split, QofBook *book)
500 : : {
501 : 7297 : qof_instance_init_data(&split->inst, GNC_ID_SPLIT, book);
502 : 7297 : }
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 : 7297 : xaccMallocSplit(QofBook *book)
540 : : {
541 : : Split *split;
542 : 7297 : g_return_val_if_fail (book, nullptr);
543 : :
544 : 7297 : split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
545 : 7297 : xaccInitSplit (split, book);
546 : :
547 : 7297 : 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 : 2793 : xaccDupeSplit (const Split *s)
561 : : {
562 : 2793 : 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 : 2793 : split->inst.e_type = nullptr;
569 : 2793 : qof_instance_copy_guid(split, s);
570 : 2793 : qof_instance_copy_book(split, s);
571 : :
572 : 2793 : split->parent = s->parent;
573 : 2793 : split->acc = s->acc;
574 : 2793 : split->orig_acc = s->orig_acc;
575 : 2793 : split->lot = s->lot;
576 : :
577 : 2793 : CACHE_REPLACE(split->memo, s->memo);
578 : 2793 : CACHE_REPLACE(split->action, s->action);
579 : :
580 : 2793 : qof_instance_copy_kvp (QOF_INSTANCE (split), QOF_INSTANCE (s));
581 : :
582 : 2793 : split->reconciled = s->reconciled;
583 : 2793 : split->date_reconciled = s->date_reconciled;
584 : :
585 : 2793 : split->value = s->value;
586 : 2793 : 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 : 2793 : 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 : 7725 : xaccFreeSplit (Split *split)
716 : : {
717 : 7725 : if (!split) return;
718 : :
719 : : /* Debug double-free's */
720 : 7725 : if (((char *) 1) == split->memo)
721 : : {
722 : 0 : PERR ("double-free %p", split);
723 : 0 : return;
724 : : }
725 : 7725 : CACHE_REMOVE(split->memo);
726 : 7725 : CACHE_REMOVE(split->action);
727 : :
728 : 7725 : 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 : 7725 : split->memo = (char *) 1;
753 : 7725 : split->action = nullptr;
754 : 7725 : split->reconciled = NREC;
755 : 7725 : split->amount = gnc_numeric_zero();
756 : 7725 : split->value = gnc_numeric_zero();
757 : 7725 : split->parent = nullptr;
758 : 7725 : split->lot = nullptr;
759 : 7725 : split->acc = nullptr;
760 : 7725 : split->orig_acc = nullptr;
761 : :
762 : 7725 : split->balance = gnc_numeric_zero();
763 : 7725 : split->cleared_balance = gnc_numeric_zero();
764 : 7725 : split->reconciled_balance = gnc_numeric_zero();
765 : 7725 : split->noclosing_balance = gnc_numeric_zero();
766 : 7725 : split->adjusted_amount = gnc_numeric_zero();
767 : 7725 : split->split_type = nullptr;
768 : :
769 : 7725 : split->date_reconciled = 0;
770 : 7725 : G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split));
771 : :
772 : 7725 : 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 : 7725 : g_object_unref(split);
781 : : }
782 : :
783 : 31513 : void mark_split (Split *s)
784 : : {
785 : 31513 : if (s->acc)
786 : 22047 : g_object_set(s->acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr);
787 : :
788 : : /* set dirty flag on lot too. */
789 : 31513 : if (s->lot)
790 : 225 : gnc_lot_set_closed_unknown(s->lot);
791 : 31513 : }
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 : 124910 : xaccSplitGetAccount (const Split *s)
957 : : {
958 : 124910 : return s ? s->acc : nullptr;
959 : : }
960 : :
961 : : void
962 : 7268 : xaccSplitSetAccount (Split *s, Account *acc)
963 : : {
964 : : Transaction *trans;
965 : :
966 : 7268 : g_return_if_fail(s && acc);
967 : 7268 : g_return_if_fail(qof_instance_books_equal(acc, s));
968 : :
969 : 7268 : trans = s->parent;
970 : 7268 : if (trans)
971 : 5155 : xaccTransBeginEdit(trans);
972 : :
973 : 7268 : s->acc = acc;
974 : 7268 : qof_instance_set_dirty(QOF_INSTANCE(s));
975 : :
976 : 7268 : if (trans)
977 : 5155 : 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 : 13109 : xaccSplitCommitEdit(Split *s)
989 : : {
990 : 13109 : Account *acc = nullptr;
991 : 13109 : Account *orig_acc = nullptr;
992 : :
993 : 13109 : g_return_if_fail(s);
994 : 13109 : if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
995 : 0 : return;
996 : :
997 : 13109 : orig_acc = s->orig_acc;
998 : :
999 : 13109 : if (GNC_IS_ACCOUNT(s->acc))
1000 : 13091 : acc = s->acc;
1001 : :
1002 : : /* Remove from lot (but only if it hasn't been moved to
1003 : : new lot already) */
1004 : 13109 : 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 : 13109 : 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 : 13109 : if (acc && (orig_acc != acc) && !qof_instance_get_destroying(s))
1018 : : {
1019 : 7086 : 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 : 7085 : 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 : 7086 : xaccSplitSetAmount(s, xaccSplitGetAmount(s));
1031 : : }
1032 : :
1033 : 13109 : if (s->parent != s->orig_parent)
1034 : : {
1035 : : //FIXME: find better event
1036 : 6973 : if (s->orig_parent)
1037 : 3 : qof_event_gen(&s->orig_parent->inst, QOF_EVENT_MODIFY,
1038 : : nullptr);
1039 : : }
1040 : 13109 : if (s->lot)
1041 : : {
1042 : : /* A change of value/amnt affects gains display, etc. */
1043 : 292 : 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 : 13109 : s->orig_acc = s->acc;
1050 : 13109 : s->orig_parent = s->parent;
1051 : 13109 : if (!qof_commit_edit_part2(QOF_INSTANCE(s), commit_err, nullptr, do_destroy))
1052 : 0 : return;
1053 : :
1054 : 13109 : if (acc)
1055 : : {
1056 : 13091 : g_object_set(acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr);
1057 : 13091 : 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 : 438 : xaccSplitDetermineGainStatus (Split *split)
1111 : : {
1112 : : Split *other;
1113 : 438 : GncGUID *guid = nullptr;
1114 : :
1115 : 438 : if (GAINS_STATUS_UNKNOWN != split->gains) return;
1116 : :
1117 : 305 : other = xaccSplitGetCapGainsSplit (split);
1118 : 305 : if (other)
1119 : : {
1120 : 0 : split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
1121 : 0 : split->gains_split = other;
1122 : 0 : return;
1123 : : }
1124 : :
1125 : 915 : if (auto v = qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE (split), {"gains-source"}))
1126 : 1 : guid = const_cast<GncGUID*>(*v);
1127 : :
1128 : 305 : if (!guid)
1129 : : {
1130 : : // CHECKME: We leave split->gains_split alone. Is that correct?
1131 : 304 : split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
1132 : : }
1133 : : else
1134 : : {
1135 : : QofCollection *col;
1136 : 1 : col = qof_book_get_collection (qof_instance_get_book(split),
1137 : : GNC_ID_SPLIT);
1138 : 1 : split->gains = GAINS_STATUS_GAINS;
1139 : 1 : other = (Split *) qof_collection_lookup_entity (col, guid);
1140 : 1 : split->gains_split = other;
1141 : : }
1142 : : }
1143 : :
1144 : : /********************************************************************\
1145 : : \********************************************************************/
1146 : :
1147 : : static inline int
1148 : 15062 : get_currency_denom(const Split * s)
1149 : : {
1150 : 15062 : if (!(s && s->parent && s->parent->common_currency))
1151 : : {
1152 : 2857 : return GNC_DENOM_AUTO;
1153 : : }
1154 : : else
1155 : : {
1156 : 12205 : return gnc_commodity_get_fraction (s->parent->common_currency);
1157 : : }
1158 : : }
1159 : :
1160 : : static inline int
1161 : 12844 : get_commodity_denom(const Split * s)
1162 : : {
1163 : 12844 : if (!(s && s->acc))
1164 : : {
1165 : 2 : return GNC_DENOM_AUTO;
1166 : : }
1167 : : else
1168 : : {
1169 : 12842 : return xaccAccountGetCommoditySCU(s->acc);
1170 : : }
1171 : : }
1172 : :
1173 : : /********************************************************************\
1174 : : \********************************************************************/
1175 : :
1176 : : void
1177 : 2 : xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt)
1178 : : {
1179 : 2 : if (!s) return;
1180 : 2 : ENTER (" ");
1181 : 2 : xaccTransBeginEdit (s->parent);
1182 : :
1183 : 2 : s->amount = gnc_numeric_convert(amt, get_commodity_denom(s),
1184 : : GNC_HOW_RND_ROUND_HALF_UP);
1185 : 2 : s->value = gnc_numeric_mul(s->amount, price,
1186 : 2 : get_currency_denom(s), GNC_HOW_RND_ROUND_HALF_UP);
1187 : :
1188 : 2 : SET_GAINS_A_VDIRTY(s);
1189 : 2 : mark_split (s);
1190 : 2 : qof_instance_set_dirty(QOF_INSTANCE(s));
1191 : 2 : xaccTransCommitEdit(s->parent);
1192 : 2 : LEAVE ("");
1193 : : }
1194 : :
1195 : : static void
1196 : 0 : qofSplitSetSharePrice (Split *split, gnc_numeric price)
1197 : : {
1198 : 0 : g_return_if_fail(split);
1199 : 0 : split->value = gnc_numeric_mul(xaccSplitGetAmount(split),
1200 : 0 : price, get_currency_denom(split),
1201 : : GNC_HOW_RND_ROUND_HALF_UP);
1202 : : }
1203 : :
1204 : : void
1205 : 64 : xaccSplitSetSharePrice (Split *s, gnc_numeric price)
1206 : : {
1207 : 64 : if (!s) return;
1208 : :
1209 : 64 : if (gnc_numeric_zero_p (price))
1210 : 0 : return;
1211 : :
1212 : 64 : ENTER (" ");
1213 : 64 : xaccTransBeginEdit (s->parent);
1214 : :
1215 : 64 : s->value = gnc_numeric_mul(xaccSplitGetAmount(s),
1216 : 64 : price, get_currency_denom(s),
1217 : : GNC_HOW_RND_ROUND_HALF_UP);
1218 : :
1219 : 64 : SET_GAINS_VDIRTY(s);
1220 : 64 : mark_split (s);
1221 : 64 : qof_instance_set_dirty(QOF_INSTANCE(s));
1222 : 64 : xaccTransCommitEdit(s->parent);
1223 : 64 : LEAVE ("");
1224 : : }
1225 : :
1226 : : static void
1227 : 0 : qofSplitSetAmount (Split *split, gnc_numeric amt)
1228 : : {
1229 : 0 : g_return_if_fail(split);
1230 : 0 : if (split->acc)
1231 : : {
1232 : 0 : split->amount = gnc_numeric_convert(amt,
1233 : 0 : get_commodity_denom(split), GNC_HOW_RND_ROUND_HALF_UP);
1234 : : }
1235 : : else
1236 : : {
1237 : 0 : split->amount = amt;
1238 : : }
1239 : : }
1240 : :
1241 : : /* The amount of the split in the _account's_ commodity. */
1242 : : void
1243 : 14515 : xaccSplitSetAmount (Split *s, gnc_numeric amt)
1244 : : {
1245 : 14515 : if (!s) return;
1246 : 14515 : g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK);
1247 : 14515 : ENTER ("(split=%p) old amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT
1248 : : " new amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s,
1249 : : s->amount.num, s->amount.denom, amt.num, amt.denom);
1250 : :
1251 : 14515 : xaccTransBeginEdit (s->parent);
1252 : 14515 : if (s->acc)
1253 : : {
1254 : 12686 : s->amount = gnc_numeric_convert(amt, get_commodity_denom(s),
1255 : : GNC_HOW_RND_ROUND_HALF_UP);
1256 : 12686 : g_assert (gnc_numeric_check (s->amount) == GNC_ERROR_OK);
1257 : : }
1258 : : else
1259 : 1829 : s->amount = amt;
1260 : 14515 : s->adjusted_amount = s->amount;
1261 : :
1262 : 14515 : SET_GAINS_ADIRTY(s);
1263 : 14515 : mark_split (s);
1264 : 14515 : qof_instance_set_dirty(QOF_INSTANCE(s));
1265 : 14515 : xaccTransCommitEdit(s->parent);
1266 : 14515 : LEAVE("");
1267 : : }
1268 : :
1269 : : static void
1270 : 0 : qofSplitSetValue (Split *split, gnc_numeric amt)
1271 : : {
1272 : 0 : g_return_if_fail(split);
1273 : 0 : split->value = gnc_numeric_convert(amt,
1274 : 0 : get_currency_denom(split), GNC_HOW_RND_ROUND_HALF_UP);
1275 : 0 : g_assert(gnc_numeric_check (split->value) != GNC_ERROR_OK);
1276 : : }
1277 : :
1278 : : /* The value of the split in the _transaction's_ currency. */
1279 : : void
1280 : 14833 : xaccSplitSetValue (Split *s, gnc_numeric amt)
1281 : : {
1282 : : gnc_numeric new_val;
1283 : 14833 : if (!s) return;
1284 : :
1285 : 14833 : g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK);
1286 : 14833 : ENTER ("(split=%p) old val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT
1287 : : " new val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s,
1288 : : s->value.num, s->value.denom, amt.num, amt.denom);
1289 : :
1290 : 14833 : xaccTransBeginEdit (s->parent);
1291 : 14833 : new_val = gnc_numeric_convert(amt, get_currency_denom(s),
1292 : : GNC_HOW_RND_ROUND_HALF_UP);
1293 : 29666 : if (gnc_numeric_check(new_val) == GNC_ERROR_OK &&
1294 : 14833 : !(gnc_numeric_zero_p (new_val) && !gnc_numeric_zero_p (amt)))
1295 : : {
1296 : 14827 : s->value = new_val;
1297 : : }
1298 : : else
1299 : : {
1300 : 6 : PERR("numeric error %s in converting the split value's denominator with amount %s and denom %d",
1301 : : gnc_numeric_errorCode_to_string(gnc_numeric_check(new_val)),
1302 : : gnc_num_dbg_to_string (amt), get_currency_denom(s));
1303 : : }
1304 : :
1305 : 14833 : SET_GAINS_VDIRTY(s);
1306 : 14833 : mark_split (s);
1307 : 14833 : qof_instance_set_dirty(QOF_INSTANCE(s));
1308 : 14833 : xaccTransCommitEdit(s->parent);
1309 : 14833 : LEAVE ("");
1310 : : }
1311 : :
1312 : : /********************************************************************\
1313 : : \********************************************************************/
1314 : :
1315 : : gnc_numeric
1316 : 991 : xaccSplitGetBalance (const Split *s)
1317 : : {
1318 : 991 : return s ? s->balance : gnc_numeric_zero();
1319 : : }
1320 : :
1321 : : gnc_numeric
1322 : 3934 : xaccSplitGetNoclosingBalance (const Split *s)
1323 : : {
1324 : 3934 : return s ? s->noclosing_balance : gnc_numeric_zero();
1325 : : }
1326 : :
1327 : : gnc_numeric
1328 : 0 : xaccSplitGetClearedBalance (const Split *s)
1329 : : {
1330 : 0 : return s ? s->cleared_balance : gnc_numeric_zero();
1331 : : }
1332 : :
1333 : : gnc_numeric
1334 : 0 : xaccSplitGetReconciledBalance (const Split *s)
1335 : : {
1336 : 0 : return s ? s->reconciled_balance : gnc_numeric_zero();
1337 : : }
1338 : :
1339 : : gnc_numeric
1340 : 2240 : xaccSplitGetAdjustedAmount (const Split *s)
1341 : : {
1342 : 2240 : return s ? s->adjusted_amount : gnc_numeric_zero();
1343 : : }
1344 : :
1345 : : void
1346 : 1211 : xaccSplitSetAdjustedAmount (Split *s, gnc_numeric amt)
1347 : : {
1348 : 1211 : if (!s) return;
1349 : 1211 : if (gnc_numeric_check (amt)) return;
1350 : :
1351 : 1211 : s->adjusted_amount = amt;
1352 : : }
1353 : :
1354 : : void
1355 : 156 : xaccSplitSetBaseValue (Split *s, gnc_numeric value,
1356 : : const gnc_commodity * base_currency)
1357 : : {
1358 : : const gnc_commodity *currency;
1359 : : const gnc_commodity *commodity;
1360 : :
1361 : 156 : if (!s) return;
1362 : 156 : xaccTransBeginEdit (s->parent);
1363 : :
1364 : 156 : if (!s->acc)
1365 : : {
1366 : 1 : PERR ("split must have a parent account");
1367 : 1 : return;
1368 : : }
1369 : :
1370 : 155 : currency = xaccTransGetCurrency (s->parent);
1371 : 155 : commodity = xaccAccountGetCommodity (s->acc);
1372 : :
1373 : : /* If the base_currency is the transaction's commodity ('currency'),
1374 : : * set the value. If it's the account commodity, set the
1375 : : * amount. If both, set both. */
1376 : 155 : if (gnc_commodity_equiv(currency, base_currency))
1377 : : {
1378 : 153 : if (gnc_commodity_equiv(commodity, base_currency))
1379 : : {
1380 : 152 : s->amount = gnc_numeric_convert(value,
1381 : 152 : get_commodity_denom(s),
1382 : : GNC_HOW_RND_ROUND_HALF_UP);
1383 : : }
1384 : 153 : s->value = gnc_numeric_convert(value,
1385 : 153 : get_currency_denom(s),
1386 : : GNC_HOW_RND_ROUND_HALF_UP);
1387 : : }
1388 : 2 : else if (gnc_commodity_equiv(commodity, base_currency))
1389 : : {
1390 : 1 : s->amount = gnc_numeric_convert(value, get_commodity_denom(s),
1391 : : GNC_HOW_RND_ROUND_HALF_UP);
1392 : : }
1393 : : else
1394 : : {
1395 : 1 : PERR ("inappropriate base currency %s "
1396 : : "given split currency=%s and commodity=%s\n",
1397 : : gnc_commodity_get_printname(base_currency),
1398 : : gnc_commodity_get_printname(currency),
1399 : : gnc_commodity_get_printname(commodity));
1400 : 1 : return;
1401 : : }
1402 : :
1403 : 154 : SET_GAINS_A_VDIRTY(s);
1404 : 154 : mark_split (s);
1405 : 154 : qof_instance_set_dirty(QOF_INSTANCE(s));
1406 : 154 : xaccTransCommitEdit(s->parent);
1407 : : }
1408 : :
1409 : : gnc_numeric
1410 : 0 : xaccSplitGetBaseValue (const Split *s, const gnc_commodity * base_currency)
1411 : : {
1412 : 0 : if (!s || !s->acc || !s->parent) return gnc_numeric_zero();
1413 : :
1414 : : /* be more precise -- the value depends on the currency we want it
1415 : : * expressed in. */
1416 : 0 : if (gnc_commodity_equiv(xaccTransGetCurrency(s->parent), base_currency))
1417 : 0 : return xaccSplitGetValue(s);
1418 : 0 : if (gnc_commodity_equiv(xaccAccountGetCommodity(s->acc), base_currency))
1419 : 0 : return xaccSplitGetAmount(s);
1420 : :
1421 : 0 : PERR ("inappropriate base currency %s "
1422 : : "given split currency=%s and commodity=%s\n",
1423 : : gnc_commodity_get_printname(base_currency),
1424 : : gnc_commodity_get_printname(xaccTransGetCurrency (s->parent)),
1425 : : gnc_commodity_get_printname(xaccAccountGetCommodity(s->acc)));
1426 : 0 : return gnc_numeric_zero();
1427 : : }
1428 : :
1429 : : /********************************************************************\
1430 : : \********************************************************************/
1431 : :
1432 : : gnc_numeric
1433 : 154 : xaccSplitConvertAmount (const Split *split, const Account * account)
1434 : : {
1435 : : gnc_commodity *acc_com, *to_commodity;
1436 : : Transaction *txn;
1437 : : gnc_numeric amount, value, convrate;
1438 : : Account * split_acc;
1439 : :
1440 : 154 : amount = xaccSplitGetAmount (split);
1441 : :
1442 : : /* If this split is attached to this account, OR */
1443 : 154 : split_acc = xaccSplitGetAccount (split);
1444 : 154 : if (split_acc == account)
1445 : 105 : return amount;
1446 : :
1447 : : /* If split->account->commodity == to_commodity, return the amount */
1448 : 49 : acc_com = xaccAccountGetCommodity (split_acc);
1449 : 49 : to_commodity = xaccAccountGetCommodity (account);
1450 : 49 : if (acc_com && gnc_commodity_equal (acc_com, to_commodity))
1451 : 45 : return amount;
1452 : :
1453 : : /* Ok, this split is not for the viewed account, and the commodity
1454 : : * does not match. So we need to do some conversion.
1455 : : *
1456 : : * First, we can cheat. If this transaction is balanced and has
1457 : : * exactly two splits, then we can implicitly determine the exchange
1458 : : * rate and just return the 'other' split amount.
1459 : : */
1460 : 4 : txn = xaccSplitGetParent (split);
1461 : 4 : if (txn && xaccTransIsBalanced (txn))
1462 : : {
1463 : 2 : const Split *osplit = xaccSplitGetOtherSplit (split);
1464 : :
1465 : 2 : if (osplit)
1466 : : {
1467 : : gnc_commodity* split_comm =
1468 : 2 : xaccAccountGetCommodity(xaccSplitGetAccount(osplit));
1469 : 2 : if (!gnc_commodity_equal(to_commodity, split_comm))
1470 : : {
1471 : : gchar guidstr[GUID_ENCODING_LENGTH+1];
1472 : 1 : guid_to_string_buff(xaccSplitGetGUID(osplit),guidstr);
1473 : 1 : PERR("The split's (%s) amount can't be converted from %s into %s.",
1474 : : guidstr,
1475 : : gnc_commodity_get_mnemonic(split_comm),
1476 : : gnc_commodity_get_mnemonic(to_commodity)
1477 : : );
1478 : 1 : return gnc_numeric_zero();
1479 : : }
1480 : 1 : return gnc_numeric_neg (xaccSplitGetAmount (osplit));
1481 : : }
1482 : : }
1483 : :
1484 : : /* ... otherwise, we need to compute the amount from the conversion
1485 : : * rate into _this account_. So, find the split into this account,
1486 : : * compute the conversion rate (based on amount/value), and then multiply
1487 : : * this times the split value.
1488 : : */
1489 : 2 : value = xaccSplitGetValue (split);
1490 : :
1491 : 2 : if (gnc_numeric_zero_p (value))
1492 : : {
1493 : 1 : return value;
1494 : : }
1495 : :
1496 : 1 : convrate = xaccTransGetAccountConvRate(txn, account);
1497 : 1 : return gnc_numeric_mul (value, convrate,
1498 : 1 : gnc_commodity_get_fraction (to_commodity),
1499 : 1 : GNC_HOW_RND_ROUND_HALF_UP);
1500 : : }
1501 : :
1502 : : /********************************************************************\
1503 : : \********************************************************************/
1504 : :
1505 : : gboolean
1506 : 4954 : xaccSplitDestroy (Split *split)
1507 : : {
1508 : : Account *acc;
1509 : : Transaction *trans;
1510 : : GncEventData ed;
1511 : :
1512 : 4954 : if (!split) return TRUE;
1513 : :
1514 : 4954 : acc = split->acc;
1515 : 4954 : trans = split->parent;
1516 : 4954 : if (acc && !qof_instance_get_destroying(acc)
1517 : 4935 : && !qof_instance_get_destroying(trans)
1518 : 9908 : && xaccTransGetReadOnly(trans))
1519 : 0 : return FALSE;
1520 : :
1521 : 4954 : xaccTransBeginEdit(trans);
1522 : 4954 : ed.node = split;
1523 : 4954 : ed.idx = xaccTransGetSplitIndex(trans, split);
1524 : 4954 : qof_instance_set_dirty(QOF_INSTANCE(split));
1525 : 4954 : qof_instance_set_destroying(split, TRUE);
1526 : 4954 : qof_event_gen(&trans->inst, GNC_EVENT_ITEM_REMOVED, &ed);
1527 : 4954 : xaccTransCommitEdit(trans);
1528 : :
1529 : 4954 : return TRUE;
1530 : : }
1531 : :
1532 : : /********************************************************************\
1533 : : \********************************************************************/
1534 : :
1535 : : gint
1536 : 691425 : xaccSplitOrder (const Split *sa, const Split *sb)
1537 : : {
1538 : : int retval;
1539 : : int comp;
1540 : : const char *da, *db;
1541 : : gboolean action_for_num;
1542 : :
1543 : 691425 : if (sa == sb) return 0;
1544 : : /* nothing is always less than something */
1545 : 691424 : if (!sa) return -1;
1546 : 691423 : if (!sb) return +1;
1547 : :
1548 : : /* sort in transaction order, but use split action rather than trans num
1549 : : * according to book option */
1550 : : action_for_num = qof_book_use_split_action_for_num_field
1551 : 691422 : (xaccSplitGetBook (sa));
1552 : 691422 : if (action_for_num)
1553 : 2 : retval = xaccTransOrder_num_action (sa->parent, sa->action,
1554 : 2 : sb->parent, sb->action);
1555 : : else
1556 : 691420 : retval = xaccTransOrder (sa->parent, sb->parent);
1557 : 691422 : if (retval) return retval;
1558 : :
1559 : : /* otherwise, sort on memo strings */
1560 : 9142 : da = sa->memo ? sa->memo : "";
1561 : 9142 : db = sb->memo ? sb->memo : "";
1562 : 9142 : retval = g_utf8_collate (da, db);
1563 : 9142 : if (retval)
1564 : 192 : return retval;
1565 : :
1566 : : /* otherwise, sort on action strings */
1567 : 8950 : da = sa->action ? sa->action : "";
1568 : 8950 : db = sb->action ? sb->action : "";
1569 : 8950 : retval = g_utf8_collate (da, db);
1570 : 8950 : if (retval != 0)
1571 : 570 : return retval;
1572 : :
1573 : : /* the reconciled flag ... */
1574 : 8380 : if (sa->reconciled < sb->reconciled) return -1;
1575 : 8379 : if (sa->reconciled > sb->reconciled) return +1;
1576 : :
1577 : : /* compare amounts */
1578 : 8378 : comp = gnc_numeric_compare(xaccSplitGetAmount(sa), xaccSplitGetAmount (sb));
1579 : 8378 : if (comp < 0) return -1;
1580 : 4982 : if (comp > 0) return +1;
1581 : :
1582 : 343 : comp = gnc_numeric_compare(xaccSplitGetValue(sa), xaccSplitGetValue (sb));
1583 : 343 : if (comp < 0) return -1;
1584 : 340 : if (comp > 0) return +1;
1585 : :
1586 : : /* if dates differ, return */
1587 : 337 : if (sa->date_reconciled < sb->date_reconciled)
1588 : 1 : return -1;
1589 : 336 : else if (sa->date_reconciled > sb->date_reconciled)
1590 : 2 : return 1;
1591 : :
1592 : : /* else, sort on guid - keeps sort stable. */
1593 : 334 : retval = qof_instance_guid_compare(sa, sb);
1594 : 334 : if (retval) return retval;
1595 : :
1596 : 0 : return 0;
1597 : : }
1598 : :
1599 : : gint
1600 : 212 : xaccSplitOrderDateOnly (const Split *sa, const Split *sb)
1601 : : {
1602 : : Transaction *ta, *tb;
1603 : :
1604 : 212 : if (sa == sb) return 0;
1605 : : /* nothing is always less than something */
1606 : 211 : if (!sa) return -1;
1607 : 210 : if (!sb) return +1;
1608 : :
1609 : 209 : ta = sa->parent;
1610 : 209 : tb = sb->parent;
1611 : 209 : if ( !ta && !tb ) return 0;
1612 : 208 : if ( !tb ) return -1;
1613 : 207 : if ( !ta ) return +1;
1614 : :
1615 : 206 : if (ta->date_posted == tb->date_posted)
1616 : 107 : return -1; // Keep the same order
1617 : 99 : return (ta->date_posted > tb->date_posted) - (ta->date_posted < tb->date_posted);
1618 : : }
1619 : :
1620 : : static gboolean
1621 : 27 : get_corr_account_split(const Split *sa, const Split **retval)
1622 : : {
1623 : 27 : *retval = nullptr;
1624 : 27 : g_return_val_if_fail(sa, FALSE);
1625 : :
1626 : 26 : if (xaccTransCountSplits (sa->parent) > 2)
1627 : 2 : return FALSE;
1628 : :
1629 : 24 : *retval = xaccSplitGetOtherSplit (sa);
1630 : 24 : if (*retval)
1631 : 21 : return TRUE;
1632 : : else
1633 : 3 : return FALSE;
1634 : : }
1635 : :
1636 : : /* TODO: these static consts can be shared. */
1637 : : const char *
1638 : 0 : xaccSplitGetCorrAccountName(const Split *sa)
1639 : : {
1640 : : static const char *split_const = nullptr;
1641 : : const Split *other_split;
1642 : :
1643 : 0 : if (!get_corr_account_split(sa, &other_split))
1644 : : {
1645 : 0 : if (!split_const)
1646 : 0 : split_const = _("-- Split Transaction --");
1647 : :
1648 : 0 : return split_const;
1649 : : }
1650 : :
1651 : 0 : return xaccAccountGetName(other_split->acc);
1652 : : }
1653 : :
1654 : : char *
1655 : 18 : xaccSplitGetCorrAccountFullName(const Split *sa)
1656 : : {
1657 : : static const char *split_const = nullptr;
1658 : : const Split *other_split;
1659 : :
1660 : 18 : if (!get_corr_account_split(sa, &other_split))
1661 : : {
1662 : 1 : if (!split_const)
1663 : 1 : split_const = _("-- Split Transaction --");
1664 : :
1665 : 2 : return g_strdup(split_const);
1666 : : }
1667 : 17 : return gnc_account_get_full_name(other_split->acc);
1668 : : }
1669 : :
1670 : : const char *
1671 : 4 : xaccSplitGetCorrAccountCode(const Split *sa)
1672 : : {
1673 : : static const char *split_const = nullptr;
1674 : : const Split *other_split;
1675 : :
1676 : 4 : if (!get_corr_account_split(sa, &other_split))
1677 : : {
1678 : 1 : if (!split_const)
1679 : 1 : split_const = C_("Displayed account code of the other account in a multi-split transaction", "Split");
1680 : :
1681 : 1 : return split_const;
1682 : : }
1683 : 3 : return xaccAccountGetCode(other_split->acc);
1684 : : }
1685 : :
1686 : : /* TODO: It's not too hard to make this function avoid the malloc/free. */
1687 : : int
1688 : 1541 : xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb)
1689 : : {
1690 : : Account *aa, *ab;
1691 : 1541 : if (sa == sb) return 0;
1692 : 1540 : if (!sa) return -1;
1693 : 1539 : if (!sb) return 1;
1694 : :
1695 : 1538 : aa = sa->acc;
1696 : 1538 : ab = sb->acc;
1697 : 1538 : if (aa == ab) return 0;
1698 : :
1699 : 97 : auto path_a = gnc_account_get_all_parents (aa);
1700 : 97 : auto path_b = gnc_account_get_all_parents (ab);
1701 : 97 : auto mismatch_pair = std::mismatch (path_a.rbegin(), path_a.rend(),
1702 : 194 : path_b.rbegin(), path_b.rend());
1703 : :
1704 : 194 : return mismatch_pair.first == path_a.rend() ? -1
1705 : 97 : : mismatch_pair.second == path_b.rend() ? 1
1706 : 97 : : g_utf8_collate (xaccAccountGetName (*mismatch_pair.first),
1707 : 97 : xaccAccountGetName (*mismatch_pair.second));
1708 : 0 : }
1709 : :
1710 : :
1711 : : int
1712 : 4 : xaccSplitCompareAccountCodes(const Split *sa, const Split *sb)
1713 : : {
1714 : : Account *aa, *ab;
1715 : 4 : if (!sa && !sb) return 0;
1716 : 3 : if (!sa) return -1;
1717 : 2 : if (!sb) return 1;
1718 : :
1719 : 1 : aa = sa->acc;
1720 : 1 : ab = sb->acc;
1721 : :
1722 : 1 : return g_strcmp0(xaccAccountGetCode(aa), xaccAccountGetCode(ab));
1723 : : }
1724 : :
1725 : : int
1726 : 11 : xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb)
1727 : : {
1728 : : char *ca, *cb;
1729 : : int retval;
1730 : 11 : if (!sa && !sb) return 0;
1731 : 10 : if (!sa) return -1;
1732 : 9 : if (!sb) return 1;
1733 : :
1734 : : /* doesn't matter what separator we use
1735 : : * as long as they are the same
1736 : : */
1737 : :
1738 : 8 : ca = xaccSplitGetCorrAccountFullName(sa);
1739 : 8 : cb = xaccSplitGetCorrAccountFullName(sb);
1740 : 8 : retval = g_strcmp0(ca, cb);
1741 : 8 : g_free(ca);
1742 : 8 : g_free(cb);
1743 : 8 : return retval;
1744 : : }
1745 : :
1746 : : int
1747 : 4 : xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb)
1748 : : {
1749 : : const char *ca, *cb;
1750 : 4 : if (!sa && !sb) return 0;
1751 : 3 : if (!sa) return -1;
1752 : 2 : if (!sb) return 1;
1753 : :
1754 : 1 : ca = xaccSplitGetCorrAccountCode(sa);
1755 : 1 : cb = xaccSplitGetCorrAccountCode(sb);
1756 : 1 : return g_strcmp0(ca, cb);
1757 : : }
1758 : :
1759 : : static void
1760 : 0 : qofSplitSetMemo (Split *split, const char* memo)
1761 : : {
1762 : 0 : g_return_if_fail(split);
1763 : 0 : CACHE_REPLACE(split->memo, memo);
1764 : : }
1765 : :
1766 : : void
1767 : 1883 : xaccSplitSetMemo (Split *split, const char *memo)
1768 : : {
1769 : 1883 : if (!split || !memo) return;
1770 : 1782 : xaccTransBeginEdit (split->parent);
1771 : :
1772 : 1782 : CACHE_REPLACE(split->memo, memo);
1773 : 1782 : qof_instance_set_dirty(QOF_INSTANCE(split));
1774 : 1782 : xaccTransCommitEdit(split->parent);
1775 : :
1776 : : }
1777 : :
1778 : : static void
1779 : 0 : qofSplitSetAction (Split *split, const char *actn)
1780 : : {
1781 : 0 : g_return_if_fail(split);
1782 : 0 : CACHE_REPLACE(split->action, actn);
1783 : : }
1784 : :
1785 : : void
1786 : 1110 : xaccSplitSetAction (Split *split, const char *actn)
1787 : : {
1788 : 1110 : if (!split || !actn) return;
1789 : 1110 : xaccTransBeginEdit (split->parent);
1790 : :
1791 : 1110 : CACHE_REPLACE(split->action, actn);
1792 : 1110 : qof_instance_set_dirty(QOF_INSTANCE(split));
1793 : 1110 : xaccTransCommitEdit(split->parent);
1794 : :
1795 : : }
1796 : :
1797 : : static void
1798 : 0 : qofSplitSetReconcile (Split *split, char recn)
1799 : : {
1800 : 0 : g_return_if_fail(split);
1801 : 0 : switch (recn)
1802 : : {
1803 : 0 : case NREC:
1804 : : case CREC:
1805 : : case YREC:
1806 : : case FREC:
1807 : : case VREC:
1808 : 0 : split->reconciled = recn;
1809 : 0 : mark_split (split);
1810 : 0 : xaccAccountRecomputeBalance (split->acc);
1811 : 0 : break;
1812 : 0 : default:
1813 : 0 : PERR("Bad reconciled flag");
1814 : 0 : break;
1815 : : }
1816 : : }
1817 : :
1818 : : void
1819 : 2932 : xaccSplitSetReconcile (Split *split, char recn)
1820 : : {
1821 : 2932 : if (!split || split->reconciled == recn) return;
1822 : 1143 : xaccTransBeginEdit (split->parent);
1823 : :
1824 : 1143 : switch (recn)
1825 : : {
1826 : 1143 : case NREC:
1827 : : case CREC:
1828 : : case YREC:
1829 : : case FREC:
1830 : : case VREC:
1831 : 1143 : split->reconciled = recn;
1832 : 1143 : mark_split (split);
1833 : 1143 : qof_instance_set_dirty(QOF_INSTANCE(split));
1834 : 1143 : xaccAccountRecomputeBalance (split->acc);
1835 : 1143 : break;
1836 : 0 : default:
1837 : 0 : PERR("Bad reconciled flag");
1838 : 0 : break;
1839 : : }
1840 : 1143 : xaccTransCommitEdit(split->parent);
1841 : :
1842 : : }
1843 : :
1844 : : void
1845 : 788 : xaccSplitSetDateReconciledSecs (Split *split, time64 secs)
1846 : : {
1847 : 788 : if (!split) return;
1848 : 788 : xaccTransBeginEdit (split->parent);
1849 : :
1850 : 788 : split->date_reconciled = secs;
1851 : 788 : qof_instance_set_dirty(QOF_INSTANCE(split));
1852 : 788 : xaccTransCommitEdit(split->parent);
1853 : :
1854 : : }
1855 : :
1856 : :
1857 : : /*################## Added for Reg2 #################*/
1858 : : time64
1859 : 161 : xaccSplitGetDateReconciled (const Split * split)
1860 : : {
1861 : 161 : return split ? split->date_reconciled : 0;
1862 : : }
1863 : : /*################## Added for Reg2 #################*/
1864 : :
1865 : : /********************************************************************\
1866 : : \********************************************************************/
1867 : :
1868 : : /* return the parent transaction of the split */
1869 : : Transaction *
1870 : 123519 : xaccSplitGetParent (const Split *split)
1871 : : {
1872 : 123519 : return split ? split->parent : nullptr;
1873 : : }
1874 : :
1875 : : void
1876 : 7180 : xaccSplitSetParent(Split *s, Transaction *t)
1877 : : {
1878 : : Transaction *old_trans;
1879 : : GncEventData ed;
1880 : :
1881 : 7182 : g_return_if_fail(s);
1882 : 7180 : if (s->parent == t) return;
1883 : :
1884 : 7178 : if (s->parent != s->orig_parent && s->orig_parent != t)
1885 : 1 : PERR("You may not add the split to more than one transaction"
1886 : : " during the BeginEdit/CommitEdit block.");
1887 : 7178 : xaccTransBeginEdit(t);
1888 : 7178 : old_trans = s->parent;
1889 : :
1890 : 7178 : xaccTransBeginEdit(old_trans);
1891 : :
1892 : 7178 : ed.node = s;
1893 : 7178 : if (old_trans)
1894 : : {
1895 : 12 : ed.idx = xaccTransGetSplitIndex(old_trans, s);
1896 : 12 : qof_event_gen(&old_trans->inst, GNC_EVENT_ITEM_REMOVED, &ed);
1897 : : }
1898 : 7178 : s->parent = t;
1899 : 7178 : qof_instance_set_dirty(QOF_INSTANCE(s));
1900 : :
1901 : 7178 : xaccTransCommitEdit(old_trans);
1902 : :
1903 : 7178 : if (t)
1904 : : {
1905 : : /* Convert split to new transaction's commodity denominator */
1906 : 7170 : xaccSplitSetValue(s, xaccSplitGetValue(s));
1907 : :
1908 : : /* add ourselves to the new transaction's list of pending splits. */
1909 : 7170 : if (nullptr == g_list_find(t->splits, s))
1910 : 7170 : t->splits = g_list_append(t->splits, s);
1911 : :
1912 : 7170 : ed.idx = -1; /* unused */
1913 : 7170 : qof_event_gen(&t->inst, GNC_EVENT_ITEM_ADDED, &ed);
1914 : : }
1915 : 7178 : xaccTransCommitEdit(t);
1916 : : }
1917 : :
1918 : :
1919 : : GNCLot *
1920 : 515 : xaccSplitGetLot (const Split *split)
1921 : : {
1922 : 515 : return split ? split->lot : nullptr;
1923 : : }
1924 : :
1925 : : void
1926 : 288 : xaccSplitSetLot(Split* split, GNCLot* lot)
1927 : : {
1928 : 288 : xaccTransBeginEdit (split->parent);
1929 : 288 : split->lot = lot;
1930 : 288 : qof_instance_set_dirty(QOF_INSTANCE(split));
1931 : 288 : xaccTransCommitEdit(split->parent);
1932 : 288 : }
1933 : :
1934 : : const char *
1935 : 2467 : xaccSplitGetMemo (const Split *split)
1936 : : {
1937 : 2467 : return split ? split->memo : nullptr;
1938 : : }
1939 : :
1940 : : void
1941 : 6 : xaccSplitSetOnlineID (Split *split, const char *id)
1942 : : {
1943 : 6 : if (!split) return;
1944 : : /* No xaccTransBeginEdit/CommitEdit here: the online_id is normally set
1945 : : * while many other parameters of a transaction are being changed, so
1946 : : * the caller wraps this in the parent transaction's edit. */
1947 : 6 : std::optional<const char*> val;
1948 : 6 : if (id && *id)
1949 : 3 : val = g_strdup (id);
1950 : 18 : qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(split), val, {split_online_id});
1951 : 6 : qof_instance_set_dirty (QOF_INSTANCE(split));
1952 : 18 : }
1953 : :
1954 : : const char *
1955 : 13 : xaccSplitGetOnlineID (const Split *split)
1956 : : {
1957 : 13 : if (!split) return nullptr;
1958 : 39 : auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(split), {split_online_id})};
1959 : 13 : return rv ? *rv : nullptr;
1960 : 39 : }
1961 : :
1962 : : gboolean
1963 : 6 : xaccSplitHasOnlineID (const Split *split)
1964 : : {
1965 : 6 : auto id = xaccSplitGetOnlineID (split);
1966 : 6 : return (id && *id);
1967 : : }
1968 : :
1969 : : const char *
1970 : 1426 : xaccSplitGetAction (const Split *split)
1971 : : {
1972 : 1426 : return split ? split->action : nullptr;
1973 : : }
1974 : :
1975 : : char
1976 : 33106 : xaccSplitGetReconcile (const Split *split)
1977 : : {
1978 : 33106 : return split ? split->reconciled : ' ';
1979 : : }
1980 : :
1981 : :
1982 : : gnc_numeric
1983 : 160901 : xaccSplitGetAmount (const Split * split)
1984 : : {
1985 : 160901 : return split ? split->amount : gnc_numeric_zero();
1986 : : }
1987 : :
1988 : : gnc_numeric
1989 : 65001 : xaccSplitGetValue (const Split * split)
1990 : : {
1991 : 65001 : return split ? split->value : gnc_numeric_zero();
1992 : : }
1993 : :
1994 : : gnc_numeric
1995 : 416 : xaccSplitGetSharePrice (const Split * split)
1996 : : {
1997 : : gnc_numeric amt, val, price;
1998 : 416 : if (!split) return gnc_numeric_create(0, 1);
1999 : :
2000 : :
2001 : : /* if amount == 0, return 0
2002 : : * otherwise return value/amount
2003 : : */
2004 : :
2005 : 415 : amt = xaccSplitGetAmount(split);
2006 : 415 : val = xaccSplitGetValue(split);
2007 : 415 : if (gnc_numeric_zero_p(amt))
2008 : 48 : return gnc_numeric_create(0, 1);
2009 : :
2010 : 367 : price = gnc_numeric_div(val, amt,
2011 : : GNC_DENOM_AUTO,
2012 : : GNC_HOW_RND_ROUND_HALF_UP);
2013 : :
2014 : : /* During random checks we can get some very weird prices. Let's
2015 : : * handle some overflow and other error conditions by returning
2016 : : * zero. But still print an error to let us know it happened.
2017 : : */
2018 : 367 : if (gnc_numeric_check(price))
2019 : : {
2020 : 2 : PERR("Computing share price failed (%d): [ %" G_GINT64_FORMAT " / %"
2021 : : G_GINT64_FORMAT " ] / [ %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " ]",
2022 : : gnc_numeric_check(price), val.num, val.denom, amt.num, amt.denom);
2023 : 2 : return gnc_numeric_create(0, 1);
2024 : : }
2025 : :
2026 : 365 : return price;
2027 : : }
2028 : :
2029 : : /********************************************************************\
2030 : : \********************************************************************/
2031 : :
2032 : : QofBook *
2033 : 709008 : xaccSplitGetBook (const Split *split)
2034 : : {
2035 : 709008 : return qof_instance_get_book(QOF_INSTANCE(split));
2036 : : }
2037 : :
2038 : : const char *
2039 : 98991 : xaccSplitGetType(Split *s)
2040 : : {
2041 : 98991 : if (!s) return nullptr;
2042 : :
2043 : 98991 : if (!s->split_type)
2044 : : {
2045 : 14100 : auto type{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(s), {"split-type"})};
2046 : :
2047 : 7050 : if (!type || !g_strcmp0 (*type, split_type_normal))
2048 : 7050 : s->split_type = split_type_normal;
2049 : 0 : else if (!g_strcmp0 (*type, split_type_stock_split))
2050 : 0 : s->split_type = split_type_stock_split;
2051 : : else
2052 : : {
2053 : 0 : PERR ("unexpected split-type %s, reset to normal.", *type);
2054 : 0 : s->split_type = split_type_normal;
2055 : : }
2056 : : }
2057 : 98991 : return s->split_type;
2058 : : }
2059 : :
2060 : : /* reconfigure a split to be a stock split - after this, you shouldn't
2061 : : mess with the value, just the amount. */
2062 : : void
2063 : 15 : xaccSplitMakeStockSplit(Split *s)
2064 : : {
2065 : 15 : xaccTransBeginEdit (s->parent);
2066 : :
2067 : 15 : s->value = gnc_numeric_zero();
2068 : 15 : s->split_type = split_type_stock_split;
2069 : 45 : qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(s), g_strdup(split_type_stock_split),
2070 : : {"split-type"});
2071 : 15 : SET_GAINS_VDIRTY(s);
2072 : 15 : mark_split(s);
2073 : 15 : qof_instance_set_dirty(QOF_INSTANCE(s));
2074 : 15 : xaccTransCommitEdit(s->parent);
2075 : 15 : }
2076 : :
2077 : : gboolean
2078 : 98977 : xaccSplitIsStockSplit (Split *s)
2079 : : {
2080 : 98977 : return g_strcmp0(split_type_stock_split, xaccSplitGetType(s)) == 0;
2081 : : }
2082 : :
2083 : : void
2084 : 8 : xaccSplitAddPeerSplit (Split *split, const Split *other_split,
2085 : : time64 timestamp)
2086 : : {
2087 : : const GncGUID* guid;
2088 : :
2089 : 8 : g_return_if_fail (split != nullptr);
2090 : 8 : g_return_if_fail (other_split != nullptr);
2091 : :
2092 : 8 : guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
2093 : 8 : xaccTransBeginEdit (split->parent);
2094 : 8 : qof_instance_kvp_add_guid (QOF_INSTANCE (split), "lot-split",
2095 : 8 : gnc_time(nullptr), "peer_guid", guid_copy(guid));
2096 : 8 : mark_split (split);
2097 : 8 : qof_instance_set_dirty (QOF_INSTANCE (split));
2098 : 8 : xaccTransCommitEdit (split->parent);
2099 : : }
2100 : :
2101 : : gboolean
2102 : 114 : xaccSplitHasPeers (const Split *split)
2103 : : {
2104 : 114 : return qof_instance_has_slot (QOF_INSTANCE (split), "lot-split");
2105 : : }
2106 : :
2107 : : gboolean
2108 : 1 : xaccSplitIsPeerSplit (const Split *split, const Split *other_split)
2109 : : {
2110 : : const GncGUID* guid;
2111 : :
2112 : 1 : g_return_val_if_fail (split != nullptr, FALSE);
2113 : 1 : g_return_val_if_fail (other_split != nullptr, FALSE);
2114 : :
2115 : 1 : guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
2116 : 1 : return qof_instance_kvp_has_guid (QOF_INSTANCE (split), "lot-split",
2117 : 1 : "peer_guid", guid);
2118 : : }
2119 : :
2120 : : void
2121 : 0 : xaccSplitRemovePeerSplit (Split *split, const Split *other_split)
2122 : : {
2123 : : const GncGUID* guid;
2124 : :
2125 : 0 : g_return_if_fail (split != nullptr);
2126 : 0 : g_return_if_fail (other_split != nullptr);
2127 : :
2128 : 0 : guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
2129 : 0 : xaccTransBeginEdit (split->parent);
2130 : 0 : qof_instance_kvp_remove_guid (QOF_INSTANCE (split), "lot-split",
2131 : : "peer_guid", guid);
2132 : 0 : mark_split (split);
2133 : 0 : qof_instance_set_dirty (QOF_INSTANCE (split));
2134 : 0 : xaccTransCommitEdit (split->parent);
2135 : : }
2136 : :
2137 : : void
2138 : 0 : xaccSplitMergePeerSplits (Split *split, const Split *other_split)
2139 : : {
2140 : 0 : xaccTransBeginEdit (split->parent);
2141 : 0 : qof_instance_kvp_merge_guids (QOF_INSTANCE (split),
2142 : 0 : QOF_INSTANCE (other_split), "lot-split");
2143 : 0 : mark_split (split);
2144 : 0 : qof_instance_set_dirty (QOF_INSTANCE (split));
2145 : 0 : xaccTransCommitEdit (split->parent);
2146 : 0 : }
2147 : :
2148 : : /********************************************************************\
2149 : : \********************************************************************/
2150 : : /* In the old world, the 'other split' was the other split of a
2151 : : * transaction that contained only two splits. In the new world,
2152 : : * a split may have been cut up between multiple lots, although
2153 : : * in a conceptual sense, if lots hadn't been used, there would be
2154 : : * only a pair. So we handle this conceptual case: we can still
2155 : : * identify, unambiguously, the 'other' split when 'this' split
2156 : : * as been cut up across lots. We do this by looking for the
2157 : : * 'lot-split' keyword, which occurs only in cut-up splits.
2158 : : */
2159 : :
2160 : : Split *
2161 : 219 : xaccSplitGetOtherSplit (const Split *split)
2162 : : {
2163 : : Transaction *trans;
2164 : 219 : Split *other = nullptr;
2165 : :
2166 : 219 : if (!split) return nullptr;
2167 : 218 : trans = split->parent;
2168 : 218 : if (!trans) return nullptr;
2169 : :
2170 : 634 : for (GList *n = xaccTransGetSplitList (trans); n; n = n->next)
2171 : : {
2172 : 456 : Split *s = GNC_SPLIT(n->data);
2173 : 914 : if ((s == split) ||
2174 : 254 : (!xaccTransStillHasSplit(trans, s)) ||
2175 : 963 : (xaccAccountGetType (xaccSplitGetAccount (s)) == ACCT_TYPE_TRADING) ||
2176 : 253 : (qof_instance_has_slot (QOF_INSTANCE (s), "lot-split")))
2177 : 204 : continue;
2178 : :
2179 : 252 : if (other)
2180 : 39 : return nullptr;
2181 : :
2182 : 213 : other = s;
2183 : : }
2184 : 178 : return other;
2185 : : }
2186 : :
2187 : : /********************************************************************\
2188 : : \********************************************************************/
2189 : :
2190 : : gnc_numeric
2191 : 65 : xaccSplitVoidFormerAmount(const Split *split)
2192 : : {
2193 : 65 : g_return_val_if_fail(split, gnc_numeric_zero());
2194 : 195 : auto num{qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {void_former_amt_str})};
2195 : 65 : return num ? *num : gnc_numeric_zero();
2196 : 195 : }
2197 : :
2198 : : gnc_numeric
2199 : 12 : xaccSplitVoidFormerValue(const Split *split)
2200 : : {
2201 : 12 : g_return_val_if_fail(split, gnc_numeric_zero());
2202 : 36 : auto num{qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {void_former_val_str})};
2203 : 12 : return num ? *num : gnc_numeric_zero();
2204 : 36 : }
2205 : :
2206 : : void
2207 : 205 : xaccSplitVoid(Split *split)
2208 : : {
2209 : 205 : g_return_if_fail (GNC_IS_SPLIT(split));
2210 : 615 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), xaccSplitGetAmount(split), {void_former_amt_str});
2211 : 615 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), xaccSplitGetValue(split), {void_former_val_str});
2212 : 205 : qof_instance_set_dirty (QOF_INSTANCE(split));
2213 : :
2214 : 205 : static gnc_numeric zero = gnc_numeric_zero();
2215 : 205 : xaccSplitSetAmount (split, zero);
2216 : 205 : xaccSplitSetValue (split, zero);
2217 : 205 : xaccSplitSetReconcile(split, VREC);
2218 : 1025 : }
2219 : :
2220 : : void
2221 : 7 : xaccSplitUnvoid(Split *split)
2222 : : {
2223 : 7 : g_return_if_fail (GNC_IS_SPLIT(split));
2224 : 7 : xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split));
2225 : 7 : xaccSplitSetValue (split, xaccSplitVoidFormerValue(split));
2226 : 7 : xaccSplitSetReconcile(split, NREC);
2227 : 21 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {}, {void_former_amt_str});
2228 : 21 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {}, {void_former_val_str});
2229 : 7 : qof_instance_set_dirty (QOF_INSTANCE(split));
2230 : 35 : }
2231 : :
2232 : : /********************************************************************\
2233 : : \********************************************************************/
2234 : : /* QofObject function implementation */
2235 : :
2236 : : /* Hook into the QofObject registry */
2237 : :
2238 : : #ifdef _MSC_VER
2239 : : /* MSVC compiler doesn't have C99 "designated initializers"
2240 : : * so we wrap them in a macro that is empty on MSVC. */
2241 : : # define DI(x) /* */
2242 : : #else
2243 : : # define DI(x) x
2244 : : #endif
2245 : : static QofObject split_object_def =
2246 : : {
2247 : : DI(.interface_version = ) QOF_OBJECT_VERSION,
2248 : : DI(.e_type = ) GNC_ID_SPLIT,
2249 : : DI(.type_label = ) "Split",
2250 : : DI(.create = ) (void* (*)(QofBook*))xaccMallocSplit,
2251 : : DI(.book_begin = ) nullptr,
2252 : : DI(.book_end = ) nullptr,
2253 : : DI(.is_dirty = ) qof_collection_is_dirty,
2254 : : DI(.mark_clean = ) qof_collection_mark_clean,
2255 : : DI(.foreach = ) qof_collection_foreach,
2256 : : DI(.printable = ) (const char * (*)(gpointer)) xaccSplitGetMemo,
2257 : : DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
2258 : : };
2259 : :
2260 : : static gpointer
2261 : 613 : split_account_guid_getter (gpointer obj, const QofParam *p)
2262 : : {
2263 : 613 : Split *s = GNC_SPLIT(obj);
2264 : : Account *acc;
2265 : :
2266 : 613 : if (!s) return nullptr;
2267 : 613 : acc = xaccSplitGetAccount (s);
2268 : 613 : if (!acc) return nullptr;
2269 : 613 : return ((gpointer)xaccAccountGetGUID (acc));
2270 : : }
2271 : :
2272 : : static double /* internal use only */
2273 : 0 : DxaccSplitGetShareAmount (const Split * split)
2274 : : {
2275 : 0 : return split ? gnc_numeric_to_double(xaccSplitGetAmount(split)) : 0.0;
2276 : : }
2277 : :
2278 : : static gpointer
2279 : 3088 : no_op (gpointer obj, const QofParam *p)
2280 : : {
2281 : 3088 : return obj;
2282 : : }
2283 : :
2284 : : static void
2285 : 0 : qofSplitSetParentTrans(Split *s, QofInstance *ent)
2286 : : {
2287 : 0 : Transaction *trans = (Transaction*)ent;
2288 : :
2289 : 0 : g_return_if_fail(trans);
2290 : 0 : xaccSplitSetParent(s, trans);
2291 : : }
2292 : :
2293 : : static void
2294 : 0 : qofSplitSetAccount(Split *s, QofInstance *ent)
2295 : : {
2296 : 0 : Account *acc = (Account*)ent;
2297 : :
2298 : 0 : g_return_if_fail(acc);
2299 : 0 : xaccSplitSetAccount(s, acc);
2300 : : }
2301 : :
2302 : 83 : gboolean xaccSplitRegister (void)
2303 : : {
2304 : : static const QofParam params[] =
2305 : : {
2306 : : {
2307 : : SPLIT_DATE_RECONCILED, QOF_TYPE_DATE,
2308 : : (QofAccessFunc)xaccSplitGetDateReconciled,
2309 : : (QofSetterFunc)xaccSplitSetDateReconciledSecs
2310 : : },
2311 : :
2312 : : /* d-* are deprecated query params, should not be used in new
2313 : : * queries, should be removed from old queries. */
2314 : : {
2315 : : "d-share-amount", QOF_TYPE_DOUBLE,
2316 : : (QofAccessFunc)DxaccSplitGetShareAmount, nullptr
2317 : : },
2318 : : {
2319 : : "d-share-int64", QOF_TYPE_INT64,
2320 : : (QofAccessFunc)qof_entity_get_guid, nullptr
2321 : : },
2322 : : {
2323 : : SPLIT_BALANCE, QOF_TYPE_NUMERIC,
2324 : : (QofAccessFunc)xaccSplitGetBalance, nullptr
2325 : : },
2326 : : {
2327 : : SPLIT_CLEARED_BALANCE, QOF_TYPE_NUMERIC,
2328 : : (QofAccessFunc)xaccSplitGetClearedBalance, nullptr
2329 : : },
2330 : : {
2331 : : SPLIT_RECONCILED_BALANCE, QOF_TYPE_NUMERIC,
2332 : : (QofAccessFunc)xaccSplitGetReconciledBalance, nullptr
2333 : : },
2334 : : {
2335 : : SPLIT_MEMO, QOF_TYPE_STRING,
2336 : : (QofAccessFunc)xaccSplitGetMemo, (QofSetterFunc)qofSplitSetMemo
2337 : : },
2338 : : {
2339 : : SPLIT_ACTION, QOF_TYPE_STRING,
2340 : : (QofAccessFunc)xaccSplitGetAction, (QofSetterFunc)qofSplitSetAction
2341 : : },
2342 : : {
2343 : : SPLIT_RECONCILE, QOF_TYPE_CHAR,
2344 : : (QofAccessFunc)xaccSplitGetReconcile,
2345 : : (QofSetterFunc)qofSplitSetReconcile
2346 : : },
2347 : : {
2348 : : SPLIT_AMOUNT, QOF_TYPE_NUMERIC,
2349 : : (QofAccessFunc)xaccSplitGetAmount, (QofSetterFunc)qofSplitSetAmount
2350 : : },
2351 : : {
2352 : : SPLIT_SHARE_PRICE, QOF_TYPE_NUMERIC,
2353 : : (QofAccessFunc)xaccSplitGetSharePrice,
2354 : : (QofSetterFunc)qofSplitSetSharePrice
2355 : : },
2356 : : {
2357 : : SPLIT_VALUE, QOF_TYPE_DEBCRED,
2358 : : (QofAccessFunc)xaccSplitGetValue, (QofSetterFunc)qofSplitSetValue
2359 : : },
2360 : : { SPLIT_TYPE, QOF_TYPE_STRING, (QofAccessFunc)xaccSplitGetType, nullptr },
2361 : : {
2362 : : SPLIT_VOIDED_AMOUNT, QOF_TYPE_NUMERIC,
2363 : : (QofAccessFunc)xaccSplitVoidFormerAmount, nullptr
2364 : : },
2365 : : {
2366 : : SPLIT_VOIDED_VALUE, QOF_TYPE_NUMERIC,
2367 : : (QofAccessFunc)xaccSplitVoidFormerValue, nullptr
2368 : : },
2369 : : { SPLIT_LOT, GNC_ID_LOT, (QofAccessFunc)xaccSplitGetLot, nullptr },
2370 : : {
2371 : : SPLIT_TRANS, GNC_ID_TRANS,
2372 : : (QofAccessFunc)xaccSplitGetParent,
2373 : : (QofSetterFunc)qofSplitSetParentTrans
2374 : : },
2375 : : {
2376 : : SPLIT_ACCOUNT, GNC_ID_ACCOUNT,
2377 : : (QofAccessFunc)xaccSplitGetAccount, (QofSetterFunc)qofSplitSetAccount
2378 : : },
2379 : : { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, nullptr },
2380 : : /* these are no-ops to register the parameter names (for sorting) but
2381 : : they return an allocated object which getters cannot do. */
2382 : : { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, nullptr },
2383 : : { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, nullptr },
2384 : : { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, nullptr },
2385 : : { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, nullptr },
2386 : : {
2387 : : QOF_PARAM_GUID, QOF_TYPE_GUID,
2388 : : (QofAccessFunc)qof_entity_get_guid, nullptr
2389 : : },
2390 : : { nullptr },
2391 : : };
2392 : :
2393 : 83 : qof_class_register (GNC_ID_SPLIT, (QofSortFunc)xaccSplitOrder, params);
2394 : 83 : qof_class_register (SPLIT_ACCT_FULLNAME,
2395 : : (QofSortFunc)xaccSplitCompareAccountFullNames, nullptr);
2396 : 83 : qof_class_register (SPLIT_CORR_ACCT_NAME,
2397 : : (QofSortFunc)xaccSplitCompareOtherAccountFullNames,
2398 : : nullptr);
2399 : 83 : qof_class_register (SPLIT_CORR_ACCT_CODE,
2400 : : (QofSortFunc)xaccSplitCompareOtherAccountCodes, nullptr);
2401 : :
2402 : 83 : return qof_object_register (&split_object_def);
2403 : : }
2404 : :
2405 : : SplitTestFunctions*
2406 : 31 : _utest_split_fill_functions (void)
2407 : : {
2408 : 31 : SplitTestFunctions *func = g_new (SplitTestFunctions, 1);
2409 : :
2410 : 31 : func->xaccSplitEqualCheckBal = xaccSplitEqualCheckBal;
2411 : 31 : func->get_currency_denom = get_currency_denom;
2412 : 31 : func->get_commodity_denom = get_commodity_denom;
2413 : 31 : func->get_corr_account_split = get_corr_account_split;
2414 : 31 : return func;
2415 : : }
2416 : :
2417 : : /************************ END OF ************************************\
2418 : : \************************* FILE *************************************/
|