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