You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/src/engine/Transaction.c

2365 lines
60 KiB

/********************************************************************\
* Transaction.c -- the transaction data structure *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1997-2000 Linas Vepstas <linas@linas.org> *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#include <assert.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "config.h"
#include "Account.h"
#include "AccountP.h"
#include "BackendP.h"
#include "GNCIdP.h"
#include "Group.h"
#include "Transaction.h"
#include "TransactionP.h"
#include "TransLog.h"
#include "util.h"
#include "date.h"
/*
* The "force_double_entry" flag determines how
* the splits in a transaction will be balanced.
*
* The following values have significance:
* 0 -- anything goes
* 1 -- The sum of all splits in a transaction will be
* forced to be zero, even if this requires the
* creation of additional splits. Note that a split
* whose value is zero (e.g. a stock price) can exist
* by itself. Otherwise, all splits must come in at
* least pairs.
* 2 -- splits without parents will be forced into a
* lost & found account. (Not implemented)
*/
int force_double_entry = 0;
/* bit-field flags for controlling transaction commits */
#define BEGIN_EDIT 0x1
#define DEFER_REBALANCE 0x2
#define BEING_DESTROYED 0x4
/* a very small number */
#define ZERO_THRESH_VALUE 0.0000000000001
/********************************************************************\
* Because I can't use C++ for this project, doesn't mean that I *
* can't pretend too! These functions perform actions on the *
* Transaction data structure, in order to encapsulate the *
* knowledge of the internals of the Transaction in one file. *
\********************************************************************/
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_ENGINE;
/********************************************************************\
* xaccInitSplit
* Initialize a splitaction structure
\********************************************************************/
void
xaccInitSplit(Split * split)
{
/* fill in some sane defaults */
split->acc = NULL;
split->parent = NULL;
split->action = strdup("");
split->memo = strdup("");
split->docref = strdup("");
split->reconciled = NREC;
split->damount = 0.0;
split->share_price = 1.0;
split->date_reconciled.tv_sec = 0;
split->date_reconciled.tv_nsec = 0;
split->balance = 0.0;
split->cleared_balance = 0.0;
split->reconciled_balance = 0.0;
split->share_balance = 0.0;
split->share_cleared_balance = 0.0;
split->share_reconciled_balance = 0.0;
split->cost_basis = 0.0;
split->ticket = 0;
split->kvp_data = NULL;
xaccGUIDNew(&split->guid);
xaccStoreEntity(split, &split->guid, GNC_ID_SPLIT);
}
/********************************************************************\
\********************************************************************/
Split *
xaccMallocSplit(void)
{
Split *split = (Split *)_malloc(sizeof(Split));
xaccInitSplit (split);
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.
*/
static Split *
xaccCloneSplit (Split *s)
{
Split *split = (Split *)_malloc(sizeof(Split));
split->acc = s ->acc;
split->parent = s->parent;
split->action = strdup(s->action);
split->memo = strdup(s->memo);
split->docref = strdup(s->docref);
split->reconciled = s->reconciled;
split->damount = s->damount;
split->share_price = s->share_price;
split->date_reconciled.tv_sec = s->date_reconciled.tv_sec;
split->date_reconciled.tv_nsec = s->date_reconciled.tv_nsec;
/* copy(!) the guid. The cloned split is *not* unique,
* is a sick twisted clone that holds 'undo' information. */
split->guid = s->guid;
/* 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;
* split->share_balance = s->share_balance;
* split->share_cleared_balance = s->share_cleared_balance;
* split->share_reconciled_balance = s->share_reconciled_balance;
*/
return (split);
}
/********************************************************************\
\********************************************************************/
void
xaccFreeSplit( Split *split )
{
if (!split) return;
if (split->memo) free (split->memo);
if (split->action) free (split->action);
if (split->docref) free (split->docref);
/* just in case someone looks up freed memory ... */
split->memo = 0x0;
split->action = 0x0;
split->docref = 0x0;
split->reconciled = NREC;
split->damount = 0.0;
split->share_price = 1.0;
split->parent = NULL;
split->acc = NULL;
split->date_reconciled.tv_sec = 0;
split->date_reconciled.tv_nsec = 0;
_free(split);
}
/********************************************************************
* xaccSplitGetSlot
********************************************************************/
kvp_value *
xaccSplitGetSlot(Split * split, const char * key) {
if(!split || !key || !(split->kvp_data)) {
return NULL;
}
else {
return kvp_frame_get_slot(split->kvp_data, key);
}
}
/********************************************************************
* xaccSplitSetSlot
********************************************************************/
void
xaccSplitSetSlot(Split * split, const char * key, const kvp_value * value) {
if(!split || !key || !value) {
return;
}
else {
if(!split->kvp_data) {
split->kvp_data = kvp_frame_new();
}
kvp_frame_set_slot(split->kvp_data, key, value);
}
}
/********************************************************************\
\********************************************************************/
const GUID *
xaccSplitGetGUID (Split *split)
{
if (!split) return xaccGUIDNULL();
return &split->guid;
}
/********************************************************************\
\********************************************************************/
void
xaccSplitSetGUID (Split *split, GUID *guid)
{
if (!split || !guid) return;
xaccRemoveEntity(&split->guid);
split->guid = *guid;
xaccStoreEntity(split, &split->guid, GNC_ID_SPLIT);
}
/********************************************************************\
\********************************************************************/
Split *
xaccSplitLookup (const GUID *guid)
{
if (!guid) return NULL;
return xaccLookupEntity(guid, GNC_ID_SPLIT);
}
/********************************************************************\
\********************************************************************/
void
xaccConfigSetForceDoubleEntry (int force)
{
force_double_entry = force;
}
int
xaccConfigGetForceDoubleEntry (void)
{
return (force_double_entry);
}
/********************************************************************\
\********************************************************************/
#define MARK_SPLIT(split) { \
Account *acc = (Account *) ((split)->acc); \
if (acc) acc->changed |= ACC_INVALIDATE_ALL; \
if (acc) xaccGroupMarkNotSaved(acc->parent); \
}
static void
MarkChanged (Transaction *trans)
{
if (trans->splits) {
int i=0;
while (trans->splits[i]) {
MARK_SPLIT (trans->splits[i]);
i++;
}
}
}
/********************************************************************\
\********************************************************************/
int
xaccCountSplits (Split **tarray)
{
Split *split;
int nsplit = 0;
if (!tarray) return 0;
split = tarray[0];
while (split) {
nsplit ++;
split = tarray[nsplit];
}
return nsplit;
}
/********************************************************************\
\********************************************************************/
void xaccSplitSetSharePriceAndAmount (Split *s, double price, double amt)
{
if (!s) return;
MARK_SPLIT(s);
s -> share_price = price;
s -> damount = amt;
/* force double entry to always balance */
xaccSplitRebalance (s);
}
void xaccSplitSetSharePrice (Split *s, double amt)
{
if (!s) return;
MARK_SPLIT(s);
s -> share_price = amt;
/* force double entry to always balance */
xaccSplitRebalance (s);
}
void xaccSplitSetShareAmount (Split *s, double amt)
{
if (!s) return;
MARK_SPLIT(s);
s -> damount = amt;
/* force double entry to always balance */
xaccSplitRebalance (s);
}
void xaccSplitSetValue (Split *s, double amt)
{
if (!s) return;
MARK_SPLIT(s);
/* remember, damount is actually share price */
s -> damount = amt / (s->share_price);
/* force double entry to always balance */
xaccSplitRebalance (s);
}
/********************************************************************\
\********************************************************************/
double xaccSplitGetBalance (Split *s)
{
if (!s) return 0.0;
return s->balance;
}
double xaccSplitGetClearedBalance (Split *s)
{
if (!s) return 0.0;
return s->cleared_balance;
}
double xaccSplitGetReconciledBalance (Split *s)
{
if (!s) return 0.0;
return s->reconciled_balance;
}
double xaccSplitGetShareBalance (Split *s)
{
if (!s) return 0.0;
return s->share_balance;
}
double xaccSplitGetShareClearedBalance (Split *s)
{
if (!s) return 0.0;
return s->share_cleared_balance;
}
double xaccSplitGetShareReconciledBalance (Split *s)
{
if (!s) return 0.0;
return s->share_reconciled_balance;
}
double xaccSplitGetCostBasis (Split *s)
{
if (!s) return 0.0;
xaccAccountRecomputeCostBasis (s->acc);
return s->cost_basis;
}
/********************************************************************\
* xaccInitTransaction
* Initialize a transaction structure
\********************************************************************/
void
xaccInitTransaction( Transaction * trans )
{
Split *split;
/* Fill in some sane defaults */
trans->num = strdup("");
trans->description = strdup("");
trans->docref = strdup("");
trans->splits = (Split **) _malloc (3* sizeof (Split *));
/* Create a single split only. As soon as the balance becomes
* non-zero, additional splits will get created.
*/
split = xaccMallocSplit ();
split->parent = trans;
trans->splits[0] = split;
trans->splits[1] = NULL;
trans->date_entered.tv_sec = 0;
trans->date_entered.tv_nsec = 0;
trans->date_posted.tv_sec = 0;
trans->date_posted.tv_nsec = 0;
trans->marker = 0;
trans->open = 0;
trans->orig = NULL;
trans->kvp_data = NULL;
xaccGUIDNew(&trans->guid);
xaccStoreEntity(trans, &trans->guid, GNC_ID_TRANS);
}
/********************************************************************\
\********************************************************************/
Transaction *
xaccMallocTransaction( void )
{
Transaction *trans = (Transaction *)_malloc(sizeof(Transaction));
xaccInitTransaction (trans);
return trans;
}
/********************************************************************\
\********************************************************************/
/* This routine is not exposed externally, since it does weird things,
* like not really owning the splits correctly, and other weirdnesses.
* This routine is prone to programmer snafu if not used correctly.
* It is used only by the edit-rollback code.
*/
static Transaction *
xaccCloneTransaction (Transaction *t)
{
Transaction *trans;
int n;
trans = (Transaction *)_malloc(sizeof(Transaction));
trans->num = strdup(t->num);
trans->description = strdup(t->description);
trans->docref = strdup(t->docref);
n=0; while (t->splits[n]) n++;
trans->splits = (Split **) _malloc ((n+1)* sizeof (Split *));
n=0;
while (t->splits[n]) {
trans->splits[n] = xaccCloneSplit (t->splits[n]);
n++;
}
trans->splits[n] = NULL;
trans->date_entered.tv_sec = t->date_entered.tv_sec;
trans->date_entered.tv_nsec = t->date_entered.tv_nsec;
trans->date_posted.tv_sec = t->date_posted.tv_sec;
trans->date_posted.tv_nsec = t->date_posted.tv_nsec;
trans->open = 0;
trans->orig = NULL;
/* copy(!) the guid. The cloned transaction is *not* unique,
* is a sick twisted clone that holds 'undo' information. */
trans->guid = t->guid;
return (trans);
}
/********************************************************************\
\********************************************************************/
void
xaccFreeTransaction( Transaction *trans )
{
int i;
Split *s;
if (!trans) return;
ENTER ("addr=%p\n", trans);
/* free up the destination splits */
if (trans->splits) {
i = 0;
s = trans->splits[i];
while (s) {
xaccFreeSplit (s);
i++;
s = trans->splits[i];
}
}
_free (trans->splits);
/* free up transaction strings */
if (trans->num) free (trans->num);
if (trans->description) free (trans->description);
if (trans->docref) free (trans->docref);
/* just in case someone looks up freed memory ... */
trans->num = 0x0;
trans->description = 0x0;
trans->docref = 0x0;
trans->date_entered.tv_sec = 0;
trans->date_entered.tv_nsec = 0;
trans->date_posted.tv_sec = 0;
trans->date_posted.tv_nsec = 0;
trans->open = 0;
if (trans->orig) {
xaccFreeTransaction (trans->orig);
trans->orig = NULL;
}
_free(trans);
LEAVE ("addr=%p\n", trans);
}
/********************************************************************
* xaccTransGetSlot
********************************************************************/
kvp_value *
xaccTransGetSlot(Transaction * trans, const char * key) {
if(!trans || !key || !(trans->kvp_data)) {
return NULL;
}
else {
return kvp_frame_get_slot(trans->kvp_data, key);
}
}
/********************************************************************
* xaccTransSetSlot
********************************************************************/
void
xaccTransSetSlot(Transaction * trans, const char * key,
const kvp_value * value) {
if(!trans || !key || !value) {
return;
}
else {
if(!trans->kvp_data) {
trans->kvp_data = kvp_frame_new();
}
kvp_frame_set_slot(trans->kvp_data, key, value);
}
}
/********************************************************************\
\********************************************************************/
const GUID *
xaccTransGetGUID (Transaction *trans)
{
if (!trans) return xaccGUIDNULL();
return &trans->guid;
}
/********************************************************************\
\********************************************************************/
void
xaccTransSetGUID (Transaction *trans, GUID *guid)
{
if (!trans || !guid) return;
xaccRemoveEntity(&trans->guid);
trans->guid = *guid;
xaccStoreEntity(trans, &trans->guid, GNC_ID_TRANS);
}
/********************************************************************\
\********************************************************************/
Transaction *
xaccTransLookup (const GUID *guid)
{
if (!guid) return NULL;
return xaccLookupEntity(guid, GNC_ID_TRANS);
}
/********************************************************************\
\********************************************************************/
/* compute a=b/c unless c is zero ... */
#define DEVIDE(a,b,c) { \
if (DEQEPS (0.0, (c), 1.0e-15)) { \
if (DEQEPS (0.0, (b), 1.0e-6)) { \
(a) = 0.0; \
} else { \
PERR ("zero share price but non-zero value\n"); \
(a) = (b)/(c); \
} \
} else { \
(a) = (b)/(c); \
} \
}
void
xaccSplitSetBaseValue (Split *s, double value, const char * base_currency)
{
int adjust_price = 0;
if (!s) return;
MARK_SPLIT(s);
/* Novice/casual users may not want or use the double entry
* features of this engine. So, in particular, there
* may be the occasional split without a parent account.
* Well, that's ok, we'll just go with the flow.
*/
if (!(s->acc)) {
if (force_double_entry) {
PERR ("split must have a parent\n");
assert (s->acc);
}
else {
/* if there's already a share-amount set, we need to respect
* that and adjust the price to make this balance. */
if (!DEQEPS(s->damount, 0.0, ZERO_THRESH_VALUE)) {
DEVIDE(s->share_price, value, s->damount);
}
else {
DEVIDE(s->damount, value, s->share_price);
}
return;
}
}
if(s->acc &&
s->acc->security &&
safe_strcmp(s->acc->security, s->acc->currency) &&
!DEQEPS(s->damount, 0.0, ZERO_THRESH_VALUE)) {
adjust_price = 1;
}
/* The value of a split depends on the currency we express the
* value in. This may or may not require a divide.
*/
if (!safe_strcmp(s->acc->currency, base_currency)) {
if (adjust_price) {
DEVIDE(s->share_price, value, s->damount);
}
else {
DEVIDE(s->damount, value, s->share_price);
}
}
else if (!safe_strcmp(s->acc->security, base_currency)) {
s->damount = value;
s->share_price = 1.0;
}
else if ((0x0==base_currency) && (0 == force_double_entry)) {
if (adjust_price) {
DEVIDE(s->share_price, value, s->damount);
}
else {
DEVIDE(s->damount, value, s->share_price);
}
}
else {
PERR ("inappropriate base currency %s "
"given split currency=%s and security=%s\n",
base_currency, s->acc->currency, s->acc->security);
return;
}
}
double
xaccSplitGetBaseValue (Split *s, const char * base_currency)
{
double value;
if (!s) return 0.0;
/* ahh -- users may not want or use the double entry
* features of this engine. So, in particular, there
* may be the occasional split without a parent account.
* Well, that's ok, we'll just go with the flow.
*/
if (!(s->acc)) {
if (force_double_entry) {
assert (s->acc);
} else {
value = s->damount * s->share_price;
return value;
}
}
/* be more precise -- the value depends on the currency
* we want it expressed in.
*/
if (!safe_strcmp(s->acc->currency, base_currency)) {
value = s->damount * s->share_price;
} else
if (!safe_strcmp(s->acc->security, base_currency)) {
value = s->damount;
} else
if ((NULL==base_currency) && (0 == force_double_entry)) {
value = s->damount * s->share_price;
} else
{
PERR ("inappropriate base currency %s "
"given split currency=%s and security=%s\n",
base_currency, s->acc->currency, s->acc->security);
return 0.0;
}
return value;
}
/********************************************************************\
\********************************************************************/
static double
ComputeValue (Split **sarray, Split * skip_me, const char * base_currency)
{
Split *s;
int i=0;
double value = 0.0;
s = sarray[0];
while (s) {
if (s != skip_me) {
/* ahh -- users may not want or use the double entry
* features of this engine. So, in particular, there
* may be the occasional split without a parent account.
* Well, that's ok, we'll just go with the flow.
*/
if (!(s->acc)) {
if (force_double_entry) {
assert (s->acc);
} else {
value += s->damount * s->share_price;
}
} else
if ((0x0 == base_currency) && (0 == force_double_entry)) {
value += s->damount * s->share_price;
} else {
/* OK, we've got a parent account, we've got currency,
* lets behave like professionals now, instead of the
* shenanigans above.
*/
if (!safe_strcmp(s->acc->currency, base_currency)) {
value += s->share_price * s->damount;
} else
if (!safe_strcmp(s->acc->security, base_currency)) {
value += s->damount;
} else {
PERR ("inconsistent currencies\n");
assert (0);
}
}
}
i++; s = sarray [i];
}
return value;
}
double
xaccTransGetImbalance (Transaction * trans)
{
const char * currency = xaccTransFindCommonCurrency (trans);
double imbal = ComputeValue (trans->splits, NULL, currency);
return imbal;
}
/********************************************************************\
\********************************************************************/
gncBoolean
xaccIsCommonCurrency(const char *currency_1, const char *security_1,
const char *currency_2, const char *security_2)
{
int c1c2, c1s2, s1c2, s1s2;
if ((currency_1 == NULL) || (currency_2 == NULL))
return GNC_F;
if ((security_1 != NULL) && (security_1[0] == 0x0))
security_1 = NULL;
if ((security_2 != NULL) && (security_2[0] == 0x0))
security_2 = NULL;
c1c2 = safe_strcmp(currency_1, currency_2);
c1s2 = safe_strcmp(currency_1, security_2);
if (security_1 != NULL)
{
s1c2 = safe_strcmp(security_1, currency_2);
s1s2 = safe_strcmp(security_1, security_2);
}
else /* no match */
{
s1c2 = 1;
s1s2 = 1;
}
return (c1c2 == 0) || (c1s2 == 0) || (s1c2 == 0) || (s1s2 == 0);
}
static const char *
FindCommonCurrency (Split **slist, const char * ra, const char * rb)
{
Split *s;
int i = 0;
if (!slist) return NULL;
if (rb && ('\0' == rb[0])) rb = NULL;
i=0; s = slist[0];
while (s) {
char *sa, *sb;
/* Novice/casual users may not want or use the double entry
* features of this engine. Because of this, there
* may be the occasional split without a parent account.
* Well, that's ok, we'll just go with the flow.
*/
if (force_double_entry) {
assert (s->acc);
} else
if (NULL == s->acc) {
i++; s=slist[i]; continue;
}
sa = s->acc->currency;
sb = s->acc->security;
if (sb && (0x0==sb[0])) sb = NULL;
if (ra && rb) {
int aa = safe_strcmp (ra,sa);
int ab = safe_strcmp (ra,sb);
int ba = safe_strcmp (rb,sa);
int bb = safe_strcmp (rb,sb);
if ( (!aa) && bb) rb = NULL;
else
if ( (!ab) && ba) rb = NULL;
else
if ( (!ba) && ab) ra = NULL;
else
if ( (!bb) && aa) ra = NULL;
else
if ( aa && bb && ab && ba ) { ra = NULL; rb = NULL; }
if (!ra) { ra = rb; rb = NULL; }
}
else
if (ra && !rb) {
int aa = safe_strcmp (ra,sa);
int ab = safe_strcmp (ra,sb);
if ( aa && ab ) ra = NULL;
}
if ((!ra) && (!rb)) return NULL;
i++; s = slist[i];
}
return (ra);
}
const char *
xaccTransFindCommonCurrency (Transaction *trans)
{
char *ra, *rb;
if (trans->splits == NULL) return NULL;
if (trans->splits[0] == NULL) return NULL;
if (trans->splits[0]->acc == NULL) return NULL;
ra = trans->splits[0]->acc->currency;
rb = trans->splits[0]->acc->security;
return FindCommonCurrency (trans->splits, ra, rb);
}
const char *
xaccTransIsCommonCurrency (Transaction *trans, const char * ra)
{
return FindCommonCurrency (trans->splits, ra, NULL);
}
/********************************************************************\
\********************************************************************/
/* hack alert -- the algorithm used in this rebalance routine
* is less than intuitive, and could use some write-up.
* Maybe it does indeed do the right thing, but that is
* not at all obvious.
*/
void
xaccTransRebalance (Transaction * trans)
{
xaccSplitRebalance (trans->splits[0]);
}
void
xaccSplitRebalance (Split *split)
{
Transaction *trans;
Split *s;
int i = 0;
double value = 0.0;
const char *base_currency = NULL;
trans = split->parent;
/* We might have gotten here if someone is manipulating
* a split that has not yet been inserted in a transaction.
* Rather than punishing them with an assert, lets just
* quietly return.
*/
if (!trans) return;
if (DEFER_REBALANCE & (trans->open)) return;
if (split->acc) {
char *ra, *rb;
if (ACC_DEFER_REBALANCE & (split->acc->open)) return;
assert (trans->splits);
assert (trans->splits[0]);
/* lets find out if we are dealing with multiple currencies,
* and which one(s) all of the splits have in common. */
ra = split->acc->currency;
rb = split->acc->security;
base_currency = FindCommonCurrency (trans->splits, ra, rb);
if (!base_currency) {
PERR ("no common split currencies\n");
s = trans->splits[0];
while (s) {
if (s->acc) {
PERR ("\taccount=%s currency=%s security=%s\n",
s->acc->accountName, s->acc->currency, s->acc->security);
} else {
PERR ("\t*** No parent account *** \n");
}
i++; s = trans->splits[i];
}
assert (0);
return;
}
} else {
assert (trans->splits);
assert (trans->splits[0]);
}
if (split == trans->splits[0]) {
/* The indicated split is the source split.
* Pick a destination split (by default,
* the first destination split), and force
* the total on it.
*/
s = trans->splits[1];
if (s) {
/* the new value of the destination split will be the result. */
value = ComputeValue (trans->splits, s, base_currency);
xaccSplitSetBaseValue (s, -value, base_currency);
MARK_SPLIT (s);
xaccAccountRecomputeBalance (s->acc);
} else {
/* There are no destination splits !!
* Either this is allowed, in which case
* we just blow it off, or its forbidden,
* in which case we force a balancing split
* to be created.
*
* Note that its ok to have a single split whose amount is zero.
* this is just a split that is recording a price, and nothing
* else. (i.e. it still obeys the rule that the sum of the
* value of all the splits is zero).
*/
if (force_double_entry) {
if (! (DEQ (0.0, split->damount))) {
value = split->share_price * split->damount;
/* malloc a new split, mirror it to the source split */
s = xaccMallocSplit ();
s->damount = -value;
free (s->memo);
s->memo = strdup (split->memo);
free (s->action);
s->action = strdup (split->action);
/* insert the new split into the transaction and
* the same account as the source split */
MARK_SPLIT (s);
xaccTransAppendSplit (trans, s);
xaccAccountInsertSplit (split->acc, s);
}
}
}
} else {
/* The indicated split is a destination split.
* Compute grand total of all destination splits,
* and force the source split to balance.
*/
s = trans->splits[0];
value = ComputeValue (trans->splits, s, base_currency);
xaccSplitSetBaseValue (s, -value, base_currency);
MARK_SPLIT (s);
xaccAccountRecomputeBalance (s->acc);
}
/* hack alert -- if the "force-double-entry" flag is set,
* we should check to make sure that every split belongs
* to some account. If any of them don't, force them
* into the current account. If there's not current account,
* force them into a lost & found account */
/* hack alert -- implement the above */
}
/********************************************************************\
\********************************************************************/
#define CHECK_OPEN(trans) { \
if (!trans->open) { \
PERR ("transaction %p not open for editing\n", trans); \
/* assert (trans->open); */ \
PERR ("\t%s:%d \n", __FILE__, __LINE__); \
/* return; */ \
} \
}
void
xaccTransBeginEdit (Transaction *trans, int defer)
{
char open;
Backend *be;
assert (trans);
open = trans->open;
trans->open = BEGIN_EDIT;
if (defer) trans->open |= DEFER_REBALANCE;
if (open & BEGIN_EDIT) return;
/* See if there's a backend. If there is, invoke it. */
be = xaccTransactionGetBackend (trans);
if (be && be->trans_begin_edit) {
(be->trans_begin_edit) (be, trans, defer);
}
xaccOpenLog ();
xaccTransWriteLog (trans, 'B');
/* make a clone of the transaction; we will use this
* in case we need to roll-back the edit.
*/
trans->orig = xaccCloneTransaction (trans);
}
void
xaccTransCommitEdit (Transaction *trans)
{
int i;
Split *split;
Account *acc;
Backend *be;
if (!trans) return;
ENTER ("trans addr=%p\n", trans);
CHECK_OPEN (trans);
/* At this point, we check to see if we have a valid transaction.
* As a result of editing, we could end up with a transaction that
* has no splits in it, in which case we delete the transaction and
* return.
*/
split = trans->splits[0];
if (!split || (trans->open & BEING_DESTROYED))
{
PINFO ("delete trans at addr=%p\n", trans);
/* Make a log in the journal before destruction. */
xaccTransWriteLog (trans, 'D');
xaccRemoveEntity(&trans->guid);
xaccFreeTransaction (trans);
return;
}
/* try to get the sorting order lined up according to
* when the user typed things in. */
if (0 == trans->date_entered.tv_sec) {
struct timeval tv;
gettimeofday (&tv, NULL);
trans->date_entered.tv_sec = tv.tv_sec;
trans->date_entered.tv_nsec = 1000 * tv.tv_usec;
}
/* Alternately the transaction may have only one split in
* it, in which case ... that's OK if and only if the split has no
* value (i.e. is only recording a price). Otherwise, a single
* split with a value can't possibly balance, thus violating the
* rules of double-entry, and that's way bogus. So create
* a matching opposite and place it either here (if force==1),
* or in some dummy account (if force==2).
*/
if ((1 == force_double_entry) &&
(NULL == trans->splits[1]) && (!(DEQ(0.0, split->damount))))
{
Split * s = xaccMallocSplit();
xaccSplitSetMemo (s, split->memo);
xaccSplitSetAction (s, split->action);
s->damount = -(split->damount);
s->share_price = split->share_price;
xaccTransAppendSplit (trans, s);
s->acc = NULL;
xaccAccountInsertSplit (split->acc, s);
}
trans->open &= ~DEFER_REBALANCE;
xaccTransRebalance (trans);
/* ------------------------------------------------- */
/* OK, at this point, we are done making sure that
* we've got a validly constructed transaction.
* Next, we send it off to the back-end, to see if the
* back-end will accept it.
*/
/* See if there's a backend. If there is, invoke it. */
be = xaccTransactionGetBackend (trans);
if (be && be->trans_commit_edit) {
int rc = 0;
rc = (be->trans_commit_edit) (be, trans, trans->orig);
if (rc) {
/* if the backend puked, then we must roll-back
* at this point, and let the user know that we failed.
*/
/* hack alert -- finish this */
}
}
/* ------------------------------------------------- */
/* Make sure all associated splits are in proper order
* in their accounts. */
i=0;
split = trans->splits[i];
while (split) {
acc = split ->acc;
xaccCheckDateOrder(acc, trans->splits[i]);
i++;
split = trans->splits[i];
}
/* Recompute the account balances. */
i=0;
split = trans->splits[i];
while (split) {
acc = split->acc;
xaccAccountRecomputeBalance (acc);
i++;
split = trans->splits[i];
}
trans->open = 0;
xaccTransWriteLog (trans, 'C');
/* Get rid of the copy we made. We won't be rolling back,
* so we don't need it any more. */
xaccFreeTransaction (trans->orig);
trans->orig = NULL;
LEAVE ("trans addr=%p\n", trans);
}
void
xaccTransRollbackEdit (Transaction *trans)
{
Transaction *orig;
Split *s, *so;
Account * acc;
int force_it=0, mismatch=0, i;
if (!trans) return;
CHECK_OPEN (trans);
ENTER ("trans addr=%p\n", trans);
/* copy the original values back in. */
orig = trans->orig;
/* If the transaction had been deleted before the rollback,
* the guid would have been unlisted. Restore that */
xaccStoreEntity(trans, &trans->guid, GNC_ID_TRANS);
#define PUT_BACK(val) { free(trans->val); trans->val=orig->val; orig->val=0x0; }
PUT_BACK (num);
PUT_BACK (description);
PUT_BACK (docref);
trans->date_entered.tv_sec = orig->date_entered.tv_sec;
trans->date_entered.tv_nsec = orig->date_entered.tv_nsec;
trans->date_posted.tv_sec = orig->date_posted.tv_sec;
trans->date_posted.tv_nsec = orig->date_posted.tv_nsec;
/* OK, we also have to restore the state of the splits. Of course,
* we could brute-force our way through this, and just clobber all of the
* 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
* it could use some tuning ...
*/
if (trans->open & BEING_DESTROYED) {
force_it = 1;
mismatch = 0;
} else {
i=0;
s = trans->splits[0];
so = orig->splits[0];
while (s && so) {
if (so->acc != s->acc) { force_it = 1; mismatch=i; break; }
#define HONKY_CAT(val) { free(s->val); s->val=so->val; so->val=0x0; }
HONKY_CAT (action);
HONKY_CAT (memo);
HONKY_CAT (docref);
s->reconciled = so->reconciled;
s->damount = so->damount;
s->share_price = so->share_price;
s->date_reconciled.tv_sec = so->date_reconciled.tv_sec;
s->date_reconciled.tv_nsec = so->date_reconciled.tv_nsec;
/* do NOT check date order until all of the other fields
* have been properly restored */
xaccCheckDateOrder (s->acc, s);
MARK_SPLIT (s);
xaccAccountRecomputeBalance (s->acc);
i++;
s = trans->splits[i];
so = orig->splits[i];
}
if (so != s) { force_it = 1; mismatch=i; }
}
/* OK, if force_it got set, we'll have to tough it out and brute-force
* the rest of the way. Clobber all the edited splits, add all new splits.
* Unfortunately, this can suck up CPU cycles in the Remove/Insert routines.
*/
if (force_it) {
i=0; s = trans->splits[i];
while (s && (i<mismatch)) {
xaccFreeSplit (orig->splits[i]);
orig->splits[i] = s;
i++;
s = trans->splits[i];
}
i=mismatch; s = trans->splits[i];
while (s) {
acc = s->acc;
MARK_SPLIT (s);
xaccAccountRemoveSplit (acc, s);
xaccAccountRecomputeBalance (acc);
xaccRemoveEntity(&s->guid);
xaccFreeSplit (s);
i++;
s = trans->splits[i];
}
_free (trans->splits);
trans->splits = orig->splits;
orig->splits = NULL;
i=mismatch; s = trans->splits[i];
while (s) {
acc = s->acc;
MARK_SPLIT (s);
xaccStoreEntity(s, &s->guid, GNC_ID_SPLIT);
xaccAccountInsertSplit (acc, s);
xaccAccountRecomputeBalance (acc);
i++;
s = trans->splits[i];
}
}
xaccTransWriteLog (trans, 'R');
xaccFreeTransaction (trans->orig);
trans->orig = NULL;
trans->open = 0;
LEAVE ("trans addr=%p\n", trans);
}
gncBoolean
xaccTransIsOpen (Transaction *trans)
{
if (!trans) return GNC_F;
return (0 != (trans->open & BEGIN_EDIT));
}
/********************************************************************\
\********************************************************************/
void
xaccTransDestroy (Transaction *trans)
{
int i;
Split *split;
Account *acc;
if (!trans) return;
CHECK_OPEN (trans);
trans->open |= BEING_DESTROYED;
xaccTransWriteLog (trans, 'D');
i=0;
split = trans->splits[i];
while (split) {
MARK_SPLIT (split);
acc = split ->acc;
xaccAccountRemoveSplit (acc, split);
xaccAccountRecomputeBalance (acc);
xaccRemoveEntity(&split->guid);
xaccFreeSplit (split);
trans->splits[i] = NULL;
i++;
split = trans->splits[i];
}
xaccRemoveEntity(&trans->guid);
/* the actual free is done with the commit call, else its rolled back */
/* xaccFreeTransaction (trans); don't do this here ... */
}
/********************************************************************\
\********************************************************************/
void
xaccSplitDestroy (Split *split)
{
Account *acc;
Transaction *trans;
int numsplits = 0;
int ismember = 0;
Split *s;
if (!split) return;
trans = split->parent;
assert (trans);
assert (trans->splits);
CHECK_OPEN (trans);
xaccRemoveEntity(&split->guid);
numsplits = 0;
s = trans->splits[0];
while (s) {
MARK_SPLIT(s);
if (s == split) ismember = 1;
numsplits ++;
s = trans->splits[numsplits];
}
assert (ismember);
/* If the account has three or more splits,
* merely unlink & free the split.
*
* Or if the account has only two splits,
* then this destroy will leave only one split.
* Don't rebalance, as this will goof up the
* value of the remaining split. (The rebalance
* happens later(?) during commit(?).)
*/
MARK_SPLIT (split);
xaccTransRemoveSplit (trans, split);
acc = split->acc;
xaccAccountRemoveSplit (acc, split);
xaccAccountRecomputeBalance (acc);
xaccFreeSplit (split);
if (2 < numsplits) {
xaccSplitRebalance (trans->splits[0]);
}
}
/********************************************************************\
\********************************************************************/
void
xaccTransAppendSplit (Transaction *trans, Split *split)
{
int i, num;
Split **oldarray;
Transaction *oldtrans;
if (!trans) return;
if (!split) return;
CHECK_OPEN (trans);
/* first, make sure that the split isn't already inserted
* elsewhere. If so, then remove it. */
oldtrans = split->parent;
if (oldtrans) {
xaccTransRemoveSplit (oldtrans, split);
xaccTransRebalance (oldtrans);
}
/* now, insert the split into the array */
split->parent = trans;
num = xaccCountSplits (trans->splits);
oldarray = trans->splits;
trans->splits = (Split **) _malloc ((num+2)*sizeof(Split *));
for (i=0; i<num; i++) {
(trans->splits)[i] = oldarray[i];
}
trans->splits[num] = split;
trans->splits[num+1] = NULL;
if (oldarray) _free (oldarray);
/* force double entry to always be consistent */
xaccSplitRebalance (split);
}
/********************************************************************\
* TransRemoveSplit is an engine private function and does not/should
* not cause any rebalancing to occur.
\********************************************************************/
void
xaccTransRemoveSplit (Transaction *trans, Split *split)
{
int i=0, n=0;
Split *s;
if (!split) return;
if (!trans) return;
split->parent = NULL;
s = trans->splits[0];
while (s) {
trans->splits[i] = trans->splits[n];
if (split == s) { i--; }
i++;
n++;
s = trans->splits[n];
}
trans->splits[i] = NULL;
}
/********************************************************************\
* 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 (Split **sa, Split **sb)
{
int retval;
char *da, *db;
if ( (*sa) && !(*sb) ) return -1;
if ( !(*sa) && (*sb) ) return +1;
if ( !(*sa) && !(*sb) ) return 0;
retval = xaccTransOrder ( ((Transaction **) &((*sa)->parent)),
((Transaction **) &((*sb)->parent)));
if (0 != 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);
/* otherwise, sort on docref string */
da = (*sa)->docref;
db = (*sb)->docref;
SAFE_STRCMP (da, db);
/* the reconciled flag ... */
if (((*sa)->reconciled) < ((*sb)->reconciled)) return -1;
if (((*sa)->reconciled) > ((*sb)->reconciled)) return +1;
/* compare amounts */
if ((((*sa)->damount)+EPS) < ((*sb)->damount)) return -1;
if ((((*sa)->damount)-EPS) > ((*sb)->damount)) return +1;
if ((((*sa)->share_price)+EPS) < ((*sb)->share_price)) return -1;
if ((((*sa)->share_price)-EPS) > ((*sb)->share_price)) return +1;
/* if dates differ, return */
DATE_CMP(sa,sb,date_reconciled);
return 0;
}
int
xaccSplitOrder (Split **sa, Split **sb)
{
char *da, *db;
char diff;
if ( (*sa) && !(*sb) ) return -1;
if ( !(*sa) && (*sb) ) return +1;
if ( !(*sa) && !(*sb) ) return 0;
/* compare amounts use parenthesis paranoia for multiplication, pointers etc. */
if ( ((((*sa)->damount)*((*sa)->share_price))+EPS) <
(((*sb)->damount)*((*sb)->share_price))) return -1;
if ( ((((*sa)->damount)*((*sa)->share_price))-EPS) >
(((*sb)->damount)*((*sb)->share_price))) return +1;
if ((((*sa)->share_price)+EPS) < ((*sb)->share_price)) return -1;
if ((((*sa)->share_price)-EPS) > ((*sb)->share_price)) return +1;
/* 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 ... */
diff = ((*sa)->reconciled) - ((*sb)->reconciled);
if (diff) return diff;
/* if dates differ, return */
DATE_CMP(sa,sb,date_reconciled);
/* otherwise, sort on docref string */
da = (*sa)->docref;
db = (*sb)->docref;
SAFE_STRCMP (da, db);
return 0;
}
int
xaccSplitMatch (Split **sa, Split **sb)
{
char *da, *db;
char diff;
if ( (*sa) && !(*sb) ) return -1;
if ( !(*sa) && (*sb) ) return +1;
if ( !(*sa) && !(*sb) ) return 0;
/* compare amounts use parenthesis paranoia for multiplication, pointers etc. */
if ( ((((*sa)->damount)*((*sa)->share_price))+EPS) <
(((*sb)->damount)*((*sb)->share_price))) return -1;
if ( ((((*sa)->damount)*((*sa)->share_price))-EPS) >
(((*sb)->damount)*((*sb)->share_price))) return +1;
if ((((*sa)->share_price)+EPS) < ((*sb)->share_price)) return -1;
if ((((*sa)->share_price)-EPS) > ((*sb)->share_price)) return +1;
/* 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);
/* If the reconciled flags are different, don't compare the
* dates, since we want to match splits with different reconciled
* values. But if they do match, the dates must match as well.
* Note that
*/
diff = ((*sa)->reconciled) - ((*sb)->reconciled);
if (!diff) {
DATE_CMP(sa,sb,date_reconciled);
}
/* otherwise, sort on docref string */
da = (*sa)->docref;
db = (*sb)->docref;
SAFE_STRCMP (da, db);
return 0;
}
int
xaccTransOrder (Transaction **ta, Transaction **tb)
{
char *da, *db;
if ( (*ta) && !(*tb) ) return -1;
if ( !(*ta) && (*tb) ) return +1;
if ( !(*ta) && !(*tb) ) return 0;
/* if dates differ, return */
DATE_CMP(ta,tb,date_posted);
/* otherwise, sort on number string */
da = (*ta)->num;
db = (*tb)->num;
if (da && db && *da && *db) {
SAFE_STRCMP (da, db);
}
/* if dates differ, return */
DATE_CMP(ta,tb,date_entered);
/* otherwise, sort on description string */
da = (*ta)->description;
db = (*tb)->description;
SAFE_STRCMP (da, db);
/* otherwise, sort on docref string */
da = (*ta)->docref;
db = (*tb)->docref;
SAFE_STRCMP (da, db);
return 0;
}
int
xaccTransMatch (Transaction **tap, Transaction **tbp)
{
int retval;
Transaction *ta, *tb;
Split *sa, *sb;
int na, nb;
/* first, do the basic comparison */
retval = xaccTransOrder (tap, tbp);
if (0 != retval) return retval;
ta = *tap;
tb = *tbp;
/* Now, start comparing splits */
na=0; while (ta->splits[na]) na++;
nb=0; while (tb->splits[nb]) nb++;
if (na-nb) return (na-nb);
/* Ugh, no we've got to compare individual splits. They do not necessarily
* have to be in identical order to match. So we have to cycle through them,
* without creating bogus matches.
*/
na=0; while ((sa=ta->splits[na])) { sa->ticket = -1; na++; }
nb=0; while ((sb=tb->splits[nb])) { sb->ticket = -1; nb++; }
na=0;
while ((sa=ta->splits[na])) {
if (-1 < sa->ticket) {na++; continue;}
nb=0;
while ((sb=tb->splits[nb])) {
if (-1 < sb->ticket) {nb++; continue;}
retval = xaccSplitMatch (&sa, &sb);
if ((0 == retval) && (sa->acc == sb->acc)) {
sb->ticket = na;
sa->ticket = nb;
break;
}
nb++;
}
if (-1 == sa->ticket) return -1;
na++;
}
nb=0;
while ((sb=tb->splits[nb])) {
if (-1 == sb->ticket) return +1;
nb++;
}
return 0;
}
/********************************************************************\
\********************************************************************/
int
xaccCountTransactions (Transaction **tarray)
{
Transaction *trans;
int ntrans = 0;
if (!tarray) return 0;
trans = tarray[0];
while (trans) {
ntrans ++;
trans = tarray[ntrans];
}
return ntrans;
}
/********************************************************************\
\********************************************************************/
void
xaccTransSetDateSecs (Transaction *trans, time_t secs)
{
if (!trans) return;
CHECK_OPEN (trans);
PINFO ("addr=%p set date to %lu %s \n",
trans, secs, ctime (&secs));
trans->date_posted.tv_sec = secs;
trans->date_posted.tv_nsec = 0;
/* Because the date has changed, we need to make sure that each of the
* splits is properly ordered in each of their accounts. We could do that
* here, simply by reinserting each split into its account. However, in
* some ways this is bad behaviour, and it seems much better/nicer to defer
* that until the commit phase, i.e. until the user has called the
* xaccTransCommitEdit() routine. So, for now, we are done.
*/
}
void
xaccTransSetDateSecsL (Transaction *trans, long long secs)
{
if (!trans) return;
CHECK_OPEN (trans);
DEBUGCMD ({
time_t sicko = secs;
PINFO ("addr=%p set date to %Lu %s \n",
trans, secs, ctime (&sicko));
})
trans->date_posted.tv_sec = secs;
trans->date_posted.tv_nsec = 0;
}
void
xaccTransSetDateEnteredSecs (Transaction *trans, time_t secs)
{
if (!trans) return;
CHECK_OPEN (trans);
trans->date_entered.tv_sec = secs;
trans->date_entered.tv_nsec = 0;
}
void
xaccTransSetDateTS (Transaction *trans, const Timespec *ts)
{
if (!trans || !ts) return;
CHECK_OPEN (trans);
DEBUGCMD ({
time_t sicko = ts->tv_sec;
PINFO ("addr=%p set date to %Lu %s \n",
trans, ts->tv_sec, ctime (&sicko));
})
trans->date_posted.tv_sec = ts->tv_sec;
trans->date_posted.tv_nsec = ts->tv_nsec;
}
void
xaccTransSetDateEnteredTS (Transaction *trans, const Timespec *ts)
{
if (!trans || !ts) return;
CHECK_OPEN (trans);
trans->date_entered.tv_sec = ts->tv_sec;
trans->date_entered.tv_nsec = ts->tv_nsec;
}
#define THIRTY_TWO_YEARS 0x3c30fc00LL
Timespec
gnc_dmy2timespec(int day, int month, int year) {
Timespec result;
struct tm date;
long long secs = 0;
long long era = 0;
year -= 1900;
/* make a crude attempt to deal with dates outside the
* range of Dec 1901 to Jan 2038. Note the we screw up
* centenial leap years here ... so hack alert --
*/
if ((2 > year) || (136 < year))
{
era = year / 32;
year %= 32;
if (0 > year) { year += 32; era -= 1; }
}
date.tm_year = year;
date.tm_mon = month - 1;
date.tm_mday = day;
date.tm_hour = 11;
date.tm_min = 0;
date.tm_sec = 0;
/* compute number of seconds */
secs = mktime (&date);
secs += era * THIRTY_TWO_YEARS;
result.tv_sec = secs;
result.tv_nsec = 0;
return(result);
}
void
xaccTransSetDate (Transaction *trans, int day, int mon, int year)
{
Timespec ts = gnc_dmy2timespec(day, mon, year);
xaccTransSetDateTS (trans, &ts);
}
void
xaccTransSetDateToday (Transaction *trans)
{
struct timeval tv;
if (!trans) return;
CHECK_OPEN (trans);
gettimeofday (&tv, NULL);
trans->date_posted.tv_sec = tv.tv_sec;
trans->date_posted.tv_nsec = 1000 * tv.tv_usec;
PINFO ("addr=%p set date to %lu %s \n",
trans, tv.tv_sec, ctime ((time_t *)&tv.tv_sec));
}
/********************************************************************\
\********************************************************************/
void
xaccTransSetNum (Transaction *trans, const char *xnum)
{
char * tmp;
if (!trans || !xnum) return;
CHECK_OPEN (trans);
tmp = strdup (xnum);
if (trans->num) free (trans->num);
trans->num = tmp;
MarkChanged (trans);
}
void
xaccTransSetDescription (Transaction *trans, const char *desc)
{
char * tmp;
if (!trans || !desc) return;
CHECK_OPEN (trans);
tmp = strdup (desc);
if (trans->description) free (trans->description);
trans->description = tmp;
MarkChanged (trans);
}
void
xaccTransSetDocref (Transaction *trans, const char *docs)
{
char * tmp;
if (!trans || !docs) return;
CHECK_OPEN (trans);
tmp = strdup (docs);
if (trans->docref) free (trans->docref);
trans->docref = tmp;
MarkChanged (trans);
}
#define SET_TRANS_FIELD(trans,field,value) \
{ \
char * tmp; \
if (!trans) return; \
CHECK_OPEN (trans); \
\
/* the engine *must* always be internally consistent */ \
assert (trans->splits); \
assert (trans->splits[0]); \
\
/* there must be two splits if value of one non-zero */ \
if (force_double_entry) { \
if (! (DEQ (0.0, trans->splits[0]->damount))) { \
assert (trans->splits[1]); \
} \
} \
\
tmp = strdup (value); \
free (trans->splits[0]->field); \
trans->splits[0]->field = tmp; \
MARK_SPLIT (trans->splits[0]); \
\
/* If there are just two splits, then keep them in sync. */ \
if (0x0 != trans->splits[1]) { \
if (0x0 == trans->splits[2]) { \
free (trans->splits[1]->field); \
trans->splits[1]->field = strdup (tmp); \
MARK_SPLIT (trans->splits[1]); \
} \
} \
}
void
xaccTransSetMemo (Transaction *trans, const char *mimeo)
{
SET_TRANS_FIELD (trans, memo, mimeo);
}
void
xaccTransSetAction (Transaction *trans, const char *actn)
{
SET_TRANS_FIELD (trans, action, actn);
}
/********************************************************************\
\********************************************************************/
Split *
xaccTransGetSplit (Transaction *trans, int i)
{
if (!trans) return NULL;
if (0 > i) return NULL;
/* hack alert - should check if i > sizeof array */
if (trans->splits) {
return (trans->splits[i]);
}
return NULL;
}
const char *
xaccTransGetNum (Transaction *trans)
{
if (!trans) return NULL;
return (trans->num);
}
const char *
xaccTransGetDescription (Transaction *trans)
{
if (!trans) return NULL;
return (trans->description);
}
const char *
xaccTransGetDocref (Transaction *trans)
{
if (!trans) return NULL;
return (trans->docref);
}
time_t
xaccTransGetDate (Transaction *trans)
{
if (!trans) return 0;
return (trans->date_posted.tv_sec);
}
long long
xaccTransGetDateL (Transaction *trans)
{
if (!trans) return 0;
return (trans->date_posted.tv_sec);
}
void
xaccTransGetDateTS (Transaction *trans, Timespec *ts)
{
if (!trans || !ts) return;
*ts = (trans->date_posted);
}
void
xaccTransGetDateEnteredTS (Transaction *trans, Timespec *ts)
{
if (!trans || !ts) return;
*ts = (trans->date_entered);
}
int
xaccTransCountSplits (Transaction *trans)
{
if (!trans) return 0;
return (xaccCountSplits (trans->splits));
}
/********************************************************************\
\********************************************************************/
void
xaccSplitSetMemo (Split *split, const char *memo)
{
char * tmp;
if (!split || !memo) return;
tmp = strdup (memo);
if (split->memo) free (split->memo);
split->memo = tmp;
MARK_SPLIT (split);
}
void
xaccSplitSetAction (Split *split, const char *actn)
{
char * tmp;
if (!split || !actn) return;
tmp = strdup (actn);
if (split->action) free (split->action);
split->action = tmp;
MARK_SPLIT (split);
}
void
xaccSplitSetDocref (Split *split, const char *docs)
{
char * tmp;
if (!split || !docs) return;
tmp = strdup (docs);
if (split->docref) free (split->docref);
split->docref = tmp;
MARK_SPLIT (split);
}
void
xaccSplitSetReconcile (Split *split, char recn)
{
if (!split) return;
switch (recn)
{
case NREC:
case CREC:
case YREC:
case FREC:
break;
default:
PERR("Bad reconciled flag");
return;
}
split->reconciled = recn;
MARK_SPLIT (split);
xaccAccountRecomputeBalance (split->acc);
}
void
xaccSplitSetDateReconciledSecs (Split *split, time_t secs)
{
if (!split) return;
MARK_SPLIT (split);
split->date_reconciled.tv_sec = secs;
split->date_reconciled.tv_nsec = 0;
}
void
xaccSplitSetDateReconciledTS (Split *split, Timespec *ts)
{
if (!split || !ts) return;
MARK_SPLIT (split);
split->date_reconciled = *ts;
}
void
xaccSplitGetDateReconciledTS (Split * split, Timespec *ts)
{
if (!split || !ts) return;
*ts = (split->date_reconciled);
}
/********************************************************************\
\********************************************************************/
/* return the parent transaction of the split */
Transaction *
xaccSplitGetParent (Split *split)
{
if (!split) return NULL;
return (split->parent);
}
Account *
xaccSplitGetAccount (Split *split)
{
if (!split) return NULL;
return (split->acc);
}
const char *
xaccSplitGetMemo (Split *split)
{
if (!split) return NULL;
return (split->memo);
}
const char *
xaccSplitGetAction (Split *split)
{
if (!split) return NULL;
return (split->action);
}
const char *
xaccSplitGetDocref (Split *split)
{
if (!split) return NULL;
return (split->docref);
}
char
xaccSplitGetReconcile (Split *split)
{
if (!split) return ' ';
return (split->reconciled);
}
double
xaccSplitGetShareAmount (Split * split)
{
if (!split) return 0.0;
return (split->damount);
}
double
xaccSplitGetValue (Split * split)
{
if (!split) return 0.0;
return ((split->damount) * (split->share_price));
}
double
xaccSplitGetSharePrice (Split * split)
{
if (!split) return 1.0;
return (split->share_price);
}
/********************************************************************\
\********************************************************************/
Account *
xaccGetAccountByName (Transaction *trans, const char * name)
{
Split *s;
Account *acc = NULL;
int i;
if (!trans) return NULL;
if (!name) return NULL;
/* walk through the splits, looking for one, any one, that has a
* parent account */
i = 0;
s = trans->splits[0];
while (s) {
acc = s->acc;
if (acc) break;
i++;
s = trans->splits[i];
}
if (!acc) return 0x0;
acc = xaccGetPeerAccountFromName (acc, name);
return acc;
}
/********************************************************************\
\********************************************************************/
Account *
xaccGetAccountByFullName (Transaction *trans, const char * name,
const char separator)
{
Split *s;
Account *acc = NULL;
int i;
if (!trans) return NULL;
if (!name) return NULL;
/* walk through the splits, looking for one, any one, that has a
* parent account */
i = 0;
s = trans->splits[0];
while (s) {
acc = s->acc;
if (acc) break;
i++;
s = trans->splits[i];
}
if (!acc) return NULL;
acc = xaccGetPeerAccountFromFullName (acc, name, separator);
return acc;
}
/********************************************************************\
\********************************************************************/
Split *
xaccGetOtherSplit (Split *split)
{
Transaction *trans;
if (!split) return NULL;
trans = split->parent;
/* if more than two splits, return NULL */
if ((trans->splits[1]) && (trans->splits[2])) return NULL;
if (split == trans->splits[0]) return (trans->splits[1]);
if (split == trans->splits[1]) return (trans->splits[0]);
return NULL; /* never reached, in theory */
}
/********************************************************************\
\********************************************************************/
int
xaccIsPeerSplit (Split *sa, Split *sb)
{
Transaction *ta, *tb;
if (!sa || !sb) return 0;
ta = sa->parent;
tb = sb->parent;
if (ta == tb) return 1;
return 0;
}
/********************************************************************\
\********************************************************************/
Split *
IthSplit (Split **list, int i)
{
if (!list || 0 > i) return NULL;
return list[i];
}
Transaction *
IthTransaction (Transaction **list, int i)
{
if (!list || 0 > i) return NULL;
return list[i];
}
/************************ END OF ************************************\
\************************* FILE *************************************/