diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index 72d2410f0e..4932efb8e1 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -25,6 +25,7 @@ libgncmod_engine_la_SOURCES = \ Scrub.c \ Scrub2.c \ Scrub3.c \ + Split.c \ TransLog.c \ Transaction.c \ cap-gains.c \ @@ -62,6 +63,7 @@ gncinclude_HEADERS = \ Scrub.h \ Scrub2.h \ Scrub3.h \ + Split.h \ TransLog.h \ Transaction.h \ cap-gains.h \ @@ -89,6 +91,7 @@ noinst_HEADERS = \ QueryP.h \ SchedXactionP.h \ ScrubP.h \ + SplitP.h \ SX-book.h \ SX-ttinfo.h \ TransactionP.h \ diff --git a/src/engine/Split.c b/src/engine/Split.c new file mode 100644 index 0000000000..bd8b577718 --- /dev/null +++ b/src/engine/Split.c @@ -0,0 +1,1687 @@ +/********************************************************************\ + * Split.c -- split implementation * + * Copyright (C) 1997 Robin D. Clark * + * Copyright (C) 1997-2003 Linas Vepstas * + * Copyright (C) 2000 Bill Gribble * + * Copyright (c) 2006 David Hampton * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "Split.h" +#include "AccountP.h" +#include "Group.h" +#include "Scrub.h" +#include "Scrub3.h" +#include "TransactionP.h" +#include "TransLog.h" +#include "cap-gains.h" +#include "gnc-commodity.h" +#include "gnc-engine.h" +#include "gnc-lot-p.h" +#include "gnc-lot.h" + +const char *void_former_amt_str = "void-former-amount"; +const char *void_former_val_str = "void-former-value"; + +#define PRICE_SIGFIGS 6 + +/* This static indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_ENGINE; + +/********************************************************************\ + * xaccInitSplit + * Initialize a Split structure +\********************************************************************/ + +static void +xaccInitSplit(Split * split, QofBook *book) +{ + /* fill in some sane defaults */ + split->acc = NULL; + split->parent = NULL; + split->lot = NULL; + + split->action = gnc_string_cache_insert(""); + split->memo = gnc_string_cache_insert(""); + split->reconciled = NREC; + split->amount = gnc_numeric_zero(); + split->value = gnc_numeric_zero(); + + split->date_reconciled.tv_sec = 0; + split->date_reconciled.tv_nsec = 0; + + split->balance = gnc_numeric_zero(); + split->cleared_balance = gnc_numeric_zero(); + split->reconciled_balance = gnc_numeric_zero(); + + split->idata = 0; + + split->gains = GAINS_STATUS_UNKNOWN; + split->gains_split = NULL; + + qof_instance_init(&split->inst, GNC_ID_SPLIT, book); +} + +void +xaccSplitReinit(Split * split) +{ + /* fill in some sane defaults */ + split->acc = NULL; + split->parent = NULL; + split->lot = NULL; + + CACHE_REPLACE(split->action, ""); + CACHE_REPLACE(split->memo, ""); + split->reconciled = NREC; + split->amount = gnc_numeric_zero(); + split->value = gnc_numeric_zero(); + + split->date_reconciled.tv_sec = 0; + split->date_reconciled.tv_nsec = 0; + + split->balance = gnc_numeric_zero(); + split->cleared_balance = gnc_numeric_zero(); + split->reconciled_balance = gnc_numeric_zero(); + + if (split->inst.kvp_data) + kvp_frame_delete(split->inst.kvp_data); + split->inst.kvp_data = kvp_frame_new(); + split->idata = 0; + + split->gains = GAINS_STATUS_UNKNOWN; + split->gains_split = NULL; +} + +/********************************************************************\ +\********************************************************************/ + +Split * +xaccMallocSplit(QofBook *book) +{ + Split *split; + g_return_val_if_fail (book, NULL); + + split = g_new0 (Split, 1); + xaccInitSplit (split, book); + + return split; +} + +/********************************************************************\ +\********************************************************************/ +/* This routine is not exposed externally, since it does weird things, + * like not really setting up the parent account correctly, and ditto + * the parent transaction. This routine is prone to programmer error + * if not used correctly. It is used only by the edit-rollback code. + * Don't get duped! + */ + +Split * +xaccDupeSplit (const Split *s) +{ + Split *split = g_new0 (Split, 1); + + /* Trash the guid and entity table. We don't want to mistake + * the cloned splits as something official. If we ever use this + * split, we'll have to fix this up. + */ + split->inst.entity.e_type = NULL; + split->inst.entity.guid = *guid_null(); + split->inst.entity.collection = NULL; + split->inst.book = s->inst.book; + + split->parent = s->parent; + split->acc = s->acc; + split->lot = s->lot; + + split->memo = gnc_string_cache_insert(s->memo); + split->action = gnc_string_cache_insert(s->action); + + split->inst.kvp_data = kvp_frame_copy (s->inst.kvp_data); + + split->reconciled = s->reconciled; + split->date_reconciled = s->date_reconciled; + + split->value = s->value; + split->amount = s->amount; + + /* no need to futz with the balances; these get wiped each time ... + * split->balance = s->balance; + * split->cleared_balance = s->cleared_balance; + * split->reconciled_balance = s->reconciled_balance; + */ + + return split; +} + +Split * +xaccSplitClone (const Split *s) +{ + Split *split = g_new0 (Split, 1); + + split->parent = NULL; + split->memo = gnc_string_cache_insert(s->memo); + split->action = gnc_string_cache_insert(s->action); + split->reconciled = s->reconciled; + split->date_reconciled = s->date_reconciled; + split->value = s->value; + split->amount = s->amount; + split->balance = s->balance; + split->cleared_balance = s->cleared_balance; + split->reconciled_balance = s->reconciled_balance; + split->idata = 0; + + split->gains = GAINS_STATUS_UNKNOWN; + split->gains_split = NULL; + + qof_instance_init(&split->inst, GNC_ID_SPLIT, s->inst.book); + kvp_frame_delete(split->inst.kvp_data); + split->inst.kvp_data = kvp_frame_copy(s->inst.kvp_data); + + xaccAccountInsertSplit(s->acc, split); + if (s->lot) + { + /* FIXME: Doesn't look right. */ + s->lot->splits = g_list_append (s->lot->splits, split); + s->lot->is_closed = -1; + } + return split; +} + +#ifdef DUMP_FUNCTIONS +static void +xaccSplitDump (const Split *split, const char *tag) +{ + printf(" %s Split %p", tag, split); + printf(" GUID: %s\n", guid_to_string(&split->guid)); + printf(" Book: %p\n", split->inst.book); + printf(" Account: %p\n", split->acc); + printf(" Lot: %p\n", split->lot); + printf(" Parent: %p\n", split->parent); + printf(" Memo: %s\n", split->memo ? split->memo : "(null)"); + printf(" Action: %s\n", split->action ? split->action : "(null)"); + printf(" KVP Data: %p\n", split->inst.kvp_data); + printf(" Recncld: %c (date %s)\n", split->reconciled, + gnc_print_date(split->date_reconciled)); + printf(" Value: %s\n", gnc_numeric_to_string(split->value)); + printf(" Amount: %s\n", gnc_numeric_to_string(split->amount)); + printf(" Balance: %s\n", gnc_numeric_to_string(split->balance)); + printf(" CBalance: %s\n", gnc_numeric_to_string(split->cleared_balance)); + printf(" RBalance: %s\n", + gnc_numeric_to_string(split->reconciled_balance)); + printf(" idata: %x\n", split->idata); +} +#endif + +/********************************************************************\ +\********************************************************************/ + +void +xaccFreeSplit (Split *split) +{ + if (!split) return; + + /* Debug double-free's */ + if (((char *) 1) == split->memo) + { + PERR ("double-free %p", split); + return; + } + gnc_string_cache_remove(split->memo); + gnc_string_cache_remove(split->action); + + /* Just in case someone looks up freed memory ... */ + split->memo = (char *) 1; + split->action = NULL; + split->reconciled = NREC; + split->amount = gnc_numeric_zero(); + split->value = gnc_numeric_zero(); + split->parent = NULL; + split->lot = NULL; + split->acc = NULL; + + split->date_reconciled.tv_sec = 0; + split->date_reconciled.tv_nsec = 0; + + // Is this right? + if (split->gains_split) split->gains_split->gains_split = NULL; + qof_instance_release(&split->inst); + g_free(split); +} + +/* + * Helper routine for xaccSplitEqual. + */ +static gboolean +xaccSplitEqualCheckBal (const char *tag, gnc_numeric a, gnc_numeric b) +{ + char *str_a, *str_b; + + if (gnc_numeric_equal (a, b)) + return TRUE; + + str_a = gnc_numeric_to_string (a); + str_b = gnc_numeric_to_string (b); + + PWARN ("%sbalances differ: %s vs %s", tag, str_a, str_b); + + g_free (str_a); + g_free (str_b); + + return FALSE; +} + +/******************************************************************** + * xaccSplitEqual + ********************************************************************/ +gboolean +xaccSplitEqual(const Split *sa, const Split *sb, + gboolean check_guids, + gboolean check_balances, + gboolean check_txn_splits) +{ + if (!sa && !sb) return TRUE; /* Arguable. FALSE is better, methinks */ + + if (!sa || !sb) + { + PWARN ("one is NULL"); + return FALSE; + } + + if (sa == sb) return TRUE; + + if (check_guids) { + if (!guid_equal(&(sa->inst.entity.guid), &(sb->inst.entity.guid))) + { + PWARN ("GUIDs differ"); + return FALSE; + } + } + + /* Since these strings are cached we can just use pointer equality */ + if (sa->memo != sb->memo) + { + PWARN ("memos differ: (%p)%s vs (%p)%s", + sa->memo, sa->memo, sb->memo, sb->memo); + return FALSE; + } + + if (sa->action != sb->action) + { + PWARN ("actions differ: %s vs %s", sa->action, sb->action); + return FALSE; + } + + if (kvp_frame_compare(sa->inst.kvp_data, sb->inst.kvp_data) != 0) + { + char *frame_a; + char *frame_b; + + frame_a = kvp_frame_to_string (sa->inst.kvp_data); + frame_b = kvp_frame_to_string (sb->inst.kvp_data); + + PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b); + + g_free (frame_a); + g_free (frame_b); + + return FALSE; + } + + if (sa->reconciled != sb->reconciled) + { + PWARN ("reconcile flags differ: %c vs %c", sa->reconciled, sb->reconciled); + return FALSE; + } + + if (timespec_cmp(&(sa->date_reconciled), &(sb->date_reconciled))) + { + PWARN ("reconciled date differs"); + return FALSE; + } + + if (!gnc_numeric_eq(xaccSplitGetAmount (sa), xaccSplitGetAmount (sb))) + { + char *str_a; + char *str_b; + + str_a = gnc_numeric_to_string (xaccSplitGetAmount (sa)); + str_b = gnc_numeric_to_string (xaccSplitGetAmount (sb)); + + PWARN ("amounts differ: %s vs %s", str_a, str_b); + + g_free (str_a); + g_free (str_b); + + return FALSE; + } + + if (!gnc_numeric_eq(xaccSplitGetValue (sa), xaccSplitGetValue (sb))) + { + char *str_a; + char *str_b; + + str_a = gnc_numeric_to_string (xaccSplitGetValue (sa)); + str_b = gnc_numeric_to_string (xaccSplitGetValue (sb)); + + PWARN ("values differ: %s vs %s", str_a, str_b); + + g_free (str_a); + g_free (str_b); + + return FALSE; + } + + if (check_balances) { + if (!xaccSplitEqualCheckBal ("", sa->balance, sb->balance)) + return FALSE; + if (!xaccSplitEqualCheckBal ("cleared ", sa->cleared_balance, + sb->cleared_balance)) + return FALSE; + if (!xaccSplitEqualCheckBal ("reconciled ", sa->reconciled_balance, + sb->reconciled_balance)) + return FALSE; + } + + if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits, + check_balances, FALSE)) + { + PWARN ("transactions differ"); + return FALSE; + } + + return TRUE; +} + +static void +add_keys_to_list(gpointer key, gpointer val, gpointer list) +{ + *(GList **)list = g_list_prepend(*(GList **)list, key); +} + +GList * +xaccSplitListGetUniqueTransactions(const GList *splits) +{ + const GList *node; + GList *transList = NULL; + GHashTable *transHash = g_hash_table_new(g_direct_hash, g_direct_equal); + + for(node = splits; node; node = node->next) { + Transaction *trans = xaccSplitGetParent((Split *)(node->data)); + g_hash_table_insert(transHash, trans, trans); + } + g_hash_table_foreach(transHash, add_keys_to_list, &transList); + g_hash_table_destroy(transHash); + return transList; +} + +/******************************************************************** + * Account funcs + ********************************************************************/ + +Account * +xaccSplitGetAccount (const Split *s) +{ + return s ? s->acc : NULL; +} + +/********************************************************************\ +\********************************************************************/ + +Split * +xaccSplitLookup (const GUID *guid, QofBook *book) +{ + QofCollection *col; + if (!guid || !book) return NULL; + col = qof_book_get_collection (book, GNC_ID_SPLIT); + return (Split *) qof_collection_lookup_entity (col, guid); +} + +/********************************************************************\ +\********************************************************************/ +/* Routines for marking splits dirty, and for sending out change + * events. Note that we can't just mark-n-generate-event in one + * step, since sometimes we need to mark things up before its suitable + * to send out a change event. + */ + +/* CHECKME: This function modifies the Split without dirtying or + checking its parent. Is that correct? */ +void +xaccSplitDetermineGainStatus (Split *split) +{ + Split *other; + KvpValue *val; + + if (GAINS_STATUS_UNKNOWN != split->gains) return; + + other = xaccSplitGetCapGainsSplit (split); + if (other) + { + split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY; + split->gains_split = other; + return; + } + + val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source"); + if (!val) + { + // FIXME: This looks bogus. + other = xaccSplitGetOtherSplit (split); + if (other) + val = kvp_frame_get_slot (other->inst.kvp_data, "gains-source"); + split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY; + } else { + QofCollection *col; + col = qof_book_get_collection (split->inst.book, GNC_ID_SPLIT); + split->gains = GAINS_STATUS_GAINS; + other = (Split *) qof_collection_lookup_entity (col, + kvp_value_get_guid (val)); + split->gains_split = other; + } +} + +void mark_split (Split *s) +{ + Account *account = s->acc; + + if (account && !account->inst.do_free) + { + account->balance_dirty = TRUE; + account->sort_dirty = TRUE; + } + + /* set dirty flag on lot too. */ + if (s->lot) s->lot->is_closed = -1; +} + +void gen_event (const Split *split) +{ + Account *account = split->acc; + Transaction *trans = split->parent; + GNCLot *lot = split->lot; + + if (account) + { + xaccGroupMarkNotSaved (account->parent); + gnc_engine_gen_event (&account->inst.entity, GNC_EVENT_MODIFY); + } + + if (trans) + { + gnc_engine_gen_event (&trans->inst.entity, GNC_EVENT_MODIFY); + } + + if (lot) + { + /* A change of value/amnt affects gains displat, etc. */ + gnc_engine_gen_event (&lot->inst.entity, GNC_EVENT_MODIFY); + } +} + +/********************************************************************\ +\********************************************************************/ + +static inline int +get_currency_denom(const Split * s) +{ + if (!s) + { + return 0; + } + else if(!s->parent || !s->parent->common_currency) + { + return 100000; + } + else + { + return gnc_commodity_get_fraction (s->parent->common_currency); + } +} + +static inline int +get_commodity_denom(const Split * s) +{ + if (!s) + { + return 0; + } + else if (!s->acc) + { + return 100000; + } + else + { + return xaccAccountGetCommoditySCU(s->acc); + } +} + +/******************************************************************** + * xaccSplitGetSlots + ********************************************************************/ + +KvpFrame * +xaccSplitGetSlots (const Split * s) +{ + return qof_instance_get_slots(QOF_INSTANCE(s)); +} + +void +xaccSplitSetSlots_nc(Split *s, KvpFrame *frm) +{ + if (!s || !frm) return; + check_open (s->parent); + + qof_instance_set_slots(QOF_INSTANCE(s), frm); +} + +/********************************************************************\ +\********************************************************************/ + +void +DxaccSplitSetSharePriceAndAmount (Split *s, double price, double amt) +{ + if (!s) return; + ENTER (" "); + check_open (s->parent); + + s->amount = double_to_gnc_numeric(amt, get_commodity_denom(s), + GNC_HOW_RND_ROUND); + s->value = double_to_gnc_numeric(price * amt, get_currency_denom(s), + GNC_HOW_RND_ROUND); + + SET_GAINS_A_VDIRTY(s); + mark_split (s); +} + +void +xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt) +{ + if (!s) return; + ENTER (" "); + check_open (s->parent); + + s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), + GNC_HOW_RND_ROUND); + s->value = gnc_numeric_mul(s->amount, price, + get_currency_denom(s), GNC_HOW_RND_ROUND); + + SET_GAINS_A_VDIRTY(s); + mark_split (s); +} + +static void +qofSplitSetSharePrice (Split *split, gnc_numeric price) +{ + g_return_if_fail(split); + split->value = gnc_numeric_mul(xaccSplitGetAmount(split), + price, get_currency_denom(split), + GNC_HOW_RND_ROUND); +} + +void +xaccSplitSetSharePrice (Split *s, gnc_numeric price) +{ + if (!s) return; + ENTER (" "); + check_open (s->parent); + + s->value = gnc_numeric_mul(xaccSplitGetAmount(s), + price, get_currency_denom(s), + GNC_HOW_RND_ROUND); + + SET_GAINS_VDIRTY(s); + mark_split (s); +} + +void +DxaccSplitSetShareAmount (Split *s, double damt) +{ + gnc_numeric old_price, old_amt; + int commodity_denom = get_commodity_denom(s); + gnc_numeric amt = double_to_gnc_numeric(damt, commodity_denom, + GNC_HOW_RND_ROUND); + if (!s) return; + ENTER (" "); + check_open (s->parent); + + old_amt = xaccSplitGetAmount (s); + if (!gnc_numeric_zero_p(old_amt)) + { + old_price = gnc_numeric_div(xaccSplitGetValue (s), + old_amt, GNC_DENOM_AUTO, + GNC_HOW_DENOM_REDUCE); + } + else { + old_price = gnc_numeric_create(1, 1); + } + + s->amount = gnc_numeric_convert(amt, commodity_denom, + GNC_HOW_RND_NEVER); + s->value = gnc_numeric_mul(s->amount, old_price, + get_currency_denom(s), GNC_HOW_RND_ROUND); + + SET_GAINS_A_VDIRTY(s); + mark_split (s); +} + +static void +qofSplitSetAmount (Split *split, gnc_numeric amt) +{ + g_return_if_fail(split); + if (split->acc) + { + split->amount = gnc_numeric_convert(amt, + get_commodity_denom(split), GNC_HOW_RND_ROUND); + } + else { split->amount = amt; } +} + +/* The amount of the split in the _account's_ commodity. */ +void +xaccSplitSetAmount (Split *s, gnc_numeric amt) +{ + if (!s) return; + g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK); + ENTER ("(split=%p) old amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT + " new amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s, + s->amount.num, s->amount.denom, amt.num, amt.denom); + + check_open (s->parent); + if (s->acc) + s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), + GNC_HOW_RND_ROUND); + else + s->amount = amt; + + SET_GAINS_ADIRTY(s); + mark_split (s); + LEAVE(""); +} + +static void +qofSplitSetValue (Split *split, gnc_numeric amt) +{ + g_return_if_fail(split); + split->value = gnc_numeric_convert(amt, + get_currency_denom(split), GNC_HOW_RND_ROUND); +} + +/* The value of the split in the _transaction's_ currency. */ +void +xaccSplitSetValue (Split *s, gnc_numeric amt) +{ + if (!s) return; + + g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK); + ENTER ("(split=%p) old val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT + " new val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s, + s->value.num, s->value.denom, amt.num, amt.denom); + + check_open (s->parent); + s->value = gnc_numeric_convert(amt, get_currency_denom(s), + GNC_HOW_RND_ROUND); + + SET_GAINS_VDIRTY(s); + mark_split (s); + LEAVE (""); +} + +/********************************************************************\ +\********************************************************************/ + +gnc_numeric +xaccSplitGetBalance (const Split *s) +{ + return s ? s->balance : gnc_numeric_zero(); +} + +gnc_numeric +xaccSplitGetClearedBalance (const Split *s) +{ + return s ? s->cleared_balance : gnc_numeric_zero(); +} + +gnc_numeric +xaccSplitGetReconciledBalance (const Split *s) +{ + return s ? s->reconciled_balance : gnc_numeric_zero(); +} + +void +xaccSplitSetBaseValue (Split *s, gnc_numeric value, + const gnc_commodity * base_currency) +{ + const gnc_commodity *currency; + const gnc_commodity *commodity; + + if (!s) return; + check_open (s->parent); + + if (!s->acc) + { + PERR ("split must have a parent account"); + return; + } + + currency = xaccTransGetCurrency (s->parent); + commodity = xaccAccountGetCommodity (s->acc); + + /* If the base_currency is the transaction's commodity ('currency'), + * set the value. If it's the account commodity, set the + * amount. If both, set both. */ + if (gnc_commodity_equiv(currency, base_currency)) { + if (gnc_commodity_equiv(commodity, base_currency)) { + s->amount = gnc_numeric_convert(value, + get_commodity_denom(s), + GNC_HOW_RND_NEVER); + } + s->value = gnc_numeric_convert(value, + get_currency_denom(s), + GNC_HOW_RND_NEVER); + } + else if (gnc_commodity_equiv(commodity, base_currency)) { + s->amount = gnc_numeric_convert(value, get_commodity_denom(s), + GNC_HOW_RND_NEVER); + } + else { + PERR ("inappropriate base currency %s " + "given split currency=%s and commodity=%s\n", + gnc_commodity_get_printname(base_currency), + gnc_commodity_get_printname(currency), + gnc_commodity_get_printname(commodity)); + return; + } + + SET_GAINS_A_VDIRTY(s); + mark_split (s); +} + +gnc_numeric +xaccSplitGetBaseValue (const Split *s, const gnc_commodity * base_currency) +{ + if (!s || !s->acc || !s->parent) return gnc_numeric_zero(); + + /* be more precise -- the value depends on the currency we want it + * expressed in. */ + if (gnc_commodity_equiv(xaccTransGetCurrency(s->parent), base_currency)) + return xaccSplitGetValue(s); + if (gnc_commodity_equiv(xaccAccountGetCommodity(s->acc), base_currency)) + return xaccSplitGetAmount(s); + + PERR ("inappropriate base currency %s " + "given split currency=%s and commodity=%s\n", + gnc_commodity_get_printname(base_currency), + gnc_commodity_get_printname(xaccTransGetCurrency (s->parent)), + gnc_commodity_get_printname(xaccAccountGetCommodity(s->acc))); + return gnc_numeric_zero(); +} + +/********************************************************************\ +\********************************************************************/ + +gnc_numeric +xaccSplitsComputeValue (GList *splits, Split * skip_me, + const gnc_commodity * base_currency) +{ + GList *node; + gnc_numeric value = gnc_numeric_zero(); + + g_return_val_if_fail (base_currency, value); + + ENTER (" currency=%s", gnc_commodity_get_mnemonic (base_currency)); + + for (node = splits; node; node = node->next) + { + Split *s = node->data; + const gnc_commodity *currency; + const gnc_commodity *commodity; + + if (s == skip_me) continue; + + /* value = gnc_numeric_add(value, xaccSplitGetBaseValue(s, base_currency), + GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); */ + + /* The split-editor often sends us 'temp' splits whose account + * hasn't yet been set. Be lenient, and assume an implied base + * currency. If there's a problem later, the scrub routines will + * pick it up. + */ + commodity = s->acc ? xaccAccountGetCommodity (s->acc) : base_currency; + currency = xaccTransGetCurrency (s->parent); + + + if (gnc_commodity_equiv(currency, base_currency)) + { + value = gnc_numeric_add(value, xaccSplitGetValue(s), + GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); + } + else if (gnc_commodity_equiv(commodity, base_currency)) + { + value = gnc_numeric_add(value, xaccSplitGetAmount(s), + GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); + } + else { + PERR ("inconsistent currencies\n" + "\tbase = '%s', curr='%s', sec='%s'\n", + gnc_commodity_get_printname(base_currency), + gnc_commodity_get_printname(currency), + gnc_commodity_get_printname(commodity)); + g_return_val_if_fail (FALSE, value); + } + } + + /* Note that just because the currencies are equivalent + * doesn't mean the denominators are the same! */ + value = gnc_numeric_convert(value, + gnc_commodity_get_fraction (base_currency), + GNC_HOW_RND_ROUND); + + LEAVE (" total=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, + value.num, value.denom); + return value; +} + +gnc_numeric +xaccSplitConvertAmount (Split *split, Account * account) +{ + gnc_commodity *acc_com, *to_commodity; + Transaction *txn; + gnc_numeric amount, value, convrate; + Account * split_acc; + + amount = xaccSplitGetAmount (split); + + /* If this split is attached to this account, OR */ + split_acc = xaccSplitGetAccount (split); + if (split_acc == account) + return amount; + + /* If split->account->commodity == to_commodity, return the amount */ + acc_com = xaccAccountGetCommodity (split_acc); + to_commodity = xaccAccountGetCommodity (account); + if (acc_com && gnc_commodity_equal (acc_com, to_commodity)) + return amount; + + /* Ok, this split is not for the viewed account, and the commodity + * does not match. So we need to do some conversion. + * + * First, we can cheat. If this transaction is balanced and has + * exactly two splits, then we can implicitly determine the exchange + * rate and just return the 'other' split amount. + */ + txn = xaccSplitGetParent (split); + if (txn && gnc_numeric_zero_p (xaccTransGetImbalance (txn))) { + Split *osplit = xaccSplitGetOtherSplit (split); + + if (osplit) + return gnc_numeric_neg (xaccSplitGetAmount (osplit)); + } + + /* ... otherwise, we need to compute the amount from the conversion + * rate into _this account_. So, find the split into this account, + * compute the conversion rate (based on amount/value), and then multiply + * this times the split value. + */ + convrate = xaccTransGetAccountConvRate(txn, account); + value = xaccSplitGetValue (split); + return gnc_numeric_mul (value, convrate, + gnc_commodity_get_fraction (to_commodity), + GNC_RND_ROUND); +} + +/********************************************************************\ +\********************************************************************/ + +gboolean +xaccSplitDestroy (Split *split) +{ + Account *acc; + Transaction *trans; + + if (!split) return TRUE; + + acc = split->acc; + trans = split->parent; + if (acc && !acc->inst.do_free && xaccTransGetReadOnly (trans)) + return FALSE; + + check_open (trans); + + mark_split (split); + + if (trans) + { + if (g_list_find(trans->splits, split)) + xaccTransRemoveSplit (trans, split); + else + PERR ("split not in transaction"); + } + + /* Note: split is removed from lot when it's removed from account */ + xaccAccountRemoveSplit (acc, split); + + /* If we're shutting down then destroy the transaction, too, and + * don't recompute the balance. + */ + if (qof_book_shutting_down (split->parent->inst.book)) + /* This seems like an odd place to do this. Transactions have + to be opened to destroy them. */ + xaccTransDestroy (trans); + else + /* This seems a bit eager. Isn't there a lazy way to do this? */ + xaccAccountRecomputeBalance (acc); + + + gen_event (split); + xaccFreeSplit (split); + return TRUE; +} + +/********************************************************************\ +\********************************************************************/ + +int +xaccSplitDateOrder (const Split *sa, const Split *sb) +{ + int retval; + int comp; + char *da, *db; + + if (sa == sb) return 0; + /* nothing is always less than something */ + if (!sa && sb) return -1; + if (sa && !sb) return +1; + + retval = xaccTransOrder (sa->parent, sb->parent); + if (retval) return retval; + + /* otherwise, sort on memo strings */ + da = sa->memo; + db = sb->memo; + SAFE_STRCMP (da, db); + + /* otherwise, sort on action strings */ + da = sa->action; + db = sb->action; + SAFE_STRCMP (da, db); + + /* the reconciled flag ... */ + if (sa->reconciled < sb->reconciled) return -1; + if (sa->reconciled > sb->reconciled) return +1; + + /* compare amounts */ + comp = gnc_numeric_compare(xaccSplitGetAmount(sa), xaccSplitGetAmount (sb)); + if (comp < 0) return -1; + if (comp > 0) return +1; + + comp = gnc_numeric_compare(xaccSplitGetValue(sa), xaccSplitGetValue (sb)); + if (comp < 0) return -1; + if (comp > 0) return +1; + + /* if dates differ, return */ + DATE_CMP(sa,sb,date_reconciled); + + /* else, sort on guid - keeps sort stable. */ + retval = guid_compare(&(sa->inst.entity.guid), &(sb->inst.entity.guid)); + if (retval) return retval; + + return 0; +} + +static gboolean +get_corr_account_split(const Split *sa, Split **retval) +{ + + Split *current_split; + GList *node; + gnc_numeric sa_value, current_value; + gboolean sa_value_positive, current_value_positive, seen_different = FALSE; + + *retval = NULL; + g_return_val_if_fail(sa, TRUE); + + sa_value = xaccSplitGetValue (sa); + sa_value_positive = gnc_numeric_positive_p(sa_value); + + for (node = sa->parent->splits; node; node = node->next) + { + current_split = node->data; + if (current_split == sa) continue; + + current_value = xaccSplitGetValue (current_split); + current_value_positive = gnc_numeric_positive_p(current_value); + if ((sa_value_positive && !current_value_positive) || + (!sa_value_positive && current_value_positive)) { + if (seen_different) { + *retval = NULL; + return TRUE; + } else { + *retval = current_split; + seen_different = TRUE; + } + } + } + return FALSE; +} + +/* TODO: these static consts can be shared. */ +const char * +xaccSplitGetCorrAccountName(const Split *sa) +{ + static const char *split_const = NULL; + Split *other_split; + + if (get_corr_account_split(sa, &other_split)) + { + if (!split_const) + split_const = _("-- Split Transaction --"); + + return split_const; + } + + return xaccAccountGetName(other_split->acc); +} + +char * +xaccSplitGetCorrAccountFullName(const Split *sa, char separator) +{ + static const char *split_const = NULL; + Split *other_split; + + if (get_corr_account_split(sa, &other_split)) + { + if (!split_const) + split_const = _("-- Split Transaction --"); + + return g_strdup(split_const); + } + return xaccAccountGetFullName(other_split->acc, separator); +} + +const char * +xaccSplitGetCorrAccountCode(const Split *sa) +{ + static const char *split_const = NULL; + Split *other_split; + + if (get_corr_account_split(sa, &other_split)) + { + if (!split_const) + split_const = _("Split"); + + return split_const; + } + return xaccAccountGetCode(other_split->acc); +} + +/* TODO: It's not too hard to make this function avoid the malloc/free. */ +int +xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb) +{ + Account *aa, *ab; + char *full_a, *full_b; + int retval; + if (!sa && !sb) return 0; + if (!sa) return -1; + if (!sb) return 1; + + aa = sa->acc; + ab = sb->acc; + full_a = xaccAccountGetFullName(aa, ':'); + full_b = xaccAccountGetFullName(ab, ':'); + /* for comparison purposes it doesn't matter what we use as a separator */ + retval = safe_strcmp(full_a, full_b); + g_free(full_a); + g_free(full_b); + return retval; +} + + +int +xaccSplitCompareAccountCodes(const Split *sa, const Split *sb) +{ + Account *aa, *ab; + if (!sa && !sb) return 0; + if (!sa) return -1; + if (!sb) return 1; + + aa = sa->acc; + ab = sb->acc; + + return safe_strcmp(xaccAccountGetName(aa), xaccAccountGetName(ab)); +} + +int +xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb) +{ + char *ca, *cb; + int retval; + if (!sa && !sb) return 0; + if (!sa) return -1; + if (!sb) return 1; + + /* doesn't matter what separator we use + * as long as they are the same + */ + + ca = xaccSplitGetCorrAccountFullName(sa, ':'); + cb = xaccSplitGetCorrAccountFullName(sb, ':'); + retval = safe_strcmp(ca, cb); + g_free(ca); + g_free(cb); + return retval; +} + +int +xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb) +{ + const char *ca, *cb; + if (!sa && !sb) return 0; + if (!sa) return -1; + if (!sb) return 1; + + ca = xaccSplitGetCorrAccountCode(sa); + cb = xaccSplitGetCorrAccountCode(sb); + return safe_strcmp(ca, cb); +} + +static void +qofSplitSetMemo (Split *split, const char* memo) +{ + gchar *tmp; + + g_return_if_fail(split); + tmp = gnc_string_cache_insert((gpointer) memo); + gnc_string_cache_remove(split->memo); + split->memo = tmp; +} + +void +xaccSplitSetMemo (Split *split, const char *memo) +{ + char * tmp; + if (!split || !memo) return; + check_open (split->parent); + + tmp = gnc_string_cache_insert((gpointer) memo); + gnc_string_cache_remove(split->memo); + split->memo = tmp; +} + +static void +qofSplitSetAction (Split *split, const char *actn) +{ + g_return_if_fail(split); + split->action = g_strdup(actn); +} + +void +xaccSplitSetAction (Split *split, const char *actn) +{ + char * tmp; + if (!split || !actn) return; + check_open (split->parent); + + tmp = gnc_string_cache_insert((gpointer) actn); + gnc_string_cache_remove(split->action); + split->action = tmp; +} + +static void +qofSplitSetReconcile (Split *split, char recn) +{ + g_return_if_fail(split); + switch (recn) + { + case NREC: + case CREC: + case YREC: + case FREC: + case VREC: + split->reconciled = recn; + mark_split (split); + xaccAccountRecomputeBalance (split->acc); + break; + default: + PERR("Bad reconciled flag"); + } +} + +void +xaccSplitSetReconcile (Split *split, char recn) +{ + if (!split || split->reconciled == recn) return; + check_open (split->parent); + + switch (recn) + { + case NREC: + case CREC: + case YREC: + case FREC: + case VREC: + split->reconciled = recn; + mark_split (split); + xaccAccountRecomputeBalance (split->acc); + break; + default: + PERR("Bad reconciled flag"); + } +} + +void +xaccSplitSetDateReconciledSecs (Split *split, time_t secs) +{ + if (!split) return; + check_open (split->parent); + + split->date_reconciled.tv_sec = secs; + split->date_reconciled.tv_nsec = 0; +} + +void +xaccSplitSetDateReconciledTS (Split *split, Timespec *ts) +{ + if (!split || !ts) return; + check_open (split->parent); + + split->date_reconciled = *ts; +} + +void +xaccSplitGetDateReconciledTS (const Split * split, Timespec *ts) +{ + if (!split || !ts) return; + *ts = (split->date_reconciled); +} + +Timespec +xaccSplitRetDateReconciledTS (const Split * split) +{ + Timespec ts = {0,0}; + return split ? split->date_reconciled : ts; +} + +/********************************************************************\ +\********************************************************************/ + +/* return the parent transaction of the split */ +Transaction * +xaccSplitGetParent (const Split *split) +{ + return split ? split->parent : NULL; +} + +GNCLot * +xaccSplitGetLot (const Split *split) +{ + return split ? split->lot : NULL; +} + +const char * +xaccSplitGetMemo (const Split *split) +{ + return split ? split->memo : NULL; +} + +const char * +xaccSplitGetAction (const Split *split) +{ + return split ? split->action : NULL; +} + +char +xaccSplitGetReconcile (const Split *split) +{ + return split ? split->reconciled : ' '; +} + + +gnc_numeric +xaccSplitGetAmount (const Split * split) +{ + return split ? split->amount : gnc_numeric_zero(); +} + +gnc_numeric +xaccSplitGetValue (const Split * split) +{ + return split ? split->value : gnc_numeric_zero(); +} + +gnc_numeric +xaccSplitGetSharePrice (const Split * split) +{ + gnc_numeric amt, val, price; + if (!split) return gnc_numeric_create(1, 1); + + + /* if amount == 0 and value == 0, then return 1. + * if amount == 0 and value != 0 then return 0. + * otherwise return value/amount + */ + + amt = xaccSplitGetAmount(split); + val = xaccSplitGetValue(split); + if (gnc_numeric_zero_p(amt)) + { + if (gnc_numeric_zero_p(val)) + return gnc_numeric_create(1, 1); + return gnc_numeric_create(0, 1); + } + price = gnc_numeric_div(val, amt, + GNC_DENOM_AUTO, + GNC_HOW_DENOM_SIGFIGS(PRICE_SIGFIGS) | + GNC_HOW_RND_ROUND); + + /* During random checks we can get some very weird prices. Let's + * handle some overflow and other error conditions by returning + * zero. But still print an error to let us know it happened. + */ + if (gnc_numeric_check(price)) + { + PERR("Computing share price failed (%d): [ %" G_GINT64_FORMAT " / %" + G_GINT64_FORMAT " ] / [ %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " ]", + gnc_numeric_check(price), val.num, val.denom, amt.num, amt.denom); + return gnc_numeric_create(0,1); + } + + return price; +} + +/********************************************************************\ +\********************************************************************/ + +QofBook * +xaccSplitGetBook (const Split *split) +{ + return qof_instance_get_book(QOF_INSTANCE(split)); +} + +const char * +xaccSplitGetType(const Split *s) +{ + char *split_type; + + if (!s) return NULL; + split_type = kvp_frame_get_string(s->inst.kvp_data, "split-type"); + return split_type ? split_type : "normal"; +} + +/* reconfigure a split to be a stock split - after this, you shouldn't + mess with the value, just the amount. */ +void +xaccSplitMakeStockSplit(Split *s) +{ + check_open (s->parent); + + s->value = gnc_numeric_zero(); + kvp_frame_set_str(s->inst.kvp_data, "split-type", "stock-split"); + SET_GAINS_VDIRTY(s); + mark_split(s); +} + + +/********************************************************************\ +\********************************************************************/ +/* In the old world, the 'other split' was the other split of a + * transaction that contained only two splits. In the new world, + * a split may have been cut up between multiple lots, although + * in a conceptual sense, if lots hadn't been used, there would be + * only a pair. So we handle this conceptual case: we can still + * identify, unambiguously, the 'other' split when 'this' split + * as been cut up across lots. We do this by looking for the + * 'lot-split' keyword, which occurs only in cut-up splits. + */ + +Split * +xaccSplitGetOtherSplit (const Split *split) +{ + SplitList *node; + Transaction *trans; + int count; + Split *other = NULL; + KvpValue *sva; + + if (!split) return NULL; + trans = split->parent; + if (!trans) return NULL; + +#ifdef OLD_ALGO_HAS_ONLY_TWO_SPLITS + Split *s1, *s2; + if (g_list_length (trans->splits) != 2) return NULL; + + s1 = g_list_nth_data (trans->splits, 0); + s2 = g_list_nth_data (trans->splits, 1); + + if (s1 == split) return s2; + return s1; +#endif + + count = g_list_length (trans->splits); + sva = kvp_frame_get_slot (split->inst.kvp_data, "lot-split"); + if (!sva && (2 != count)) return NULL; + + for (node = trans->splits; node; node = node->next) + { + Split *s = node->data; + if (s == split) { --count; continue; } + if (kvp_frame_get_slot (s->inst.kvp_data, "lot-split")) { --count; continue; } + other = s; + } + return (1 == count) ? other : NULL; +} + +/********************************************************************\ +\********************************************************************/ + +gboolean +xaccIsPeerSplit (const Split *sa, const Split *sb) +{ + return (sa && sb && (sa->parent == sb->parent)); +} + +gnc_numeric +xaccSplitVoidFormerAmount(const Split *split) +{ + g_return_val_if_fail(split, gnc_numeric_zero()); + return kvp_frame_get_numeric(split->inst.kvp_data, void_former_amt_str); +} + +gnc_numeric +xaccSplitVoidFormerValue(const Split *split) +{ + g_return_val_if_fail(split, gnc_numeric_zero()); + return kvp_frame_get_numeric(split->inst.kvp_data, void_former_val_str); +} + +void +xaccSplitVoid(Split *split) +{ + gnc_numeric zero = gnc_numeric_zero(); + KvpFrame *frame = split->inst.kvp_data; + + kvp_frame_set_gnc_numeric(frame, void_former_amt_str, + xaccSplitGetAmount(split)); + kvp_frame_set_gnc_numeric(frame, void_former_val_str, + xaccSplitGetValue(split)); + + xaccSplitSetAmount (split, zero); + xaccSplitSetValue (split, zero); + xaccSplitSetReconcile(split, VREC); + +} + +void +xaccSplitUnvoid(Split *split) +{ + KvpFrame *frame = split->inst.kvp_data; + + xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split)); + xaccSplitSetValue (split, xaccSplitVoidFormerValue(split)); + xaccSplitSetReconcile(split, NREC); + kvp_frame_set_slot(frame, void_former_amt_str, NULL); + kvp_frame_set_slot(frame, void_former_val_str, NULL); +} + +/********************************************************************\ +\********************************************************************/ +/* QofObject function implementation */ + +/* Hook into the QofObject registry */ + +static QofObject split_object_def = { + interface_version: QOF_OBJECT_VERSION, + e_type: GNC_ID_SPLIT, + type_label: "Split", + create: (gpointer)xaccMallocSplit, + book_begin: NULL, + book_end: NULL, + is_dirty: NULL, + mark_clean: NULL, + foreach: qof_collection_foreach, + printable: (const char* (*)(gpointer)) xaccSplitGetMemo, + version_cmp: (int (*)(gpointer, gpointer)) qof_instance_version_cmp, +}; + +static gpointer +split_account_guid_getter (gpointer obj, const QofParam *p) +{ + Split *s = obj; + Account *acc; + + if (!s) return NULL; + acc = xaccSplitGetAccount (s); + if (!acc) return NULL; + return ((gpointer)xaccAccountGetGUID (acc)); +} + +static double /* internal use only */ +DxaccSplitGetShareAmount (const Split * split) +{ + return split ? gnc_numeric_to_double(xaccSplitGetAmount(split)) : 0.0; +} + +static gpointer +no_op (gpointer obj, const QofParam *p) +{ + return obj; +} + +static void +qofSplitSetParentTrans(Split *s, QofEntity *ent) +{ + Transaction *trans = (Transaction*)ent; + + g_return_if_fail(trans); + xaccTransAppendSplit(trans, s); +} + +static void +qofSplitSetAccount(Split *s, QofEntity *ent) +{ + Account *acc = (Account*)ent; + + g_return_if_fail(acc); + xaccAccountInsertSplit(acc, s); +} + +gboolean xaccSplitRegister (void) +{ + static const QofParam params[] = { + { SPLIT_DATE_RECONCILED, QOF_TYPE_DATE, + (QofAccessFunc)xaccSplitRetDateReconciledTS, + (QofSetterFunc)xaccSplitSetDateReconciledTS }, + + /* d-* are deprecated query params, should not be used in new + * queries, should be removed from old queries. */ + { "d-share-amount", QOF_TYPE_DOUBLE, + (QofAccessFunc)DxaccSplitGetShareAmount, NULL }, + { "d-share-int64", QOF_TYPE_INT64, + (QofAccessFunc)qof_entity_get_guid, NULL }, + { SPLIT_BALANCE, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitGetBalance, NULL }, + { SPLIT_CLEARED_BALANCE, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitGetClearedBalance, NULL }, + { SPLIT_RECONCILED_BALANCE, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitGetReconciledBalance, NULL }, + { SPLIT_MEMO, QOF_TYPE_STRING, + (QofAccessFunc)xaccSplitGetMemo, (QofSetterFunc)qofSplitSetMemo }, + { SPLIT_ACTION, QOF_TYPE_STRING, + (QofAccessFunc)xaccSplitGetAction, (QofSetterFunc)qofSplitSetAction }, + { SPLIT_RECONCILE, QOF_TYPE_CHAR, + (QofAccessFunc)xaccSplitGetReconcile, + (QofSetterFunc)qofSplitSetReconcile }, + { SPLIT_AMOUNT, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitGetAmount, (QofSetterFunc)qofSplitSetAmount }, + { SPLIT_SHARE_PRICE, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitGetSharePrice, + (QofSetterFunc)qofSplitSetSharePrice }, + { SPLIT_VALUE, QOF_TYPE_DEBCRED, + (QofAccessFunc)xaccSplitGetValue, (QofSetterFunc)qofSplitSetValue }, + { SPLIT_TYPE, QOF_TYPE_STRING, (QofAccessFunc)xaccSplitGetType, NULL }, + { SPLIT_VOIDED_AMOUNT, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitVoidFormerAmount, NULL }, + { SPLIT_VOIDED_VALUE, QOF_TYPE_NUMERIC, + (QofAccessFunc)xaccSplitVoidFormerValue, NULL }, + { SPLIT_LOT, GNC_ID_LOT, (QofAccessFunc)xaccSplitGetLot, NULL }, + { SPLIT_TRANS, GNC_ID_TRANS, + (QofAccessFunc)xaccSplitGetParent, + (QofSetterFunc)qofSplitSetParentTrans }, + { SPLIT_ACCOUNT, GNC_ID_ACCOUNT, + (QofAccessFunc)xaccSplitGetAccount, (QofSetterFunc)qofSplitSetAccount }, + { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, NULL }, +/* these are no-ops to register the parameter names (for sorting) but + they return an allocated object which getters cannot do. */ + { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, NULL }, + { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, NULL }, + { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, NULL }, + { SPLIT_KVP, QOF_TYPE_KVP, (QofAccessFunc)xaccSplitGetSlots, NULL }, + { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, NULL }, + { QOF_PARAM_GUID, QOF_TYPE_GUID, + (QofAccessFunc)qof_entity_get_guid, NULL }, + { NULL }, + }; + + qof_class_register (GNC_ID_SPLIT, (QofSortFunc)xaccSplitDateOrder, params); + qof_class_register (SPLIT_ACCT_FULLNAME, + (QofSortFunc)xaccSplitCompareAccountFullNames, NULL); + qof_class_register (SPLIT_CORR_ACCT_NAME, + (QofSortFunc)xaccSplitCompareOtherAccountFullNames, + NULL); + qof_class_register (SPLIT_CORR_ACCT_CODE, + (QofSortFunc)xaccSplitCompareOtherAccountCodes, NULL); + + return qof_object_register (&split_object_def); +} + +/************************ END OF ************************************\ +\************************* FILE *************************************/ diff --git a/src/engine/Split.h b/src/engine/Split.h new file mode 100644 index 0000000000..57a73fddad --- /dev/null +++ b/src/engine/Split.h @@ -0,0 +1,490 @@ +/********************************************************************\ + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ +/** @addtogroup Engine + @{ */ +/** @addtogroup Transaction Financial Transactions + A good overview of transactions, splits and accounts can be + found in the texinfo documentation, together with an overview of + how to use this API. + + @{ */ +/** @file Split.h + @brief API for Transactions and Splits (journal entries) + @author Copyright (C) 1997 Robin D. Clark + @author Copyright (C) 1997-2001 Linas Vepstas +*/ + +#ifndef XACC_SPLIT_H +#define XACC_SPLIT_H + +#include + +#include "gnc-commodity.h" +#include "gnc-engine.h" + +/** @name Split Reconciled field values + + If you change these + be sure to change gnc-ui-util.c:gnc_get_reconciled_str() and + associated functions + +@{ +*/ +#define CREC 'c' /**< The Split has been cleared */ +#define YREC 'y' /**< The Split has been reconciled */ +#define FREC 'f' /**< frozen into accounting period */ +#define NREC 'n' /**< not reconciled or cleared */ +#define VREC 'v' /**< split is void */ +/** @} */ + +/* Convert the amount/value of the Split for viewing in the account -- + * in particular we want to convert the Split to be in to_commodity. + * Returns the amount. + */ +gnc_numeric xaccSplitConvertAmount (Split *split, Account * account); + +/*----------------------------------------------------------------------- + * Splits + *-----------------------------------------------------------------------*/ + +/** @name Split general getters/setters +@{ +*/ + +/** Constructor. */ +Split * xaccMallocSplit (QofBook *book); + +/* Reinit a previously malloc'd split. Split remains in the book it + was already in, and the QofEntity portions also remain unchanged. + It's basically the data elements that are reverted to default + values. */ +void xaccSplitReinit(Split * split); + +/** Destructor. + * + * The xaccSplitDestroy() method will update its parent account and + * transaction in a consistent manner, resulting in the complete + * unlinking of the split, and the freeing of its associated memory. + * The goal of this routine is to perform the removal and destruction + * of the split in an atomic fashion, with no chance of accidentally + * leaving the accounting structure out-of-balance or otherwise + * inconsistent. + * + * If the deletion of the split leaves the transaction with no splits, + * then the transaction will be marked for deletion. (It will not be + * deleted until the xaccTransCommitEdit() routine is called.) + * + * @return TRUE upon successful deletion of the split. FALSE when + * the parenting Transaction is a read-only one. + */ +gboolean xaccSplitDestroy (Split *split); + +/** Returns the book of this split, i.e. the entity where this split + * is stored. */ +QofBook * xaccSplitGetBook (const Split *split); + +/** Returns the account of this split, which was set through + * xaccAccountInsertSplit(). */ +Account * xaccSplitGetAccount (const Split *split); + +/** Returns the parent transaction of the split, which was set through + * xaccTransAppendSplit(). */ +Transaction * xaccSplitGetParent (const Split *split); + +/** Returns the pointer to the debited/credited Lot where this split + * belongs to, or NULL if it doesn't belong to any. */ +GNCLot * xaccSplitGetLot (const Split *split); + + +/** Returns the KvpFrame slots of this split for direct editing. + * + * Split slots are used to store arbitrary strings, numbers, and + * structures which aren't members of the transaction struct. See + * kvp_doc.txt for reserved slot names. + */ +KvpFrame *xaccSplitGetSlots(const Split *split); + +/** Set the KvpFrame slots of this split to the given frm by directly + * using the frm pointer (i.e. non-copying). */ +void xaccSplitSetSlots_nc(Split *s, KvpFrame *frm); + + +/** The memo is an arbitrary string associated with a split. It is + * intended to hold a short (zero to forty character) string that is + * displayed by the GUI along with this split. Users typically type + * in free form text from the GUI. */ +void xaccSplitSetMemo (Split *split, const char *memo); + +/** Returns the memo string. */ +const char * xaccSplitGetMemo (const Split *split); + +/** The Action is an arbitrary user-assigned string. + * The action field is an arbitrary user-assigned value. + * It is meant to be a very short (one to ten cahracter) string that + * signifies the "type" of this split, such as e.g. Buy, Sell, Div, + * Withdraw, Deposit, ATM, Check, etc. The idea is that this field + * can be used to create custom reports or graphs of data. */ +void xaccSplitSetAction (Split *split, const char *action); + +/** Returns the action string. */ +const char * xaccSplitGetAction (const Split *split); +/** @} */ + +/** @name Split Date getters/setters +@{ +*/ +/** Set the reconcile flag. The Reconcile flag is a single char, whose + * values are typically are 'n', 'y', 'c'. In Transaction.h, macros + * are defined for typical values (e.g. CREC, YREC). */ +void xaccSplitSetReconcile (Split *split, char reconciled_flag); +/** Returns the value of the reconcile flag. */ +char xaccSplitGetReconcile (const Split *split); + +/** Set the date on which this split was reconciled by specifying the + * time as time_t. */ +void xaccSplitSetDateReconciledSecs (Split *split, time_t time); +/** Set the date on which this split was reconciled by specifying the + * time as Timespec. */ +void xaccSplitSetDateReconciledTS (Split *split, Timespec *ts); +/** Get the date on which this split was reconciled by having it + * written into the Timespec that 'ts' is pointing to. */ +void xaccSplitGetDateReconciledTS (const Split *split, + Timespec *ts); +/** Returns the date (as Timespec) on which this split was reconciled. */ +Timespec xaccSplitRetDateReconciledTS (const Split *split); + +/** @} */ + + +/** @name Split amount getters/setters + * + * 'value' vs. 'amount' of a Split: The 'value' is the amount of the + * _transaction_ balancing commodity (i.e. currency) involved, + * 'amount' is the amount of the _account's_ commodity involved. +@{ +*/ + +/** The xaccSplitSetAmount() method sets the amount in the account's + * commodity that the split should have. + * + * The following four setter functions set the prices and amounts. + * All of the routines always maintain balance: that is, invoking any + * of them will cause other splits in the transaction to be modified + * so that the net value of the transaction is zero. + * + * IMPORTANT: The split should be parented by an account before + * any of these routines are invoked! This is because the actual + * setting of amounts/values requires SCU settings from the account. + * If these are not available, then amounts/values will be set to + * -1/0, which is an invalid value. I beleive this order dependency + * is a bug, but I'm too lazy to find, fix & test at the moment ... + * + * @note If you use this on a newly created transaction, make sure + * that the 'value' is also set so that it doesn't remain zero. + */ +void xaccSplitSetAmount (Split *split, gnc_numeric amount); + +/** Returns the amount of the split in the account's commodity. + * Note that for cap-gains splits, this is slaved to the transaction + * that is causing the gains to occur. + */ +gnc_numeric xaccSplitGetAmount (const Split * split); + +/** The xaccSplitSetValue() method sets the value of this split in the + * transaction's commodity. + * + * @note If you use this on a newly created transaction, make sure + * that the 'amount' is also set so that it doesn't remain zero. + */ +void xaccSplitSetValue (Split *split, gnc_numeric value); + +/** Returns the value of this split in the transaction's commodity. + * Note that for cap-gains splits, this is slaved to the transaction + * that is causing the gains to occur. +*/ +gnc_numeric xaccSplitGetValue (const Split * split); + +/** The xaccSplitSetSharePriceAndAmount() method will simultaneously + * update the share price and the number of shares. This is a utility + * routine that is equivalent to a xaccSplitSetSharePrice() followed + * by and xaccSplitSetAmount(), except that it incurs the processing + * overhead of balancing only once, instead of twice. */ +void xaccSplitSetSharePriceAndAmount (Split *split, + gnc_numeric price, + gnc_numeric amount); + +/** Returns the price of the split, that is, the value divided by the + * amount. If the amount is zero, returns a gnc_numeric of value + * one. */ +gnc_numeric xaccSplitGetSharePrice (const Split * split); + +/** Depending on the base_currency, set either the value or the amount + * of this split or both: If the base_currency is the transaction's + * commodity, set the value. If it is the account's commodity, set the + * amount. If both, set both. + * + * @note WATCH OUT: When using this function and the + * transaction's and account's commodities are different, the amount + * or the value will be left as zero. This might screw up the + * multi-currency handling code in the register. So please think twice + * whether you need this function -- using xaccSplitSetValue() + * together with xaccSplitSetAmount() is definitely the better and + * safer solution! + */ +void xaccSplitSetBaseValue (Split *split, gnc_numeric value, + const gnc_commodity * base_currency); + +/** Depending on the base_currency, return either the value or the + * amount of this split: If the base_curreny is the transaction's + * commodity, return the value. If it is the account's commodity, + * return the amount. If it is neither print a warning message and + * return gnc_numeric_zero(). + */ +gnc_numeric xaccSplitGetBaseValue (const Split *split, + const gnc_commodity * base_currency); + +/** Returns the running balance up to and including the indicated split. + * The balance is the currency-denominated balance. For accounts + * with non-unit share prices, it is correctly adjusted for + * share prices. + * + * Returns the running balance up to & including the indicated split. + */ +gnc_numeric xaccSplitGetBalance (const Split *split); + +/** + * The cleared-balance is the currency-denominated balance + * of all transactions that have been marked as cleared or reconciled. + * It is correctly adjusted for price fluctuations. + * + * Returns the running balance up to & including the indicated split. + */ +gnc_numeric xaccSplitGetClearedBalance (const Split *split); + +/** + * Returns the reconciled-balance of this split. The + * reconciled-balance is the currency-denominated balance of all + * transactions that have been marked as reconciled. + * + * Returns the running balance up to & including the indicated split. + */ +gnc_numeric xaccSplitGetReconciledBalance (const Split *split); + +/** @} */ + +/** @name Split utility functions +@{ +*/ + +/* Get a GList of unique transactions containing the given list of Splits. */ +GList *xaccSplitListGetUniqueTransactions(const GList *splits); + +/** Equality. + * + * @param sa First split to compare + * @param sb Second split to compare + * + * @param check_guids If TRUE, try a guid_equal() on the GUIDs of both + * splits if their pointers are not equal in the first place. + * + * @param check_balances If TRUE, compare balances between the two + * splits. Balances are recalculated whenever a split is added or + * removed from an account, so YMMV on whether this should be set. + * + * @param check_txn_splits If the pointers are not equal, but + * everything else so far is equal (including memo, amount, value, + * kvp_frame), then, when comparing the parenting transactions with + * xaccTransEqual(), set its argument check_splits to be TRUE. + */ +gboolean xaccSplitEqual(const Split *sa, const Split *sb, + gboolean check_guids, + gboolean check_balances, + gboolean check_txn_splits); + +/** The xaccSplitLookup() subroutine will return the + * split associated with the given id, or NULL + * if there is no such split. */ +Split * xaccSplitLookup (const GUID *guid, QofBook *book); +#define xaccSplitLookupDirect(g,b) xaccSplitLookup(&(g),b) + + +/** + * The xaccSplitGetOtherSplit() is a convenience routine that returns + * the other of a pair of splits. If there are more than two + * splits, it returns NULL. + */ +Split * xaccSplitGetOtherSplit (const Split *split); + +/** The xaccIsPeerSplit() is a convenience routine that returns TRUE + * (a non-zero value) if the two splits share a common parent + * transaction, else it returns FALSE (zero). + */ +gboolean xaccIsPeerSplit (const Split *split_1, const Split *split_2); + +/** Returns the split type, which is either the string "normal", or + * "stock-split" for a split from a stock split (pun intended? :-). */ +const char *xaccSplitGetType(const Split *s); + +/** Mark a split to be of type stock split - after this, you shouldn't + modify the value anymore, just the amount. */ +void xaccSplitMakeStockSplit(Split *s); + +/** + * The xaccSplitDateOrder(sa,sb) method is useful for sorting. + * if sa and sb have different transactions, return their xaccTransOrder + * return a negative value if split sa has a smaller currency-value than sb, + * return a positive value if split sa has a larger currency-value than sb, + * return a negative value if split sa has a smaller share-price than sb, + * return a positive value if split sa has a larger share-price than sb, + * then compares memo and action using the strcmp() + * c-library routine, returning what strcmp would return. + * Then it compares the reconciled flags, then the reconciled dates, + * Finally, it returns zero if all of the above match. + */ +int xaccSplitDateOrder (const Split *sa, const Split *sb); + + +/* + * These functions compare two splits by different criteria. + * + * These functions were added because converting strings to guile + * for comparisons in the transaction report is terribly inefficient. + * More may be added here in future if it turns out that other types + * of comparisons also induces guile slowdowns. + */ + +/** Compare two splits by full name of account. Returns similar to + * strcmp. */ +int xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb); +/** Compare two splits by code of account. Returns similar to + * strcmp. */ +int xaccSplitCompareAccountCodes(const Split *sa, const Split *sb); +/** Compare two splits by full name of the other account. Returns + * similar to strcmp. This function attempts to find the split on the + * other side of a transaction and compare on it. */ +int xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb); +/** Compare two splits by code of the other account. Returns similar + * to strcmp. This function attempts to find the split on the + * other side of a transaction and compare on it. */ +int xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb); + + +/** + * These functions take a split, get the corresponding split on the + * "other side" of the transaction, and extract either the name or code + * of that split, reverting to returning a constant "Split" if the + * transaction has more than one split on the "other side". These + * were added for the transaction report, and is in C because the code + * was already written in C for the above functions and duplication + * is silly. + */ + +char * xaccSplitGetCorrAccountFullName(const Split *sa, char seperator); +/** document me */ +const char * xaccSplitGetCorrAccountName(const Split *sa); +/** document me */ +const char * xaccSplitGetCorrAccountCode(const Split *sa); + +/** @} */ + + + +/** @name Split deprecated functions +@{ +*/ + +/** @deprecated The xaccSplitSetSharePrice() method sets the price of the + * split. DEPRECATED - set the value and amount instead. */ +void xaccSplitSetSharePrice (Split *split, gnc_numeric price); + +/** @} */ + + +/********************************************************************\ + * Miscellaneous utility routines. +\********************************************************************/ + + +/** @name Split voiding +@{ +*/ + +/** Returns the original pre-void amount of a split. + * + * @param split The split in question. + * + * @return A gnc_numeric containing the original value of this split. + * Returns a gnc_numeric of zero upon error. + */ +gnc_numeric xaccSplitVoidFormerAmount(const Split *split); + +/** Returns the original pre-void value of a split. + * + * @param split The split in question. + * + * @return A gnc_numeric containing the original amount of this split. + * Returns a gnc_numeric of zero upon error. + */ +gnc_numeric xaccSplitVoidFormerValue(const Split *split); + +/** @} */ + +/** @name Split Parameter names + + * Note, if you want to get the equivalent of "ACCT_MATCH_ALL" you + * need to create a search on the following parameter list: + * SPLIT->SPLIT_TRANS->TRANS_SPLITLIST->SPLIT_ACCOUNT_GUID. If you do + * this, you might want to use the ACCOUNT_MATCH_ALL_TYPE as the + * override so the gnome-search dialog displays the right type. + @{ +*/ +#define SPLIT_KVP "kvp" + +#define SPLIT_DATE_RECONCILED "date-reconciled" +#define SPLIT_BALANCE "balance" +#define SPLIT_CLEARED_BALANCE "cleared-balance" +#define SPLIT_RECONCILED_BALANCE "reconciled-balance" +#define SPLIT_MEMO "memo" +#define SPLIT_ACTION "action" +#define SPLIT_RECONCILE "reconcile-flag" +#define SPLIT_AMOUNT "amount" +#define SPLIT_SHARE_PRICE "share-price" +#define SPLIT_VALUE "value" +#define SPLIT_TYPE "type" +#define SPLIT_VOIDED_AMOUNT "voided-amount" +#define SPLIT_VOIDED_VALUE "voided-value" +#define SPLIT_LOT "lot" +#define SPLIT_TRANS "trans" +#define SPLIT_ACCOUNT "account" +#define SPLIT_ACCOUNT_GUID "account-guid" /**< for guid_match_all */ +/* used for SORTING ONLY */ +#define SPLIT_ACCT_FULLNAME "acct-fullname" +#define SPLIT_CORR_ACCT_NAME "corr-acct-fullname" +#define SPLIT_CORR_ACCT_CODE "corr-acct-code" +/** @} */ + +/** \deprecated */ +#define xaccSplitGetGUID(X) qof_entity_get_guid(QOF_ENTITY(X)) +/** \deprecated */ +#define xaccSplitReturnGUID(X) (X ? *(qof_entity_get_guid(QOF_ENTITY(X))) : *(guid_null())) + +#endif /* XACC_SPLIT_H */ +/** @} */ +/** @} */ diff --git a/src/engine/SplitP.h b/src/engine/SplitP.h new file mode 100644 index 0000000000..f50ec2544a --- /dev/null +++ b/src/engine/SplitP.h @@ -0,0 +1,243 @@ +/********************************************************************\ + * SplitP.h -- private header for splits * + * Copyright (C) 1997 Robin D. Clark * + * Copyright (C) 1997-2000 Linas Vepstas * + * Copyright (C) 2000 Bill Gribble * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/* + * FILE: + * SplitP.h + * + * FUNCTION: + * The is the *private* split header file. Code outside of + * engine should *not* include this file. This is because code + * outside of the engine should *never* access any of the structure + * members directly. + * + * Note that this header file also defines prototypes for various + * routines that perform sub-atomic updates of the accounting + * structures. If these routines are not used properly, they + * can result in inconsistent, unbalanced accounting structures. + * In other words, their use is dangerous, and their use outside + * of the scope of the engine is forbidden. + * + */ + +#ifndef XACC_SPLIT_P_H +#define XACC_SPLIT_P_H + +#include "config.h" + +#include +#include + +#include "gnc-engine.h" /* for typedefs */ +#include "qof.h" + + +/** STRUCTS *********************************************************/ +/* A "split" is more commonly referred to as an "entry" in a "transaction". + */ + +/* Flags for handling cap-gains status */ +#define GAINS_STATUS_UNKNOWN 0xff +#define GAINS_STATUS_CLEAN 0x0 +#define GAINS_STATUS_GAINS 0x3 +#define GAINS_STATUS_DATE_DIRTY 0x10 +#define GAINS_STATUS_AMNT_DIRTY 0x20 +#define GAINS_STATUS_VALU_DIRTY 0x40 +#define GAINS_STATUS_LOT_DIRTY 0x80 +#define GAINS_STATUS_ADIRTY (GAINS_STATUS_AMNT_DIRTY|GAINS_STATUS_LOT_DIRTY) +#define GAINS_STATUS_VDIRTY (GAINS_STATUS_VALU_DIRTY) +#define GAINS_STATUS_A_VDIRTY (GAINS_STATUS_AMNT_DIRTY|GAINS_STATUS_VALU_DIRTY|GAINS_STATUS_LOT_DIRTY) + +struct split_s +{ + QofInstance inst; + + Account *acc; /* back-pointer to debited/credited account */ + + GNCLot *lot; /* back-pointer to debited/credited lot */ + + Transaction *parent; /* parent of split */ + + /* The memo field is an arbitrary user-assiged value. + * It is intended to hold a short (zero to forty character) string + * that is displayed by the GUI along with this split. + */ + char * memo; + + /* The action field is an arbitrary user-assigned value. + * It is meant to be a very short (one to ten character) string that + * signifies the "type" of this split, such as e.g. Buy, Sell, Div, + * Withdraw, Deposit, ATM, Check, etc. The idea is that this field + * can be used to create custom reports or graphs of data. + */ + char * action; /* Buy, Sell, Div, etc. */ + + Timespec date_reconciled; /* date split was reconciled */ + char reconciled; /* The reconciled field */ + + /* gains is a flag used to track the relationship between + * capital-gains splits. Depending on its value, this flag indicates + * if this split is the source of gains, if this split is a record + * of the gains, and if values are 'dirty' and need to be recomputed. + */ + unsigned char gains; + + /* 'gains_split' is a convenience pointer used to track down the + * other end of a cap-gains transaction pair. NULL if this split + * doesn't involve cap gains. + */ + Split *gains_split; + + /* 'value' is the quantity of the transaction balancing commodity + * (i.e. currency) involved, 'amount' is the amount of the account's + * commodity involved. */ + gnc_numeric value; + gnc_numeric amount; + + /* -------------------------------------------------------------- */ + /* Below follow some 'temporary' fields */ + + /* The various "balances" are the sum of all of the values of + * all the splits in the account, up to and including this split. + * These balances apply to a sorting order by date posted + * (not by date entered). */ + gnc_numeric balance; + gnc_numeric cleared_balance; + gnc_numeric reconciled_balance; + + /* -------------------------------------------------------------- */ + /* Backend private expansion data */ + guint32 idata; /* used by the sql backend for kvp management */ +}; + + +/* Set the split's GUID. This should only be done when reading + * a split from a datafile, or some other external source. Never + * call this on an existing split! */ +#define xaccSplitSetGUID(s,g) qof_entity_set_guid(QOF_ENTITY(s),g) + +/* The xaccFreeSplit() method simply frees all memory associated + * with the split. It does not verify that the split isn't + * referenced in some account. If the split is referenced by an + * account, then calling this method will leave the system in an + * inconsistent state. This *will* lead to crashes and hangs. + */ +void xaccFreeSplit (Split *split); /* frees memory */ + +Split * xaccSplitClone (const Split *s); + +Split *xaccDupeSplit (const Split *s); +G_INLINE_FUNC void mark_split (Split *s); +G_INLINE_FUNC void gen_event (const Split *split); + +void xaccSplitVoid(Split *split); +void xaccSplitUnvoid(Split *split); + +/* Compute the value of a list of splits in the given currency, + * excluding the skip_me split. */ +gnc_numeric xaccSplitsComputeValue (GList *splits, Split * skip_me, + const gnc_commodity * base_currency); + +/* Code to register Split type with the engine */ +gboolean xaccSplitRegister (void); + +/* The xaccSplitDetermineGainStatus() routine will analyze the + * the split, and try to set the internal status flags + * appropriately for the split. These flags indicate if the split + * represents cap gains, and if the gains value/amount needs to be + * recomputed. + */ +void xaccSplitDetermineGainStatus (Split *split); + +/* ---------------------------------------------------------------- */ +/* Deprecated routines */ +void DxaccSplitSetSharePriceAndAmount (Split *split, + double price, + double amount); +void DxaccSplitSetShareAmount (Split *split, double amount); + +/********************************************************************\ + * sorting comparison function + * + * returns a negative value if transaction a is dated earlier than b, + * returns a positive value if transaction a is dated later than b, + * + * This function tries very hard to uniquely order all transactions. + * If two transactions occur on the same date, then their "num" fields + * are compared. If the num fields are identical, then the description + * fields are compared. If these are identical, then the memo fields + * are compared. Hopefully, there will not be any transactions that + * occur on the same day that have all three of these values identical. + * + * Note that being able to establish this kind of absolute order is + * important for some of the ledger display functions. + * + * Yes, this kind of code dependency is ugly, but the alternatives seem + * ugly too. + * +\********************************************************************/ + + +#define DATE_CMP(aaa,bbb,field) { \ + /* if dates differ, return */ \ + if ( (aaa->field.tv_sec) < \ + (bbb->field.tv_sec)) { \ + return -1; \ + } else \ + if ( (aaa->field.tv_sec) > \ + (bbb->field.tv_sec)) { \ + return +1; \ + } \ + \ + /* else, seconds match. check nanoseconds */ \ + if ( (aaa->field.tv_nsec) < \ + (bbb->field.tv_nsec)) { \ + return -1; \ + } else \ + if ( (aaa->field.tv_nsec) > \ + (bbb->field.tv_nsec)) { \ + return +1; \ + } \ +} + +#define CHECK_GAINS_STATUS(s) \ + if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s); + +#define SET_GAINS_DIRTY(s,flg) do { \ + if (FALSE == (GAINS_STATUS_GAINS & s->gains)) { \ + s->gains |= flg; \ + } else { \ + if (s->gains_split) s->gains_split->gains |= flg; \ + } \ +} while (0) + +#define SET_GAINS_ADIRTY(s) SET_GAINS_DIRTY(s,GAINS_STATUS_ADIRTY); +#define SET_GAINS_A_VDIRTY(s) SET_GAINS_DIRTY(s,GAINS_STATUS_A_VDIRTY); +#define SET_GAINS_VDIRTY(s) SET_GAINS_DIRTY(s,GAINS_STATUS_VDIRTY); + +/*@}*/ + + +#endif /* XACC_SPLIT_P_H */ diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 93f9dcd3c7..76105bf0a2 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -1,5 +1,5 @@ /********************************************************************\ - * Transaction.c -- transaction & split implementation * + * Transaction.c -- transaction implementation * * Copyright (C) 1997 Robin D. Clark * * Copyright (C) 1997-2003 Linas Vepstas * * Copyright (C) 2000 Bill Gribble * @@ -39,6 +39,7 @@ #include "Scrub.h" #include "Scrub3.h" #include "TransactionP.h" +#include "SplitP.h" #include "TransLog.h" #include "cap-gains.h" #include "gnc-commodity.h" @@ -63,8 +64,6 @@ const char *trans_notes_str = "notes"; const char *void_reason_str = "void-reason"; const char *void_time_str = "void-time"; -const char *void_former_amt_str = "void-former-amount"; -const char *void_former_val_str = "void-former-value"; const char *void_former_notes_str = "void-former-notes"; /* KVP entry for date-due value */ @@ -72,14 +71,11 @@ const char *void_former_notes_str = "void-former-notes"; #define TRANS_TXN_TYPE_KVP "trans-txn-type" #define TRANS_READ_ONLY_REASON "trans-read-only" -#define PRICE_SIGFIGS 6 - #define ISO_DATELENGTH 32 /* length of an iso 8601 date string. */ /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ENGINE; -G_INLINE_FUNC void check_open (const Transaction *trans); void check_open (const Transaction *trans) { if (trans && 0 >= trans->inst.editlevel) @@ -87,489 +83,8 @@ void check_open (const Transaction *trans) } /********************************************************************\ - * xaccInitSplit - * Initialize a Split structure \********************************************************************/ -static void -xaccInitSplit(Split * split, QofBook *book) -{ - /* fill in some sane defaults */ - split->acc = NULL; - split->parent = NULL; - split->lot = NULL; - - split->action = gnc_string_cache_insert(""); - split->memo = gnc_string_cache_insert(""); - split->reconciled = NREC; - split->amount = gnc_numeric_zero(); - split->value = gnc_numeric_zero(); - - split->date_reconciled.tv_sec = 0; - split->date_reconciled.tv_nsec = 0; - - split->balance = gnc_numeric_zero(); - split->cleared_balance = gnc_numeric_zero(); - split->reconciled_balance = gnc_numeric_zero(); - - split->idata = 0; - - split->gains = GAINS_STATUS_UNKNOWN; - split->gains_split = NULL; - - qof_instance_init(&split->inst, GNC_ID_SPLIT, book); -} - -void -xaccSplitReinit(Split * split) -{ - /* fill in some sane defaults */ - split->acc = NULL; - split->parent = NULL; - split->lot = NULL; - - CACHE_REPLACE(split->action, ""); - CACHE_REPLACE(split->memo, ""); - split->reconciled = NREC; - split->amount = gnc_numeric_zero(); - split->value = gnc_numeric_zero(); - - split->date_reconciled.tv_sec = 0; - split->date_reconciled.tv_nsec = 0; - - split->balance = gnc_numeric_zero(); - split->cleared_balance = gnc_numeric_zero(); - split->reconciled_balance = gnc_numeric_zero(); - - if (split->inst.kvp_data) - kvp_frame_delete(split->inst.kvp_data); - split->inst.kvp_data = kvp_frame_new(); - split->idata = 0; - - split->gains = GAINS_STATUS_UNKNOWN; - split->gains_split = NULL; -} - -/********************************************************************\ -\********************************************************************/ - -Split * -xaccMallocSplit(QofBook *book) -{ - Split *split; - g_return_val_if_fail (book, NULL); - - split = g_new0 (Split, 1); - xaccInitSplit (split, book); - - return split; -} - -/********************************************************************\ -\********************************************************************/ -/* This routine is not exposed externally, since it does weird things, - * like not really setting up the parent account correctly, and ditto - * the parent transaction. This routine is prone to programmer error - * if not used correctly. It is used only by the edit-rollback code. - * Don't get duped! - */ - -static Split * -xaccDupeSplit (const Split *s) -{ - Split *split = g_new0 (Split, 1); - - /* Trash the guid and entity table. We don't want to mistake - * the cloned splits as something official. If we ever use this - * split, we'll have to fix this up. - */ - split->inst.entity.e_type = NULL; - split->inst.entity.guid = *guid_null(); - split->inst.entity.collection = NULL; - split->inst.book = s->inst.book; - - split->parent = s->parent; - split->acc = s->acc; - split->lot = s->lot; - - split->memo = gnc_string_cache_insert(s->memo); - split->action = gnc_string_cache_insert(s->action); - - split->inst.kvp_data = kvp_frame_copy (s->inst.kvp_data); - - split->reconciled = s->reconciled; - split->date_reconciled = s->date_reconciled; - - split->value = s->value; - split->amount = s->amount; - - /* no need to futz with the balances; these get wiped each time ... - * split->balance = s->balance; - * split->cleared_balance = s->cleared_balance; - * split->reconciled_balance = s->reconciled_balance; - */ - - return split; -} - -Split * -xaccSplitClone (const Split *s) -{ - Split *split = g_new0 (Split, 1); - - split->parent = NULL; - split->memo = gnc_string_cache_insert(s->memo); - split->action = gnc_string_cache_insert(s->action); - split->reconciled = s->reconciled; - split->date_reconciled = s->date_reconciled; - split->value = s->value; - split->amount = s->amount; - split->balance = s->balance; - split->cleared_balance = s->cleared_balance; - split->reconciled_balance = s->reconciled_balance; - split->idata = 0; - - split->gains = GAINS_STATUS_UNKNOWN; - split->gains_split = NULL; - - qof_instance_init(&split->inst, GNC_ID_SPLIT, s->inst.book); - kvp_frame_delete(split->inst.kvp_data); - split->inst.kvp_data = kvp_frame_copy(s->inst.kvp_data); - - xaccAccountInsertSplit(s->acc, split); - if (s->lot) - { - /* FIXME: Doesn't look right. */ - s->lot->splits = g_list_append (s->lot->splits, split); - s->lot->is_closed = -1; - } - return split; -} - -#ifdef DUMP_FUNCTIONS -static void -xaccSplitDump (const Split *split, const char *tag) -{ - printf(" %s Split %p", tag, split); - printf(" GUID: %s\n", guid_to_string(&split->guid)); - printf(" Book: %p\n", split->inst.book); - printf(" Account: %p\n", split->acc); - printf(" Lot: %p\n", split->lot); - printf(" Parent: %p\n", split->parent); - printf(" Memo: %s\n", split->memo ? split->memo : "(null)"); - printf(" Action: %s\n", split->action ? split->action : "(null)"); - printf(" KVP Data: %p\n", split->inst.kvp_data); - printf(" Recncld: %c (date %s)\n", split->reconciled, - gnc_print_date(split->date_reconciled)); - printf(" Value: %s\n", gnc_numeric_to_string(split->value)); - printf(" Amount: %s\n", gnc_numeric_to_string(split->amount)); - printf(" Balance: %s\n", gnc_numeric_to_string(split->balance)); - printf(" CBalance: %s\n", gnc_numeric_to_string(split->cleared_balance)); - printf(" RBalance: %s\n", - gnc_numeric_to_string(split->reconciled_balance)); - printf(" idata: %x\n", split->idata); -} -#endif - -/********************************************************************\ -\********************************************************************/ - -void -xaccFreeSplit (Split *split) -{ - if (!split) return; - - /* Debug double-free's */ - if (((char *) 1) == split->memo) - { - PERR ("double-free %p", split); - return; - } - gnc_string_cache_remove(split->memo); - gnc_string_cache_remove(split->action); - - /* Just in case someone looks up freed memory ... */ - split->memo = (char *) 1; - split->action = NULL; - split->reconciled = NREC; - split->amount = gnc_numeric_zero(); - split->value = gnc_numeric_zero(); - split->parent = NULL; - split->lot = NULL; - split->acc = NULL; - - split->date_reconciled.tv_sec = 0; - split->date_reconciled.tv_nsec = 0; - - // Is this right? - if (split->gains_split) split->gains_split->gains_split = NULL; - qof_instance_release(&split->inst); - g_free(split); -} - -/* - * Helper routine for xaccSplitEqual. - */ -static gboolean -xaccSplitEqualCheckBal (const char *tag, gnc_numeric a, gnc_numeric b) -{ - char *str_a, *str_b; - - if (gnc_numeric_equal (a, b)) - return TRUE; - - str_a = gnc_numeric_to_string (a); - str_b = gnc_numeric_to_string (b); - - PWARN ("%sbalances differ: %s vs %s", tag, str_a, str_b); - - g_free (str_a); - g_free (str_b); - - return FALSE; -} - -/******************************************************************** - * xaccSplitEqual - ********************************************************************/ -gboolean -xaccSplitEqual(const Split *sa, const Split *sb, - gboolean check_guids, - gboolean check_balances, - gboolean check_txn_splits) -{ - if (!sa && !sb) return TRUE; /* Arguable. FALSE is better, methinks */ - - if (!sa || !sb) - { - PWARN ("one is NULL"); - return FALSE; - } - - if (sa == sb) return TRUE; - - if (check_guids) { - if (!guid_equal(&(sa->inst.entity.guid), &(sb->inst.entity.guid))) - { - PWARN ("GUIDs differ"); - return FALSE; - } - } - - /* Since these strings are cached we can just use pointer equality */ - if (sa->memo != sb->memo) - { - PWARN ("memos differ: (%p)%s vs (%p)%s", - sa->memo, sa->memo, sb->memo, sb->memo); - return FALSE; - } - - if (sa->action != sb->action) - { - PWARN ("actions differ: %s vs %s", sa->action, sb->action); - return FALSE; - } - - if (kvp_frame_compare(sa->inst.kvp_data, sb->inst.kvp_data) != 0) - { - char *frame_a; - char *frame_b; - - frame_a = kvp_frame_to_string (sa->inst.kvp_data); - frame_b = kvp_frame_to_string (sb->inst.kvp_data); - - PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b); - - g_free (frame_a); - g_free (frame_b); - - return FALSE; - } - - if (sa->reconciled != sb->reconciled) - { - PWARN ("reconcile flags differ: %c vs %c", sa->reconciled, sb->reconciled); - return FALSE; - } - - if (timespec_cmp(&(sa->date_reconciled), &(sb->date_reconciled))) - { - PWARN ("reconciled date differs"); - return FALSE; - } - - if (!gnc_numeric_eq(xaccSplitGetAmount (sa), xaccSplitGetAmount (sb))) - { - char *str_a; - char *str_b; - - str_a = gnc_numeric_to_string (xaccSplitGetAmount (sa)); - str_b = gnc_numeric_to_string (xaccSplitGetAmount (sb)); - - PWARN ("amounts differ: %s vs %s", str_a, str_b); - - g_free (str_a); - g_free (str_b); - - return FALSE; - } - - if (!gnc_numeric_eq(xaccSplitGetValue (sa), xaccSplitGetValue (sb))) - { - char *str_a; - char *str_b; - - str_a = gnc_numeric_to_string (xaccSplitGetValue (sa)); - str_b = gnc_numeric_to_string (xaccSplitGetValue (sb)); - - PWARN ("values differ: %s vs %s", str_a, str_b); - - g_free (str_a); - g_free (str_b); - - return FALSE; - } - - if (check_balances) { - if (!xaccSplitEqualCheckBal ("", sa->balance, sb->balance)) - return FALSE; - if (!xaccSplitEqualCheckBal ("cleared ", sa->cleared_balance, - sb->cleared_balance)) - return FALSE; - if (!xaccSplitEqualCheckBal ("reconciled ", sa->reconciled_balance, - sb->reconciled_balance)) - return FALSE; - } - - if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits, - check_balances, FALSE)) - { - PWARN ("transactions differ"); - return FALSE; - } - - return TRUE; -} - -static void -add_keys_to_list(gpointer key, gpointer val, gpointer list) -{ - *(GList **)list = g_list_prepend(*(GList **)list, key); -} - -GList * -xaccSplitListGetUniqueTransactions(const GList *splits) -{ - const GList *node; - GList *transList = NULL; - GHashTable *transHash = g_hash_table_new(g_direct_hash, g_direct_equal); - - for(node = splits; node; node = node->next) { - Transaction *trans = xaccSplitGetParent((Split *)(node->data)); - g_hash_table_insert(transHash, trans, trans); - } - g_hash_table_foreach(transHash, add_keys_to_list, &transList); - g_hash_table_destroy(transHash); - return transList; -} - -/******************************************************************** - * Account funcs - ********************************************************************/ - -Account * -xaccSplitGetAccount (const Split *s) -{ - return s ? s->acc : NULL; -} - -/********************************************************************\ -\********************************************************************/ - -Split * -xaccSplitLookup (const GUID *guid, QofBook *book) -{ - QofCollection *col; - if (!guid || !book) return NULL; - col = qof_book_get_collection (book, GNC_ID_SPLIT); - return (Split *) qof_collection_lookup_entity (col, guid); -} - -/********************************************************************\ -\********************************************************************/ -/* Routines for marking splits dirty, and for sending out change - * events. Note that we can't just mark-n-generate-event in one - * step, since sometimes we need to mark things up before its suitable - * to send out a change event. - */ - -/* CHECKME: This function modifies the Split without dirtying or - checking its parent. Is that correct? */ -void -xaccSplitDetermineGainStatus (Split *split) -{ - Split *other; - KvpValue *val; - - if (GAINS_STATUS_UNKNOWN != split->gains) return; - - other = xaccSplitGetCapGainsSplit (split); - if (other) - { - split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY; - split->gains_split = other; - return; - } - - val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source"); - if (!val) - { - // FIXME: This looks bogus. - other = xaccSplitGetOtherSplit (split); - if (other) - val = kvp_frame_get_slot (other->inst.kvp_data, "gains-source"); - split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY; - } else { - QofCollection *col; - col = qof_book_get_collection (split->inst.book, GNC_ID_SPLIT); - split->gains = GAINS_STATUS_GAINS; - other = (Split *) qof_collection_lookup_entity (col, - kvp_value_get_guid (val)); - split->gains_split = other; - } -} - -#define CHECK_GAINS_STATUS(s) \ - if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s); - -#define SET_GAINS_DIRTY(s,flg) do { \ - if (FALSE == (GAINS_STATUS_GAINS & s->gains)) { \ - s->gains |= flg; \ - } else { \ - if (s->gains_split) s->gains_split->gains |= flg; \ - } \ -} while (0) - -#define SET_GAINS_ADIRTY(s) SET_GAINS_DIRTY(s,GAINS_STATUS_ADIRTY); -#define SET_GAINS_A_VDIRTY(s) SET_GAINS_DIRTY(s,GAINS_STATUS_A_VDIRTY); -#define SET_GAINS_VDIRTY(s) SET_GAINS_DIRTY(s,GAINS_STATUS_VDIRTY); - -G_INLINE_FUNC void mark_split (Split *s); -void mark_split (Split *s) -{ - Account *account = s->acc; - - if (account && !account->inst.do_free) - { - account->balance_dirty = TRUE; - account->sort_dirty = TRUE; - } - - /* set dirty flag on lot too. */ - if (s->lot) s->lot->is_closed = -1; -} - - G_INLINE_FUNC void mark_trans (Transaction *trans); void mark_trans (Transaction *trans) { @@ -581,31 +96,6 @@ void mark_trans (Transaction *trans) } } -G_INLINE_FUNC void gen_event (const Split *split); -void gen_event (const Split *split) -{ - Account *account = split->acc; - Transaction *trans = split->parent; - GNCLot *lot = split->lot; - - if (account) - { - xaccGroupMarkNotSaved (account->parent); - gnc_engine_gen_event (&account->inst.entity, GNC_EVENT_MODIFY); - } - - if (trans) - { - gnc_engine_gen_event (&trans->inst.entity, GNC_EVENT_MODIFY); - } - - if (lot) - { - /* A change of value/amnt affects gains displat, etc. */ - gnc_engine_gen_event (&lot->inst.entity, GNC_EVENT_MODIFY); - } -} - G_INLINE_FUNC void gen_event_trans (Transaction *trans); void gen_event_trans (Transaction *trans) { @@ -631,235 +121,6 @@ void gen_event_trans (Transaction *trans) gnc_engine_gen_event (&trans->inst.entity, GNC_EVENT_MODIFY); } -/********************************************************************\ -\********************************************************************/ - -static inline int -get_currency_denom(const Split * s) -{ - if (!s) - { - return 0; - } - else if(!s->parent || !s->parent->common_currency) - { - return 100000; - } - else - { - return gnc_commodity_get_fraction (s->parent->common_currency); - } -} - -static inline int -get_commodity_denom(const Split * s) -{ - if (!s) - { - return 0; - } - else if (!s->acc) - { - return 100000; - } - else - { - return xaccAccountGetCommoditySCU(s->acc); - } -} - -/******************************************************************** - * xaccSplitGetSlots - ********************************************************************/ - -KvpFrame * -xaccSplitGetSlots (const Split * s) -{ - return qof_instance_get_slots(QOF_INSTANCE(s)); -} - -void -xaccSplitSetSlots_nc(Split *s, KvpFrame *frm) -{ - if (!s || !frm) return; - check_open (s->parent); - - qof_instance_set_slots(QOF_INSTANCE(s), frm); -} - -/********************************************************************\ -\********************************************************************/ - -void -DxaccSplitSetSharePriceAndAmount (Split *s, double price, double amt) -{ - if (!s) return; - ENTER (" "); - check_open (s->parent); - - s->amount = double_to_gnc_numeric(amt, get_commodity_denom(s), - GNC_HOW_RND_ROUND); - s->value = double_to_gnc_numeric(price * amt, get_currency_denom(s), - GNC_HOW_RND_ROUND); - - SET_GAINS_A_VDIRTY(s); - mark_split (s); -} - -void -xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt) -{ - if (!s) return; - ENTER (" "); - check_open (s->parent); - - s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), - GNC_HOW_RND_ROUND); - s->value = gnc_numeric_mul(s->amount, price, - get_currency_denom(s), GNC_HOW_RND_ROUND); - - SET_GAINS_A_VDIRTY(s); - mark_split (s); -} - -static void -qofSplitSetSharePrice (Split *split, gnc_numeric price) -{ - g_return_if_fail(split); - split->value = gnc_numeric_mul(xaccSplitGetAmount(split), - price, get_currency_denom(split), - GNC_HOW_RND_ROUND); -} - -void -xaccSplitSetSharePrice (Split *s, gnc_numeric price) -{ - if (!s) return; - ENTER (" "); - check_open (s->parent); - - s->value = gnc_numeric_mul(xaccSplitGetAmount(s), - price, get_currency_denom(s), - GNC_HOW_RND_ROUND); - - SET_GAINS_VDIRTY(s); - mark_split (s); -} - -void -DxaccSplitSetShareAmount (Split *s, double damt) -{ - gnc_numeric old_price, old_amt; - int commodity_denom = get_commodity_denom(s); - gnc_numeric amt = double_to_gnc_numeric(damt, commodity_denom, - GNC_HOW_RND_ROUND); - if (!s) return; - ENTER (" "); - check_open (s->parent); - - old_amt = xaccSplitGetAmount (s); - if (!gnc_numeric_zero_p(old_amt)) - { - old_price = gnc_numeric_div(xaccSplitGetValue (s), - old_amt, GNC_DENOM_AUTO, - GNC_HOW_DENOM_REDUCE); - } - else { - old_price = gnc_numeric_create(1, 1); - } - - s->amount = gnc_numeric_convert(amt, commodity_denom, - GNC_HOW_RND_NEVER); - s->value = gnc_numeric_mul(s->amount, old_price, - get_currency_denom(s), GNC_HOW_RND_ROUND); - - SET_GAINS_A_VDIRTY(s); - mark_split (s); -} - -static void -qofSplitSetAmount (Split *split, gnc_numeric amt) -{ - g_return_if_fail(split); - if (split->acc) - { - split->amount = gnc_numeric_convert(amt, - get_commodity_denom(split), GNC_HOW_RND_ROUND); - } - else { split->amount = amt; } -} - -/* The amount of the split in the _account's_ commodity. */ -void -xaccSplitSetAmount (Split *s, gnc_numeric amt) -{ - if (!s) return; - g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK); - ENTER ("(split=%p) old amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT - " new amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s, - s->amount.num, s->amount.denom, amt.num, amt.denom); - - check_open (s->parent); - if (s->acc) - s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), - GNC_HOW_RND_ROUND); - else - s->amount = amt; - - SET_GAINS_ADIRTY(s); - mark_split (s); - LEAVE(""); -} - -static void -qofSplitSetValue (Split *split, gnc_numeric amt) -{ - g_return_if_fail(split); - split->value = gnc_numeric_convert(amt, - get_currency_denom(split), GNC_HOW_RND_ROUND); -} - -/* The value of the split in the _transaction's_ currency. */ -void -xaccSplitSetValue (Split *s, gnc_numeric amt) -{ - if (!s) return; - - g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK); - ENTER ("(split=%p) old val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT - " new val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s, - s->value.num, s->value.denom, amt.num, amt.denom); - - check_open (s->parent); - s->value = gnc_numeric_convert(amt, get_currency_denom(s), - GNC_HOW_RND_ROUND); - - SET_GAINS_VDIRTY(s); - mark_split (s); - LEAVE (""); -} - -/********************************************************************\ -\********************************************************************/ - -gnc_numeric -xaccSplitGetBalance (const Split *s) -{ - return s ? s->balance : gnc_numeric_zero(); -} - -gnc_numeric -xaccSplitGetClearedBalance (const Split *s) -{ - return s ? s->cleared_balance : gnc_numeric_zero(); -} - -gnc_numeric -xaccSplitGetReconciledBalance (const Split *s) -{ - return s ? s->reconciled_balance : gnc_numeric_zero(); -} - /********************************************************************\ * xaccInitTransaction * Initialize a transaction structure @@ -1283,140 +544,6 @@ xaccTransLookup (const GUID *guid, QofBook *book) /********************************************************************\ \********************************************************************/ -void -xaccSplitSetBaseValue (Split *s, gnc_numeric value, - const gnc_commodity * base_currency) -{ - const gnc_commodity *currency; - const gnc_commodity *commodity; - - if (!s) return; - check_open (s->parent); - - if (!s->acc) - { - PERR ("split must have a parent account"); - return; - } - - currency = xaccTransGetCurrency (s->parent); - commodity = xaccAccountGetCommodity (s->acc); - - /* If the base_currency is the transaction's commodity ('currency'), - * set the value. If it's the account commodity, set the - * amount. If both, set both. */ - if (gnc_commodity_equiv(currency, base_currency)) { - if (gnc_commodity_equiv(commodity, base_currency)) { - s->amount = gnc_numeric_convert(value, - get_commodity_denom(s), - GNC_HOW_RND_NEVER); - } - s->value = gnc_numeric_convert(value, - get_currency_denom(s), - GNC_HOW_RND_NEVER); - } - else if (gnc_commodity_equiv(commodity, base_currency)) { - s->amount = gnc_numeric_convert(value, get_commodity_denom(s), - GNC_HOW_RND_NEVER); - } - else { - PERR ("inappropriate base currency %s " - "given split currency=%s and commodity=%s\n", - gnc_commodity_get_printname(base_currency), - gnc_commodity_get_printname(currency), - gnc_commodity_get_printname(commodity)); - return; - } - - SET_GAINS_A_VDIRTY(s); - mark_split (s); -} - -gnc_numeric -xaccSplitGetBaseValue (const Split *s, const gnc_commodity * base_currency) -{ - if (!s || !s->acc || !s->parent) return gnc_numeric_zero(); - - /* be more precise -- the value depends on the currency we want it - * expressed in. */ - if (gnc_commodity_equiv(xaccTransGetCurrency(s->parent), base_currency)) - return xaccSplitGetValue(s); - if (gnc_commodity_equiv(xaccAccountGetCommodity(s->acc), base_currency)) - return xaccSplitGetAmount(s); - - PERR ("inappropriate base currency %s " - "given split currency=%s and commodity=%s\n", - gnc_commodity_get_printname(base_currency), - gnc_commodity_get_printname(xaccTransGetCurrency (s->parent)), - gnc_commodity_get_printname(xaccAccountGetCommodity(s->acc))); - return gnc_numeric_zero(); -} - -/********************************************************************\ -\********************************************************************/ - -gnc_numeric -xaccSplitsComputeValue (GList *splits, Split * skip_me, - const gnc_commodity * base_currency) -{ - GList *node; - gnc_numeric value = gnc_numeric_zero(); - - g_return_val_if_fail (base_currency, value); - - ENTER (" currency=%s", gnc_commodity_get_mnemonic (base_currency)); - - for (node = splits; node; node = node->next) - { - Split *s = node->data; - const gnc_commodity *currency; - const gnc_commodity *commodity; - - if (s == skip_me) continue; - - /* value = gnc_numeric_add(value, xaccSplitGetBaseValue(s, base_currency), - GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); */ - - /* The split-editor often sends us 'temp' splits whose account - * hasn't yet been set. Be lenient, and assume an implied base - * currency. If there's a problem later, the scrub routines will - * pick it up. - */ - commodity = s->acc ? xaccAccountGetCommodity (s->acc) : base_currency; - currency = xaccTransGetCurrency (s->parent); - - - if (gnc_commodity_equiv(currency, base_currency)) - { - value = gnc_numeric_add(value, xaccSplitGetValue(s), - GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); - } - else if (gnc_commodity_equiv(commodity, base_currency)) - { - value = gnc_numeric_add(value, xaccSplitGetAmount(s), - GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); - } - else { - PERR ("inconsistent currencies\n" - "\tbase = '%s', curr='%s', sec='%s'\n", - gnc_commodity_get_printname(base_currency), - gnc_commodity_get_printname(currency), - gnc_commodity_get_printname(commodity)); - g_return_val_if_fail (FALSE, value); - } - } - - /* Note that just because the currencies are equivalent - * doesn't mean the denominators are the same! */ - value = gnc_numeric_convert(value, - gnc_commodity_get_fraction (base_currency), - GNC_HOW_RND_ROUND); - - LEAVE (" total=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, - value.num, value.denom); - return value; -} - gnc_numeric xaccTransGetImbalance (const Transaction * trans) { @@ -1529,54 +656,6 @@ xaccTransGetAccountConvRate(Transaction *txn, Account *acc) return gnc_numeric_create (100, 100); } -gnc_numeric -xaccSplitConvertAmount (Split *split, Account * account) -{ - gnc_commodity *acc_com, *to_commodity; - Transaction *txn; - gnc_numeric amount, value, convrate; - Account * split_acc; - - amount = xaccSplitGetAmount (split); - - /* If this split is attached to this account, OR */ - split_acc = xaccSplitGetAccount (split); - if (split_acc == account) - return amount; - - /* If split->account->commodity == to_commodity, return the amount */ - acc_com = xaccAccountGetCommodity (split_acc); - to_commodity = xaccAccountGetCommodity (account); - if (acc_com && gnc_commodity_equal (acc_com, to_commodity)) - return amount; - - /* Ok, this split is not for the viewed account, and the commodity - * does not match. So we need to do some conversion. - * - * First, we can cheat. If this transaction is balanced and has - * exactly two splits, then we can implicitly determine the exchange - * rate and just return the 'other' split amount. - */ - txn = xaccSplitGetParent (split); - if (txn && gnc_numeric_zero_p (xaccTransGetImbalance (txn))) { - Split *osplit = xaccSplitGetOtherSplit (split); - - if (osplit) - return gnc_numeric_neg (xaccSplitGetAmount (osplit)); - } - - /* ... otherwise, we need to compute the amount from the conversion - * rate into _this account_. So, find the split into this account, - * compute the conversion rate (based on amount/value), and then multiply - * this times the split value. - */ - convrate = xaccTransGetAccountConvRate(txn, account); - value = xaccSplitGetValue (split); - return gnc_numeric_mul (value, convrate, - gnc_commodity_get_fraction (to_commodity), - GNC_RND_ROUND); -} - gnc_numeric xaccTransGetAccountBalance (const Transaction *trans, const Account *account) @@ -1919,7 +998,7 @@ xaccTransRollbackEdit (Transaction *trans) * old splits, and insert all of the new splits, but this kind of brute * forcing will suck memory cycles. So instead we'll try the gentle * approach first. Note that even in the gentle approach, the - * CheckDateOrder routine could be cpu-cyle brutal, so it maybe + * CheckDateOrder routine could be cpu-cycle brutal, so it maybe * it could use some tuning. */ if (trans->inst.do_free) @@ -2122,7 +1201,7 @@ xaccTransGetVersion (const Transaction *trans) * not cause any rebalancing to occur. \********************************************************************/ -static void +void xaccTransRemoveSplit (Transaction *trans, const Split *split) { if (trans) { @@ -2134,54 +1213,6 @@ xaccTransRemoveSplit (Transaction *trans, const Split *split) /********************************************************************\ \********************************************************************/ -gboolean -xaccSplitDestroy (Split *split) -{ - Account *acc; - Transaction *trans; - - if (!split) return TRUE; - - acc = split->acc; - trans = split->parent; - if (acc && !acc->inst.do_free && xaccTransGetReadOnly (trans)) - return FALSE; - - check_open (trans); - - mark_split (split); - - if (trans) - { - if (g_list_find(trans->splits, split)) - xaccTransRemoveSplit (trans, split); - else - PERR ("split not in transaction"); - } - - /* Note: split is removed from lot when it's removed from account */ - xaccAccountRemoveSplit (acc, split); - - /* If we're shutting down then destroy the transaction, too, and - * don't recompute the balance. - */ - if (qof_book_shutting_down (split->parent->inst.book)) - /* This seems like an odd place to do this. Transactions have - to be opened to destroy them. */ - xaccTransDestroy (trans); - else - /* This seems a bit eager. Isn't there a lazy way to do this? */ - xaccAccountRecomputeBalance (acc); - - - gen_event (split); - xaccFreeSplit (split); - return TRUE; -} - -/********************************************************************\ -\********************************************************************/ - void xaccTransAppendSplit (Transaction *trans, Split *split) { @@ -2221,100 +1252,6 @@ xaccTransAppendSplit (Transaction *trans, Split *split) qof_commit_edit(QOF_INSTANCE(trans)); } -/********************************************************************\ - * sorting comparison function - * - * returns a negative value if transaction a is dated earlier than b, - * returns a positive value if transaction a is dated later than b, - * - * This function tries very hard to uniquely order all transactions. - * If two transactions occur on the same date, then their "num" fields - * are compared. If the num fields are identical, then the description - * fields are compared. If these are identical, then the memo fields - * are compared. Hopefully, there will not be any transactions that - * occur on the same day that have all three of these values identical. - * - * Note that being able to establish this kind of absolute order is - * important for some of the ledger display functions. - * - * Yes, this kind of code dependency is ugly, but the alternatives seem - * ugly too. - * -\********************************************************************/ - - -#define DATE_CMP(aaa,bbb,field) { \ - /* if dates differ, return */ \ - if ( (aaa->field.tv_sec) < \ - (bbb->field.tv_sec)) { \ - return -1; \ - } else \ - if ( (aaa->field.tv_sec) > \ - (bbb->field.tv_sec)) { \ - return +1; \ - } \ - \ - /* else, seconds match. check nanoseconds */ \ - if ( (aaa->field.tv_nsec) < \ - (bbb->field.tv_nsec)) { \ - return -1; \ - } else \ - if ( (aaa->field.tv_nsec) > \ - (bbb->field.tv_nsec)) { \ - return +1; \ - } \ -} - - - -int -xaccSplitDateOrder (const Split *sa, const Split *sb) -{ - int retval; - int comp; - char *da, *db; - - if (sa == sb) return 0; - /* nothing is always less than something */ - if (!sa && sb) return -1; - if (sa && !sb) return +1; - - retval = xaccTransOrder (sa->parent, sb->parent); - if (retval) return retval; - - /* otherwise, sort on memo strings */ - da = sa->memo; - db = sb->memo; - SAFE_STRCMP (da, db); - - /* otherwise, sort on action strings */ - da = sa->action; - db = sb->action; - SAFE_STRCMP (da, db); - - /* the reconciled flag ... */ - if (sa->reconciled < sb->reconciled) return -1; - if (sa->reconciled > sb->reconciled) return +1; - - /* compare amounts */ - comp = gnc_numeric_compare(xaccSplitGetAmount(sa), xaccSplitGetAmount (sb)); - if (comp < 0) return -1; - if (comp > 0) return +1; - - comp = gnc_numeric_compare(xaccSplitGetValue(sa), xaccSplitGetValue (sb)); - if (comp < 0) return -1; - if (comp > 0) return +1; - - /* if dates differ, return */ - DATE_CMP(sa,sb,date_reconciled); - - /* else, sort on guid - keeps sort stable. */ - retval = guid_compare(&(sa->inst.entity.guid), &(sb->inst.entity.guid)); - if (retval) return retval; - - return 0; -} - int xaccTransOrder (const Transaction *ta, const Transaction *tb) { @@ -2346,162 +1283,6 @@ xaccTransOrder (const Transaction *ta, const Transaction *tb) return guid_compare(&(ta->inst.entity.guid), &(tb->inst.entity.guid)); } -static gboolean -get_corr_account_split(const Split *sa, Split **retval) -{ - - Split *current_split; - GList *node; - gnc_numeric sa_value, current_value; - gboolean sa_value_positive, current_value_positive, seen_different = FALSE; - - *retval = NULL; - g_return_val_if_fail(sa, TRUE); - - sa_value = xaccSplitGetValue (sa); - sa_value_positive = gnc_numeric_positive_p(sa_value); - - for (node = sa->parent->splits; node; node = node->next) - { - current_split = node->data; - if (current_split == sa) continue; - - current_value = xaccSplitGetValue (current_split); - current_value_positive = gnc_numeric_positive_p(current_value); - if ((sa_value_positive && !current_value_positive) || - (!sa_value_positive && current_value_positive)) { - if (seen_different) { - *retval = NULL; - return TRUE; - } else { - *retval = current_split; - seen_different = TRUE; - } - } - } - return FALSE; -} - -/* TODO: these static consts can be shared. */ -const char * -xaccSplitGetCorrAccountName(const Split *sa) -{ - static const char *split_const = NULL; - Split *other_split; - - if (get_corr_account_split(sa, &other_split)) - { - if (!split_const) - split_const = _("-- Split Transaction --"); - - return split_const; - } - - return xaccAccountGetName(other_split->acc); -} - -char * -xaccSplitGetCorrAccountFullName(const Split *sa, char separator) -{ - static const char *split_const = NULL; - Split *other_split; - - if (get_corr_account_split(sa, &other_split)) - { - if (!split_const) - split_const = _("-- Split Transaction --"); - - return g_strdup(split_const); - } - return xaccAccountGetFullName(other_split->acc, separator); -} - -const char * -xaccSplitGetCorrAccountCode(const Split *sa) -{ - static const char *split_const = NULL; - Split *other_split; - - if (get_corr_account_split(sa, &other_split)) - { - if (!split_const) - split_const = _("Split"); - - return split_const; - } - return xaccAccountGetCode(other_split->acc); -} - -/* TODO: It's not too hard to make this function avoid the malloc/free. */ -int -xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb) -{ - Account *aa, *ab; - char *full_a, *full_b; - int retval; - if (!sa && !sb) return 0; - if (!sa) return -1; - if (!sb) return 1; - - aa = sa->acc; - ab = sb->acc; - full_a = xaccAccountGetFullName(aa, ':'); - full_b = xaccAccountGetFullName(ab, ':'); - /* for comparison purposes it doesn't matter what we use as a separator */ - retval = safe_strcmp(full_a, full_b); - g_free(full_a); - g_free(full_b); - return retval; -} - - -int -xaccSplitCompareAccountCodes(const Split *sa, const Split *sb) -{ - Account *aa, *ab; - if (!sa && !sb) return 0; - if (!sa) return -1; - if (!sb) return 1; - - aa = sa->acc; - ab = sb->acc; - - return safe_strcmp(xaccAccountGetName(aa), xaccAccountGetName(ab)); -} - -int -xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb) -{ - char *ca, *cb; - int retval; - if (!sa && !sb) return 0; - if (!sa) return -1; - if (!sb) return 1; - - /* doesn't matter what separator we use - * as long as they are the same - */ - - ca = xaccSplitGetCorrAccountFullName(sa, ':'); - cb = xaccSplitGetCorrAccountFullName(sb, ':'); - retval = safe_strcmp(ca, cb); - g_free(ca); - g_free(cb); - return retval; -} - -int -xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb) -{ - const char *ca, *cb; - if (!sa && !sb) return 0; - if (!sa) return -1; - if (!sb) return 1; - - ca = xaccSplitGetCorrAccountCode(sa); - cb = xaccSplitGetCorrAccountCode(sb); - return safe_strcmp(ca, cb); -} /********************************************************************\ \********************************************************************/ @@ -2895,242 +1676,6 @@ xaccTransHasSplitsInState (const Transaction *trans, const char state) /********************************************************************\ \********************************************************************/ -static void -qofSplitSetMemo (Split *split, const char* memo) -{ - gchar *tmp; - - g_return_if_fail(split); - tmp = gnc_string_cache_insert((gpointer) memo); - gnc_string_cache_remove(split->memo); - split->memo = tmp; -} - -void -xaccSplitSetMemo (Split *split, const char *memo) -{ - char * tmp; - if (!split || !memo) return; - check_open (split->parent); - - tmp = gnc_string_cache_insert((gpointer) memo); - gnc_string_cache_remove(split->memo); - split->memo = tmp; -} - -static void -qofSplitSetAction (Split *split, const char *actn) -{ - g_return_if_fail(split); - split->action = g_strdup(actn); -} - -void -xaccSplitSetAction (Split *split, const char *actn) -{ - char * tmp; - if (!split || !actn) return; - check_open (split->parent); - - tmp = gnc_string_cache_insert((gpointer) actn); - gnc_string_cache_remove(split->action); - split->action = tmp; -} - -static void -qofSplitSetReconcile (Split *split, char recn) -{ - g_return_if_fail(split); - switch (recn) - { - case NREC: - case CREC: - case YREC: - case FREC: - case VREC: - split->reconciled = recn; - mark_split (split); - xaccAccountRecomputeBalance (split->acc); - break; - default: - PERR("Bad reconciled flag"); - } -} - -void -xaccSplitSetReconcile (Split *split, char recn) -{ - if (!split || split->reconciled == recn) return; - check_open (split->parent); - - switch (recn) - { - case NREC: - case CREC: - case YREC: - case FREC: - case VREC: - split->reconciled = recn; - mark_split (split); - xaccAccountRecomputeBalance (split->acc); - break; - default: - PERR("Bad reconciled flag"); - } -} - -void -xaccSplitSetDateReconciledSecs (Split *split, time_t secs) -{ - if (!split) return; - check_open (split->parent); - - split->date_reconciled.tv_sec = secs; - split->date_reconciled.tv_nsec = 0; -} - -void -xaccSplitSetDateReconciledTS (Split *split, Timespec *ts) -{ - if (!split || !ts) return; - check_open (split->parent); - - split->date_reconciled = *ts; -} - -void -xaccSplitGetDateReconciledTS (const Split * split, Timespec *ts) -{ - if (!split || !ts) return; - *ts = (split->date_reconciled); -} - -Timespec -xaccSplitRetDateReconciledTS (const Split * split) -{ - Timespec ts = {0,0}; - return split ? split->date_reconciled : ts; -} - -/********************************************************************\ -\********************************************************************/ - -/* return the parent transaction of the split */ -Transaction * -xaccSplitGetParent (const Split *split) -{ - return split ? split->parent : NULL; -} - -GNCLot * -xaccSplitGetLot (const Split *split) -{ - return split ? split->lot : NULL; -} - -const char * -xaccSplitGetMemo (const Split *split) -{ - return split ? split->memo : NULL; -} - -const char * -xaccSplitGetAction (const Split *split) -{ - return split ? split->action : NULL; -} - -char -xaccSplitGetReconcile (const Split *split) -{ - return split ? split->reconciled : ' '; -} - - -gnc_numeric -xaccSplitGetAmount (const Split * split) -{ - return split ? split->amount : gnc_numeric_zero(); -} - -gnc_numeric -xaccSplitGetValue (const Split * split) -{ - return split ? split->value : gnc_numeric_zero(); -} - -gnc_numeric -xaccSplitGetSharePrice (const Split * split) -{ - gnc_numeric amt, val, price; - if (!split) return gnc_numeric_create(1, 1); - - - /* if amount == 0 and value == 0, then return 1. - * if amount == 0 and value != 0 then return 0. - * otherwise return value/amount - */ - - amt = xaccSplitGetAmount(split); - val = xaccSplitGetValue(split); - if (gnc_numeric_zero_p(amt)) - { - if (gnc_numeric_zero_p(val)) - return gnc_numeric_create(1, 1); - return gnc_numeric_create(0, 1); - } - price = gnc_numeric_div(val, amt, - GNC_DENOM_AUTO, - GNC_HOW_DENOM_SIGFIGS(PRICE_SIGFIGS) | - GNC_HOW_RND_ROUND); - - /* During random checks we can get some very weird prices. Let's - * handle some overflow and other error conditions by returning - * zero. But still print an error to let us know it happened. - */ - if (gnc_numeric_check(price)) - { - PERR("Computing share price failed (%d): [ %" G_GINT64_FORMAT " / %" - G_GINT64_FORMAT " ] / [ %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " ]", - gnc_numeric_check(price), val.num, val.denom, amt.num, amt.denom); - return gnc_numeric_create(0,1); - } - - return price; -} - -/********************************************************************\ -\********************************************************************/ - -QofBook * -xaccSplitGetBook (const Split *split) -{ - return qof_instance_get_book(QOF_INSTANCE(split)); -} - -const char * -xaccSplitGetType(const Split *s) -{ - char *split_type; - - if (!s) return NULL; - split_type = kvp_frame_get_string(s->inst.kvp_data, "split-type"); - return split_type ? split_type : "normal"; -} - -/* reconfigure a split to be a stock split - after this, you shouldn't - mess with the value, just the amount. */ -void -xaccSplitMakeStockSplit(Split *s) -{ - check_open (s->parent); - - s->value = gnc_numeric_zero(); - kvp_frame_set_str(s->inst.kvp_data, "split-type", "stock-split"); - SET_GAINS_VDIRTY(s); - mark_split(s); -} - /* ====================================================================== */ @@ -3187,66 +1732,6 @@ xaccGetAccountByFullName (const Transaction *trans, const char * name, return acc ? xaccGetPeerAccountFromFullName (acc, name, separator) : NULL; } -/********************************************************************\ -\********************************************************************/ -/* In the old world, the 'other split' was the other split of a - * transaction that contained only two splits. In the new world, - * a split may have been cut up between multiple lots, although - * in a conceptual sense, if lots hadn't been used, there would be - * only a pair. So we handle this conceptual case: we can still - * identify, unambiguously, the 'other' split when 'this' split - * as been cut up across lots. We do this by looking for the - * 'lot-split' keyword, which occurs only in cut-up splits. - */ - -Split * -xaccSplitGetOtherSplit (const Split *split) -{ - SplitList *node; - Transaction *trans; - int count; - Split *other = NULL; - KvpValue *sva; - - if (!split) return NULL; - trans = split->parent; - if (!trans) return NULL; - -#ifdef OLD_ALGO_HAS_ONLY_TWO_SPLITS - Split *s1, *s2; - if (g_list_length (trans->splits) != 2) return NULL; - - s1 = g_list_nth_data (trans->splits, 0); - s2 = g_list_nth_data (trans->splits, 1); - - if (s1 == split) return s2; - return s1; -#endif - - count = g_list_length (trans->splits); - sva = kvp_frame_get_slot (split->inst.kvp_data, "lot-split"); - if (!sva && (2 != count)) return NULL; - - for (node = trans->splits; node; node = node->next) - { - Split *s = node->data; - if (s == split) { --count; continue; } - if (kvp_frame_get_slot (s->inst.kvp_data, "lot-split")) { --count; continue; } - other = s; - } - return (1 == count) ? other : NULL; -} - -/********************************************************************\ -\********************************************************************/ - -gboolean -xaccIsPeerSplit (const Split *sa, const Split *sb) -{ - return (sa && sb && (sa->parent == sb->parent)); -} - - /********************************************************************\ \********************************************************************/ @@ -3255,7 +1740,6 @@ xaccTransVoid(Transaction *trans, const char *reason) { KvpFrame *frame; KvpValue *val; - gnc_numeric zero = gnc_numeric_zero(); GList *split_list; Timespec now; char iso8601_str[ISO_DATELENGTH+1] = ""; @@ -3278,17 +1762,8 @@ xaccTransVoid(Transaction *trans, const char *reason) for (split_list = trans->splits; split_list; split_list = split_list->next) { - Split * split = split_list->data; - frame = split->inst.kvp_data; - - kvp_frame_set_gnc_numeric(frame, void_former_amt_str, - xaccSplitGetAmount(split)); - kvp_frame_set_gnc_numeric(frame, void_former_val_str, - xaccSplitGetValue(split)); - - xaccSplitSetAmount (split, zero); - xaccSplitSetValue (split, zero); - xaccSplitSetReconcile(split, VREC); + Split * split = split_list->data; + xaccSplitVoid(split); } /* Dirtying taken care of by SetReadOnly */ @@ -3310,20 +1785,6 @@ xaccTransGetVoidReason(const Transaction *trans) return kvp_frame_get_string(trans->inst.kvp_data, void_reason_str); } -gnc_numeric -xaccSplitVoidFormerAmount(const Split *split) -{ - g_return_val_if_fail(split, gnc_numeric_zero()); - return kvp_frame_get_numeric(split->inst.kvp_data, void_former_amt_str); -} - -gnc_numeric -xaccSplitVoidFormerValue(const Split *split) -{ - g_return_val_if_fail(split, gnc_numeric_zero()); - return kvp_frame_get_numeric(split->inst.kvp_data, void_former_val_str); -} - Timespec xaccTransGetVoidTime(const Transaction *tr) { @@ -3341,9 +1802,7 @@ xaccTransUnvoid (Transaction *trans) { KvpFrame *frame; KvpValue *val; - gnc_numeric amt; GList *split_list; - Split *split; g_return_if_fail(trans); @@ -3361,20 +1820,8 @@ xaccTransUnvoid (Transaction *trans) for (split_list = trans->splits; split_list; split_list = split_list->next) { - split = split_list->data; - frame = split->inst.kvp_data; - - val = kvp_frame_get_slot(frame, void_former_amt_str); - amt = kvp_value_get_numeric(val); - xaccSplitSetAmount (split, amt); - kvp_frame_set_slot(frame, void_former_amt_str, NULL); - - val = kvp_frame_get_slot(frame, void_former_val_str); - amt = kvp_value_get_numeric(val); - xaccSplitSetValue (split, amt); - kvp_frame_set_slot(frame, void_former_val_str, NULL); - - xaccSplitSetReconcile(split, NREC); + Split *split = split_list->data; + xaccSplitUnvoid(split); } /* Dirtying taken care of by ClearReadOnly */ @@ -3414,132 +1861,6 @@ xaccTransReverse (Transaction *trans) /* QofObject function implementation */ /* Hook into the QofObject registry */ - -static QofObject split_object_def = { - interface_version: QOF_OBJECT_VERSION, - e_type: GNC_ID_SPLIT, - type_label: "Split", - create: (gpointer)xaccMallocSplit, - book_begin: NULL, - book_end: NULL, - is_dirty: NULL, - mark_clean: NULL, - foreach: qof_collection_foreach, - printable: (const char* (*)(gpointer)) xaccSplitGetMemo, - version_cmp: (int (*)(gpointer, gpointer)) qof_instance_version_cmp, -}; - -static gpointer -split_account_guid_getter (gpointer obj, const QofParam *p) -{ - Split *s = obj; - Account *acc; - - if (!s) return NULL; - acc = xaccSplitGetAccount (s); - if (!acc) return NULL; - return ((gpointer)xaccAccountGetGUID (acc)); -} - -static double /* internal use only */ -DxaccSplitGetShareAmount (const Split * split) -{ - return split ? gnc_numeric_to_double(xaccSplitGetAmount(split)) : 0.0; -} - -static gpointer -no_op (gpointer obj, const QofParam *p) -{ - return obj; -} - -static void -qofSplitSetParentTrans(Split *s, QofEntity *ent) -{ - Transaction *trans = (Transaction*)ent; - - g_return_if_fail(trans); - xaccTransAppendSplit(trans, s); -} - -static void -qofSplitSetAccount(Split *s, QofEntity *ent) -{ - Account *acc = (Account*)ent; - - g_return_if_fail(acc); - xaccAccountInsertSplit(acc, s); -} - -gboolean xaccSplitRegister (void) -{ - static const QofParam params[] = { - { SPLIT_DATE_RECONCILED, QOF_TYPE_DATE, - (QofAccessFunc)xaccSplitRetDateReconciledTS, - (QofSetterFunc)xaccSplitSetDateReconciledTS }, - - /* d-* are deprecated query params, should not be used in new - * queries, should be removed from old queries. */ - { "d-share-amount", QOF_TYPE_DOUBLE, - (QofAccessFunc)DxaccSplitGetShareAmount, NULL }, - { "d-share-int64", QOF_TYPE_INT64, - (QofAccessFunc)qof_entity_get_guid, NULL }, - { SPLIT_BALANCE, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitGetBalance, NULL }, - { SPLIT_CLEARED_BALANCE, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitGetClearedBalance, NULL }, - { SPLIT_RECONCILED_BALANCE, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitGetReconciledBalance, NULL }, - { SPLIT_MEMO, QOF_TYPE_STRING, - (QofAccessFunc)xaccSplitGetMemo, (QofSetterFunc)qofSplitSetMemo }, - { SPLIT_ACTION, QOF_TYPE_STRING, - (QofAccessFunc)xaccSplitGetAction, (QofSetterFunc)qofSplitSetAction }, - { SPLIT_RECONCILE, QOF_TYPE_CHAR, - (QofAccessFunc)xaccSplitGetReconcile, - (QofSetterFunc)qofSplitSetReconcile }, - { SPLIT_AMOUNT, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitGetAmount, (QofSetterFunc)qofSplitSetAmount }, - { SPLIT_SHARE_PRICE, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitGetSharePrice, - (QofSetterFunc)qofSplitSetSharePrice }, - { SPLIT_VALUE, QOF_TYPE_DEBCRED, - (QofAccessFunc)xaccSplitGetValue, (QofSetterFunc)qofSplitSetValue }, - { SPLIT_TYPE, QOF_TYPE_STRING, (QofAccessFunc)xaccSplitGetType, NULL }, - { SPLIT_VOIDED_AMOUNT, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitVoidFormerAmount, NULL }, - { SPLIT_VOIDED_VALUE, QOF_TYPE_NUMERIC, - (QofAccessFunc)xaccSplitVoidFormerValue, NULL }, - { SPLIT_LOT, GNC_ID_LOT, (QofAccessFunc)xaccSplitGetLot, NULL }, - { SPLIT_TRANS, GNC_ID_TRANS, - (QofAccessFunc)xaccSplitGetParent, - (QofSetterFunc)qofSplitSetParentTrans }, - { SPLIT_ACCOUNT, GNC_ID_ACCOUNT, - (QofAccessFunc)xaccSplitGetAccount, (QofSetterFunc)qofSplitSetAccount }, - { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, NULL }, -/* these are no-ops to register the parameter names (for sorting) but - they return an allocated object which getters cannot do. */ - { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, NULL }, - { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, NULL }, - { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, NULL }, - { SPLIT_KVP, QOF_TYPE_KVP, (QofAccessFunc)xaccSplitGetSlots, NULL }, - { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, NULL }, - { QOF_PARAM_GUID, QOF_TYPE_GUID, - (QofAccessFunc)qof_entity_get_guid, NULL }, - { NULL }, - }; - - qof_class_register (GNC_ID_SPLIT, (QofSortFunc)xaccSplitDateOrder, params); - qof_class_register (SPLIT_ACCT_FULLNAME, - (QofSortFunc)xaccSplitCompareAccountFullNames, NULL); - qof_class_register (SPLIT_CORR_ACCT_NAME, - (QofSortFunc)xaccSplitCompareOtherAccountFullNames, - NULL); - qof_class_register (SPLIT_CORR_ACCT_CODE, - (QofSortFunc)xaccSplitCompareOtherAccountCodes, NULL); - - return qof_object_register (&split_object_def); -} - static QofObject trans_object_def = { interface_version: QOF_OBJECT_VERSION, e_type: GNC_ID_TRANS, diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index a73c69fed6..b42ab2554f 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -90,21 +90,7 @@ Splits plus the value of all of its sub-Accounts. #include "gnc-commodity.h" #include "gnc-engine.h" - -/** @name Split Reconciled field values - - If you change these - be sure to change gnc-ui-util.c:gnc_get_reconciled_str() and - associated functions - -@{ -*/ -#define CREC 'c' /**< The Split has been cleared */ -#define YREC 'y' /**< The Split has been reconciled */ -#define FREC 'f' /**< frozen into accounting period */ -#define NREC 'n' /**< not reconciled or cleared */ -#define VREC 'v' /**< split is void */ -/** @} */ +#include "Split.h" /** @name Transaction Type field values @{ @@ -348,12 +334,6 @@ gnc_numeric xaccTransGetAccountAmount (const Transaction *trans, */ gnc_numeric xaccTransGetAccountConvRate(Transaction *txn, Account *acc); -/* Convert the amount/value of the Split for viewing in the account -- - * in particular we want to convert the Split to be in to_commodity. - * Returns the amount. - */ -gnc_numeric xaccSplitConvertAmount (Split *split, Account * account); - /** Get the account balance for the specified account after the last split in the specified transaction. */ gnc_numeric xaccTransGetAccountBalance (const Transaction *trans, @@ -449,362 +429,6 @@ void xaccTransGetDateDueTS (const Transaction *trans, Timespec *ts); /** @} */ -/*----------------------------------------------------------------------- - * Splits - *-----------------------------------------------------------------------*/ - -/** @name Split general getters/setters -@{ -*/ - -/** Constructor. */ -Split * xaccMallocSplit (QofBook *book); - -/* Reinit a previously malloc'd split. Split remains in the book it - was already in, and the QofEntity portions also remain unchanged. - It's basically the data elements that are reverted to default - values. */ -void xaccSplitReinit(Split * split); - -/** Destructor. - * - * The xaccSplitDestroy() method will update its parent account and - * transaction in a consistent manner, resulting in the complete - * unlinking of the split, and the freeing of its associated memory. - * The goal of this routine is to perform the removal and destruction - * of the split in an atomic fashion, with no chance of accidentally - * leaving the accounting structure out-of-balance or otherwise - * inconsistent. - * - * If the deletion of the split leaves the transaction with no splits, - * then the transaction will be marked for deletion. (It will not be - * deleted until the xaccTransCommitEdit() routine is called.) - * - * @return TRUE upon successful deletion of the split. FALSE when - * the parenting Transaction is a read-only one. - */ -gboolean xaccSplitDestroy (Split *split); - -/** Returns the book of this split, i.e. the entity where this split - * is stored. */ -QofBook * xaccSplitGetBook (const Split *split); - -/** Returns the account of this split, which was set through - * xaccAccountInsertSplit(). */ -Account * xaccSplitGetAccount (const Split *split); - -/** Returns the parent transaction of the split, which was set through - * xaccTransAppendSplit(). */ -Transaction * xaccSplitGetParent (const Split *split); - -/** Returns the pointer to the debited/credited Lot where this split - * belongs to, or NULL if it doesn't belong to any. */ -GNCLot * xaccSplitGetLot (const Split *split); - - -/** Returns the KvpFrame slots of this split for direct editing. - * - * Split slots are used to store arbitrary strings, numbers, and - * structures which aren't members of the transaction struct. See - * kvp_doc.txt for reserved slot names. - */ -KvpFrame *xaccSplitGetSlots(const Split *split); - -/** Set the KvpFrame slots of this split to the given frm by directly - * using the frm pointer (i.e. non-copying). */ -void xaccSplitSetSlots_nc(Split *s, KvpFrame *frm); - - -/** The memo is an arbitrary string associated with a split. It is - * intended to hold a short (zero to forty character) string that is - * displayed by the GUI along with this split. Users typically type - * in free form text from the GUI. */ -void xaccSplitSetMemo (Split *split, const char *memo); - -/** Returns the memo string. */ -const char * xaccSplitGetMemo (const Split *split); - -/** The Action is an arbitrary user-assigned string. - * The action field is an arbitrary user-assigned value. - * It is meant to be a very short (one to ten cahracter) string that - * signifies the "type" of this split, such as e.g. Buy, Sell, Div, - * Withdraw, Deposit, ATM, Check, etc. The idea is that this field - * can be used to create custom reports or graphs of data. */ -void xaccSplitSetAction (Split *split, const char *action); - -/** Returns the action string. */ -const char * xaccSplitGetAction (const Split *split); -/** @} */ - -/** @name Split Date getters/setters -@{ -*/ -/** Set the reconcile flag. The Reconcile flag is a single char, whose - * values are typically are 'n', 'y', 'c'. In Transaction.h, macros - * are defined for typical values (e.g. CREC, YREC). */ -void xaccSplitSetReconcile (Split *split, char reconciled_flag); -/** Returns the value of the reconcile flag. */ -char xaccSplitGetReconcile (const Split *split); - -/** Set the date on which this split was reconciled by specifying the - * time as time_t. */ -void xaccSplitSetDateReconciledSecs (Split *split, time_t time); -/** Set the date on which this split was reconciled by specifying the - * time as Timespec. */ -void xaccSplitSetDateReconciledTS (Split *split, Timespec *ts); -/** Get the date on which this split was reconciled by having it - * written into the Timespec that 'ts' is pointing to. */ -void xaccSplitGetDateReconciledTS (const Split *split, - Timespec *ts); -/** Returns the date (as Timespec) on which this split was reconciled. */ -Timespec xaccSplitRetDateReconciledTS (const Split *split); - -/** @} */ - - -/** @name Split amount getters/setters - * - * 'value' vs. 'amount' of a Split: The 'value' is the amount of the - * _transaction_ balancing commodity (i.e. currency) involved, - * 'amount' is the amount of the _account's_ commodity involved. -@{ -*/ - -/** The xaccSplitSetAmount() method sets the amount in the account's - * commodity that the split should have. - * - * The following four setter functions set the prices and amounts. - * All of the routines always maintain balance: that is, invoking any - * of them will cause other splits in the transaction to be modified - * so that the net value of the transaction is zero. - * - * IMPORTANT: The split should be parented by an account before - * any of these routines are invoked! This is because the actual - * setting of amounts/values requires SCU settings from the account. - * If these are not available, then amounts/values will be set to - * -1/0, which is an invalid value. I beleive this order dependency - * is a bug, but I'm too lazy to find, fix & test at the moment ... - * - * @note If you use this on a newly created transaction, make sure - * that the 'value' is also set so that it doesn't remain zero. - */ -void xaccSplitSetAmount (Split *split, gnc_numeric amount); - -/** Returns the amount of the split in the account's commodity. - * Note that for cap-gains splits, this is slaved to the transaction - * that is causing the gains to occur. - */ -gnc_numeric xaccSplitGetAmount (const Split * split); - -/** The xaccSplitSetValue() method sets the value of this split in the - * transaction's commodity. - * - * @note If you use this on a newly created transaction, make sure - * that the 'amount' is also set so that it doesn't remain zero. - */ -void xaccSplitSetValue (Split *split, gnc_numeric value); - -/** Returns the value of this split in the transaction's commodity. - * Note that for cap-gains splits, this is slaved to the transaction - * that is causing the gains to occur. -*/ -gnc_numeric xaccSplitGetValue (const Split * split); - -/** The xaccSplitSetSharePriceAndAmount() method will simultaneously - * update the share price and the number of shares. This is a utility - * routine that is equivalent to a xaccSplitSetSharePrice() followed - * by and xaccSplitSetAmount(), except that it incurs the processing - * overhead of balancing only once, instead of twice. */ -void xaccSplitSetSharePriceAndAmount (Split *split, - gnc_numeric price, - gnc_numeric amount); - -/** Returns the price of the split, that is, the value divided by the - * amount. If the amount is zero, returns a gnc_numeric of value - * one. */ -gnc_numeric xaccSplitGetSharePrice (const Split * split); - -/** Depending on the base_currency, set either the value or the amount - * of this split or both: If the base_currency is the transaction's - * commodity, set the value. If it is the account's commodity, set the - * amount. If both, set both. - * - * @note WATCH OUT: When using this function and the - * transaction's and account's commodities are different, the amount - * or the value will be left as zero. This might screw up the - * multi-currency handling code in the register. So please think twice - * whether you need this function -- using xaccSplitSetValue() - * together with xaccSplitSetAmount() is definitely the better and - * safer solution! - */ -void xaccSplitSetBaseValue (Split *split, gnc_numeric value, - const gnc_commodity * base_currency); - -/** Depending on the base_currency, return either the value or the - * amount of this split: If the base_curreny is the transaction's - * commodity, return the value. If it is the account's commodity, - * return the amount. If it is neither print a warning message and - * return gnc_numeric_zero(). - */ -gnc_numeric xaccSplitGetBaseValue (const Split *split, - const gnc_commodity * base_currency); - -/** Returns the running balance up to and including the indicated split. - * The balance is the currency-denominated balance. For accounts - * with non-unit share prices, it is correctly adjusted for - * share prices. - * - * Returns the running balance up to & including the indicated split. - */ -gnc_numeric xaccSplitGetBalance (const Split *split); - -/** - * The cleared-balance is the currency-denominated balance - * of all transactions that have been marked as cleared or reconciled. - * It is correctly adjusted for price fluctuations. - * - * Returns the running balance up to & including the indicated split. - */ -gnc_numeric xaccSplitGetClearedBalance (const Split *split); - -/** - * Returns the reconciled-balance of this split. The - * reconciled-balance is the currency-denominated balance of all - * transactions that have been marked as reconciled. - * - * Returns the running balance up to & including the indicated split. - */ -gnc_numeric xaccSplitGetReconciledBalance (const Split *split); - -/** @} */ - -/** @name Split utility functions -@{ -*/ - -/* Get a GList of unique transactions containing the given list of Splits. */ -GList *xaccSplitListGetUniqueTransactions(const GList *splits); - -/** Equality. - * - * @param sa First split to compare - * @param sb Second split to compare - * - * @param check_guids If TRUE, try a guid_equal() on the GUIDs of both - * splits if their pointers are not equal in the first place. - * - * @param check_balances If TRUE, compare balances between the two - * splits. Balances are recalculated whenever a split is added or - * removed from an account, so YMMV on whether this should be set. - * - * @param check_txn_splits If the pointers are not equal, but - * everything else so far is equal (including memo, amount, value, - * kvp_frame), then, when comparing the parenting transactions with - * xaccTransEqual(), set its argument check_splits to be TRUE. - */ -gboolean xaccSplitEqual(const Split *sa, const Split *sb, - gboolean check_guids, - gboolean check_balances, - gboolean check_txn_splits); - -/** The xaccSplitLookup() subroutine will return the - * split associated with the given id, or NULL - * if there is no such split. */ -Split * xaccSplitLookup (const GUID *guid, QofBook *book); -#define xaccSplitLookupDirect(g,b) xaccSplitLookup(&(g),b) - - -/** - * The xaccSplitGetOtherSplit() is a convenience routine that returns - * the other of a pair of splits. If there are more than two - * splits, it returns NULL. - */ -Split * xaccSplitGetOtherSplit (const Split *split); - -/** The xaccIsPeerSplit() is a convenience routine that returns TRUE - * (a non-zero value) if the two splits share a common parent - * transaction, else it returns FALSE (zero). - */ -gboolean xaccIsPeerSplit (const Split *split_1, const Split *split_2); - -/** Returns the split type, which is either the string "normal", or - * "stock-split" for a split from a stock split (pun intended? :-). */ -const char *xaccSplitGetType(const Split *s); - -/** Mark a split to be of type stock split - after this, you shouldn't - modify the value anymore, just the amount. */ -void xaccSplitMakeStockSplit(Split *s); - -/** - * The xaccSplitDateOrder(sa,sb) method is useful for sorting. - * if sa and sb have different transactions, return their xaccTransOrder - * return a negative value if split sa has a smaller currency-value than sb, - * return a positive value if split sa has a larger currency-value than sb, - * return a negative value if split sa has a smaller share-price than sb, - * return a positive value if split sa has a larger share-price than sb, - * then compares memo and action using the strcmp() - * c-library routine, returning what strcmp would return. - * Then it compares the reconciled flags, then the reconciled dates, - * Finally, it returns zero if all of the above match. - */ -int xaccSplitDateOrder (const Split *sa, const Split *sb); - - -/* - * These functions compare two splits by different criteria. - * - * These functions were added because converting strings to guile - * for comparisons in the transaction report is terribly inefficient. - * More may be added here in future if it turns out that other types - * of comparisons also induces guile slowdowns. - */ - -/** Compare two splits by full name of account. Returns similar to - * strcmp. */ -int xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb); -/** Compare two splits by code of account. Returns similar to - * strcmp. */ -int xaccSplitCompareAccountCodes(const Split *sa, const Split *sb); -/** Compare two splits by full name of the other account. Returns - * similar to strcmp. This function attempts to find the split on the - * other side of a transaction and compare on it. */ -int xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb); -/** Compare two splits by code of the other account. Returns similar - * to strcmp. This function attempts to find the split on the - * other side of a transaction and compare on it. */ -int xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb); - - -/** - * These functions take a split, get the corresponding split on the - * "other side" of the transaction, and extract either the name or code - * of that split, reverting to returning a constant "Split" if the - * transaction has more than one split on the "other side". These - * were added for the transaction report, and is in C because the code - * was already written in C for the above functions and duplication - * is silly. - */ - -char * xaccSplitGetCorrAccountFullName(const Split *sa, char seperator); -/** document me */ -const char * xaccSplitGetCorrAccountName(const Split *sa); -/** document me */ -const char * xaccSplitGetCorrAccountCode(const Split *sa); - -/** @} */ - - - -/** @name Split deprecated functions -@{ -*/ - -/** @deprecated The xaccSplitSetSharePrice() method sets the price of the - * split. DEPRECATED - set the value and amount instead. */ -void xaccSplitSetSharePrice (Split *split, gnc_numeric price); - -/** @} */ - /********************************************************************\ * Miscellaneous utility routines. @@ -875,24 +499,6 @@ gboolean xaccTransGetVoidStatus(const Transaction *transaction); */ char *xaccTransGetVoidReason(const Transaction *transaction); -/** Returns the original pre-void amount of a split. - * - * @param split The split in question. - * - * @return A gnc_numeric containing the original value of this split. - * Returns a gnc_numeric of zero upon error. - */ -gnc_numeric xaccSplitVoidFormerAmount(const Split *split); - -/** Returns the original pre-void value of a split. - * - * @param split The split in question. - * - * @return A gnc_numeric containing the original amount of this split. - * Returns a gnc_numeric of zero upon error. - */ -gnc_numeric xaccSplitVoidFormerValue(const Split *split); - /** Returns the time that a transaction was voided. * * @param tr The transaction in question. @@ -903,40 +509,6 @@ gnc_numeric xaccSplitVoidFormerValue(const Split *split); Timespec xaccTransGetVoidTime(const Transaction *tr); /** @} */ -/** @name Split Parameter names - - * Note, if you want to get the equivalent of "ACCT_MATCH_ALL" you - * need to create a search on the following parameter list: - * SPLIT->SPLIT_TRANS->TRANS_SPLITLIST->SPLIT_ACCOUNT_GUID. If you do - * this, you might want to use the ACCOUNT_MATCH_ALL_TYPE as the - * override so the gnome-search dialog displays the right type. - @{ -*/ -#define SPLIT_KVP "kvp" - -#define SPLIT_DATE_RECONCILED "date-reconciled" -#define SPLIT_BALANCE "balance" -#define SPLIT_CLEARED_BALANCE "cleared-balance" -#define SPLIT_RECONCILED_BALANCE "reconciled-balance" -#define SPLIT_MEMO "memo" -#define SPLIT_ACTION "action" -#define SPLIT_RECONCILE "reconcile-flag" -#define SPLIT_AMOUNT "amount" -#define SPLIT_SHARE_PRICE "share-price" -#define SPLIT_VALUE "value" -#define SPLIT_TYPE "type" -#define SPLIT_VOIDED_AMOUNT "voided-amount" -#define SPLIT_VOIDED_VALUE "voided-value" -#define SPLIT_LOT "lot" -#define SPLIT_TRANS "trans" -#define SPLIT_ACCOUNT "account" -#define SPLIT_ACCOUNT_GUID "account-guid" /**< for guid_match_all */ -/* used for SORTING ONLY */ -#define SPLIT_ACCT_FULLNAME "acct-fullname" -#define SPLIT_CORR_ACCT_NAME "corr-acct-fullname" -#define SPLIT_CORR_ACCT_CODE "corr-acct-code" -/** @} */ - /** @name Transaction Parameter names @{ */ @@ -958,10 +530,6 @@ Timespec xaccTransGetVoidTime(const Transaction *tr); #define RECONCILED_MATCH_TYPE "reconciled-match" -/** \deprecated */ -#define xaccSplitGetGUID(X) qof_entity_get_guid(QOF_ENTITY(X)) -/** \deprecated */ -#define xaccSplitReturnGUID(X) (X ? *(qof_entity_get_guid(QOF_ENTITY(X))) : *(guid_null())) /** \deprecated */ #define xaccTransGetBook(X) qof_instance_get_book (QOF_INSTANCE(X)) /** \deprecated */ diff --git a/src/engine/TransactionP.h b/src/engine/TransactionP.h index deeba214fb..840603749f 100644 --- a/src/engine/TransactionP.h +++ b/src/engine/TransactionP.h @@ -51,6 +51,7 @@ #include #include "gnc-engine.h" /* for typedefs */ +#include "SplitP.h" #include "qof.h" @@ -72,81 +73,6 @@ * A "split" is more commonly referred to as an "entry" in a "transaction". */ -/* Flags for handling cap-gains status */ -#define GAINS_STATUS_UNKNOWN 0xff -#define GAINS_STATUS_CLEAN 0x0 -#define GAINS_STATUS_GAINS 0x3 -#define GAINS_STATUS_DATE_DIRTY 0x10 -#define GAINS_STATUS_AMNT_DIRTY 0x20 -#define GAINS_STATUS_VALU_DIRTY 0x40 -#define GAINS_STATUS_LOT_DIRTY 0x80 -#define GAINS_STATUS_ADIRTY (GAINS_STATUS_AMNT_DIRTY|GAINS_STATUS_LOT_DIRTY) -#define GAINS_STATUS_VDIRTY (GAINS_STATUS_VALU_DIRTY) -#define GAINS_STATUS_A_VDIRTY (GAINS_STATUS_AMNT_DIRTY|GAINS_STATUS_VALU_DIRTY|GAINS_STATUS_LOT_DIRTY) - -struct split_s -{ - QofInstance inst; - - Account *acc; /* back-pointer to debited/credited account */ - - GNCLot *lot; /* back-pointer to debited/credited lot */ - - Transaction *parent; /* parent of split */ - - /* The memo field is an arbitrary user-assiged value. - * It is intended to hold a short (zero to forty character) string - * that is displayed by the GUI along with this split. - */ - char * memo; - - /* The action field is an arbitrary user-assigned value. - * It is meant to be a very short (one to ten character) string that - * signifies the "type" of this split, such as e.g. Buy, Sell, Div, - * Withdraw, Deposit, ATM, Check, etc. The idea is that this field - * can be used to create custom reports or graphs of data. - */ - char * action; /* Buy, Sell, Div, etc. */ - - Timespec date_reconciled; /* date split was reconciled */ - char reconciled; /* The reconciled field */ - - /* gains is a flag used to track the relationship between - * capital-gains splits. Depending on its value, this flag indicates - * if this split is the source of gains, if this split is a record - * of the gains, and if values are 'dirty' and need to be recomputed. - */ - unsigned char gains; - - /* 'gains_split' is a convenience pointer used to track down the - * other end of a cap-gains transaction pair. NULL if this split - * doesn't involve cap gains. - */ - Split *gains_split; - - /* 'value' is the quantity of the transaction balancing commodity - * (i.e. currency) involved, 'amount' is the amount of the account's - * commodity involved. */ - gnc_numeric value; - gnc_numeric amount; - - /* -------------------------------------------------------------- */ - /* Below follow some 'temporary' fields */ - - /* The various "balances" are the sum of all of the values of - * all the splits in the account, up to and including this split. - * These balances apply to a sorting order by date posted - * (not by date entered). */ - gnc_numeric balance; - gnc_numeric cleared_balance; - gnc_numeric reconciled_balance; - - /* -------------------------------------------------------------- */ - /* Backend private expansion data */ - guint32 idata; /* used by the sql backend for kvp management */ -}; - - struct transaction_s { QofInstance inst; /* glbally unique id */ @@ -201,21 +127,6 @@ struct transaction_s * call this on an existing transaction! */ #define xaccTransSetGUID(t,g) qof_entity_set_guid(QOF_ENTITY(t),g) -/* Set the split's GUID. This should only be done when reading - * a split from a datafile, or some other external source. Never - * call this on an existing split! */ -#define xaccSplitSetGUID(s,g) qof_entity_set_guid(QOF_ENTITY(s),g) - -/* The xaccFreeSplit() method simply frees all memory associated - * with the split. It does not verify that the split isn't - * referenced in some account. If the split is referenced by an - * account, then calling this method will leave the system in an - * inconsistent state. This *will* lead to crashes and hangs. - */ -void xaccFreeSplit (Split *split); /* frees memory */ - -Split * xaccSplitClone (const Split *s); - /* This routine makes a 'duplicate' of the indicated transaction. * This routine cannot be exposed publically since the duplicate * is wrong in many ways: it is not issued a unique guid, and thus @@ -227,11 +138,6 @@ Split * xaccSplitClone (const Split *s); */ Transaction * xaccDupeTransaction (const Transaction *t); -/* Compute the value of a list of splits in the given currency, - * excluding the skip_me split. */ -gnc_numeric xaccSplitsComputeValue (GList *splits, Split * skip_me, - const gnc_commodity * base_currency); - /* The xaccTransSet/GetVersion() routines set & get the version * numbers on this transaction. The version number is used to manage * multi-user updates. These routines are private because we don't @@ -240,8 +146,7 @@ gnc_numeric xaccSplitsComputeValue (GList *splits, Split * skip_me, void xaccTransSetVersion (Transaction*, gint32); gint32 xaccTransGetVersion (const Transaction*); -/* Code to register Split and Transaction types with the engine */ -gboolean xaccSplitRegister (void); +/* Code to register Transaction type with the engine */ gboolean xaccTransRegister (void); /* The xaccTransactionGetBackend() subroutine will find the @@ -264,22 +169,6 @@ QofBackend * xaccTransactionGetBackend (Transaction *trans); void xaccEnableDataScrubbing(void); void xaccDisableDataScrubbing(void); -/* The xaccSplitDetermineGainStatus() routine will analyze the - * the split, and try to set the internal status flags - * appropriately for the split. These flags indicate if the split - * represents cap gains, and if the gains value/amount needs to be - * recomputed. - */ -void xaccSplitDetermineGainStatus (Split *split); - -/* ---------------------------------------------------------------- */ -/* Depricated routines */ -void DxaccSplitSetSharePriceAndAmount (Split *split, - double price, - double amount); -void DxaccSplitSetShareAmount (Split *split, double amount); - - /** Set the KvpFrame slots of this transaction to the given frm by * * directly using the frm pointer (i.e. non-copying). * * XXX this is wrong, nedds to be replaced with a transactional thingy @@ -287,6 +176,9 @@ void DxaccSplitSetShareAmount (Split *split, double amount); * */ #define xaccTransSetSlots_nc(T,F) qof_instance_set_slots(QOF_INSTANCE(T),F) +void xaccTransRemoveSplit (Transaction *trans, const Split *split); +G_INLINE_FUNC void check_open (const Transaction *trans); + /*@}*/