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