mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-16 18:25:11 -06:00
Bug #537476: Implement currency trading accounts optionally, to be enabled per-book.
Patch by Mike Alexander: This patch implements trading accounts somewhat as described in Peter Selinger's document at <http://www.mathstat.dal.ca/~selinger/accounting/gnucash.html>. Although he describes it as a multiple currency problem, it really applies to any transactions involving multiple commodities (for example buying or selling a stock) Hence I've called the trading accounts "commodity exchange accounts" which seems more descriptive. In summary these patches add an option to use commodity exchange accounts and if it is on a transaction must be balanced both in value (in the transaction currency) and in each commodity or currency used in any split in the transaction. If a transaction only contains splits in the transaction currency then this is the same rule as Gnucash has always enforced. In this patch, the option to use trading accounts has been moved from Edit->Preferences to File->Properties and is now associated with the active book instead of being a global option. If you have set the global value on in a previous version you will need to set it on again in each file for which you want trading accounts, the previous global setting will be ignored. A more detailed list of changes follows: 1. Added a "Use commodity exchange accounts" per-book option. 2. Added gnc_monetary and MonetaryList data types. 3. Renamed xaccTransGetImbalance to xaccTransGetImbalanceValue and added a new xaccTransGetImbalance that returns a MonetaryList. Also added xaccTransIsBalanced to see if the transaction is balanced without returning a GList that needs to be freed. It calls both xaccTransGetImbalance and xaccTransGetImbalanceValue since a transaction may be unbalanced with regard to either without being unbalanced with regard to the other. 4. Changed gnc_split_register_get_debcred_bg_color to use xaccTransIsBalanced. 5. Changed gnc_split_register_balance_trans to not offer to adjust an existing split if there imbalances in multiple currencies. Because of bugs in the register code this is rarely called. 6. Changed importers to use xaccTransGetImbalanceValue to check for imbalance, assuming that they won't create multiple currency trasactions. 7. Changed xaccTransScrubImbalance to create a balancing split for each imbalanced commodity in the transaction. Also balances the transaction value. The commodity balancing splits go into accounts in the hierarchy Trading:NAMESPACE:COMMODITY. The value balancing splits go into Imbalance-CURRENCY as before. 8. Changed xaccSplitConvertAmount to use xaccTransIsBalanced instead of xaccTransGetImbalance. 9. Changed gnc_split_register_get_debcred_entry to sometimes use the split amount instead of value if using currency accounts. If the register is a stock register (i.e., shows shares and prices), it uses the value if the split is in the register commodity (i.e. is for the stock) and the amount otherwise. It shows the currency symbol unless the commodity is the default currency. If the register is not a stock register it always uses the amount and shows the currency symbol if the split is not in the register commodity. Also changed it to not return a value for a null split unless the transaction is unbalanced in exactly one currency. This is what goes in a blank split as the proposed value. 10. Changed refresh_model_row to use xaccTransGetImbalanceValue to get the imbalance, assuming that importers don't create transactions in multiple currencies. Also same change in gnc_import_process_trans_item, downloaded_transaction_append, and gnc_import_TransInfo_is_balanced. 11. Changed the TRANS_IMBALANCE accessor method in xaccTransRegister to use xaccTransGetImbalanceValue. As far as I can tell this is only used by the "pd-balance" query type in gnc_scm2query_term_query_v1() defined in engine-helpers.c. This query type only tests the result for zero/non-zero. 12. Changed xaccTransGetAccountConvRate to accept any split into the correct commodity instead of insisting on one into the provided account. Then can use it in xaccTransScrubImbalance to set the value of the imbalance split from its amount, however later changed xaccTransScrubImbalance to not use it. Instead it sets the value for the new split correctly to keep the value of the whole transaction balanced. 13. Changed the balance sheet report to include a new option to not compute unrealized gains and losses. 14. Related to 9 above, changed gnc_split_register_auto_calc to not do anything if given a stock register where the value cell doesn't contain the value. 15. Also related to 9, changed gnc_split_register_save_amount_values to set the amount and value fields in the split correctly when using trading accounts. 16. Changed the new account and edit account dialogs to allow any commodity or currency for an income account if using trading accounts. It would be better to add a new account type for trading accounts, but that's a big deal and I'll leave that for later after we see whether this set of changes is going to be accepted or rejected. 17. Change gnc_xfer_dialog_run_exchange_dialog to understand that the new value is really the split's amount if using trading accounts. 18. Changed xaccSplitGetOtherSplit to ignore trading splits if using commodity exchange accounts. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18429 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
20197b89af
commit
b578c56b27
@ -75,6 +75,7 @@
|
||||
(cons ACCT-TYPE-EXPENSE (N_ "Expense"))
|
||||
(cons ACCT-TYPE-PAYABLE (N_ "Payment"))
|
||||
(cons ACCT-TYPE-RECEIVABLE (N_ "Invoice"))
|
||||
(cons ACCT-TYPE-TRADING (N_ "Decrease"))
|
||||
(cons ACCT-TYPE-EQUITY (N_ "Decrease"))))
|
||||
|
||||
(define gnc:*credit-strings*
|
||||
@ -91,6 +92,7 @@
|
||||
(cons ACCT-TYPE-EXPENSE (N_ "Rebate"))
|
||||
(cons ACCT-TYPE-PAYABLE (N_ "Bill"))
|
||||
(cons ACCT-TYPE-RECEIVABLE (N_ "Payment"))
|
||||
(cons ACCT-TYPE-TRADING (N_ "Increase"))
|
||||
(cons ACCT-TYPE-EQUITY (N_ "Increase"))))
|
||||
|
||||
(define (gnc:get-debit-string type)
|
||||
|
@ -134,7 +134,7 @@ skip_expense_acct_cb (Account *account, gpointer user_data)
|
||||
type = xaccAccountGetType (account);
|
||||
if (type == ACCT_TYPE_PAYABLE || type == ACCT_TYPE_RECEIVABLE ||
|
||||
type == ACCT_TYPE_CASH || type == ACCT_TYPE_BANK ||
|
||||
type == ACCT_TYPE_EQUITY)
|
||||
type == ACCT_TYPE_EQUITY || type == ACCT_TYPE_TRADING)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@ -157,7 +157,7 @@ skip_income_acct_cb (Account *account, gpointer user_data)
|
||||
type = xaccAccountGetType (account);
|
||||
if (type == ACCT_TYPE_PAYABLE || type == ACCT_TYPE_RECEIVABLE ||
|
||||
type == ACCT_TYPE_CASH || type == ACCT_TYPE_BANK ||
|
||||
type == ACCT_TYPE_EQUITY)
|
||||
type == ACCT_TYPE_EQUITY || type == ACCT_TYPE_TRADING)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -81,6 +81,12 @@
|
||||
gnc:*business-label* (N_ "Fancy Date Format")
|
||||
"g" (N_ "The default date format used for fancy printed dates")
|
||||
#f))
|
||||
|
||||
(reg-option
|
||||
(gnc:make-simple-boolean-option
|
||||
gnc:*book-label* gnc:*trading-accounts*
|
||||
"a" (N_ "True if trading accounts should be used for transactions involving more than one commodity")
|
||||
#f))
|
||||
)
|
||||
|
||||
(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator)
|
||||
|
@ -18,5 +18,10 @@
|
||||
gnc:*company-phone* gnc:*company-fax* gnc:*company-url*
|
||||
gnc:*company-email* gnc:*company-contact*)
|
||||
|
||||
(define gnc:*book-label* (N_ "Accounts"))
|
||||
(define gnc:*trading-accounts* (N_ "Trading Accounts"))
|
||||
|
||||
(export gnc:*book-label* gnc:*trading-accounts*)
|
||||
|
||||
(load-from-path "business-options.scm")
|
||||
(load-from-path "business-prefs.scm")
|
||||
|
@ -1919,7 +1919,7 @@ static int typeorder[NUM_ACCOUNT_TYPES] = {
|
||||
ACCT_TYPE_BANK, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL, ACCT_TYPE_CURRENCY,
|
||||
ACCT_TYPE_CASH, ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE,
|
||||
ACCT_TYPE_CREDIT, ACCT_TYPE_LIABILITY, ACCT_TYPE_PAYABLE,
|
||||
ACCT_TYPE_INCOME, ACCT_TYPE_EXPENSE, ACCT_TYPE_EQUITY };
|
||||
ACCT_TYPE_INCOME, ACCT_TYPE_EXPENSE, ACCT_TYPE_EQUITY, ACCT_TYPE_TRADING };
|
||||
|
||||
static int revorder[NUM_ACCOUNT_TYPES] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
@ -3701,6 +3701,7 @@ xaccAccountTypeEnumAsString(GNCAccountType type)
|
||||
GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
|
||||
GNC_RETURN_ENUM_AS_STRING(PAYABLE);
|
||||
GNC_RETURN_ENUM_AS_STRING(ROOT);
|
||||
GNC_RETURN_ENUM_AS_STRING(TRADING);
|
||||
GNC_RETURN_ENUM_AS_STRING(CHECKING);
|
||||
GNC_RETURN_ENUM_AS_STRING(SAVINGS);
|
||||
GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
|
||||
@ -3736,6 +3737,7 @@ xaccAccountStringToType(const char* str, GNCAccountType *type)
|
||||
GNC_RETURN_ON_MATCH(RECEIVABLE);
|
||||
GNC_RETURN_ON_MATCH(PAYABLE);
|
||||
GNC_RETURN_ON_MATCH(ROOT);
|
||||
GNC_RETURN_ON_MATCH(TRADING);
|
||||
GNC_RETURN_ON_MATCH(CHECKING);
|
||||
GNC_RETURN_ON_MATCH(SAVINGS);
|
||||
GNC_RETURN_ON_MATCH(MONEYMRKT);
|
||||
@ -3777,7 +3779,9 @@ account_type_name[NUM_ACCOUNT_TYPES] = {
|
||||
N_("Expense"),
|
||||
N_("Equity"),
|
||||
N_("A/Receivable"),
|
||||
N_("A/Payable")
|
||||
N_("A/Payable"),
|
||||
N_("Root"),
|
||||
N_("Trading")
|
||||
/*
|
||||
N_("Checking"),
|
||||
N_("Savings"),
|
||||
@ -3849,6 +3853,10 @@ xaccParentAccountTypesCompatibleWith (GNCAccountType type)
|
||||
return
|
||||
(1 << ACCT_TYPE_EQUITY) |
|
||||
(1 << ACCT_TYPE_ROOT);
|
||||
case ACCT_TYPE_TRADING:
|
||||
return
|
||||
(1 << ACCT_TYPE_TRADING) |
|
||||
(1 << ACCT_TYPE_ROOT);
|
||||
default:
|
||||
PERR("bad account type: %d", type);
|
||||
return 0;
|
||||
|
@ -140,18 +140,24 @@ typedef enum
|
||||
ACCT_TYPE_PAYABLE = 12, /**< A/P account type */
|
||||
|
||||
ACCT_TYPE_ROOT = 13, /**< The hidden root account of an account tree. */
|
||||
|
||||
ACCT_TYPE_TRADING = 14, /**< Account used to record multiple commodity transactions.
|
||||
* This is not the same as ACCT_TYPE_CURRENCY above.
|
||||
* Multiple commodity transactions have splits in these
|
||||
* accounts to make the transaction balance in each
|
||||
* commodity as well as in total value. */
|
||||
|
||||
NUM_ACCOUNT_TYPES = 14, /**< stop here; the following types
|
||||
NUM_ACCOUNT_TYPES = 15, /**< stop here; the following types
|
||||
* just aren't ready for prime time */
|
||||
|
||||
/* bank account types */
|
||||
ACCT_TYPE_CHECKING = 14, /**< bank account type -- don't use this
|
||||
ACCT_TYPE_CHECKING = 15, /**< bank account type -- don't use this
|
||||
* for now, see NUM_ACCOUNT_TYPES */
|
||||
ACCT_TYPE_SAVINGS = 15, /**< bank account type -- don't use this for
|
||||
ACCT_TYPE_SAVINGS = 16, /**< bank account type -- don't use this for
|
||||
* now, see NUM_ACCOUNT_TYPES */
|
||||
ACCT_TYPE_MONEYMRKT = 16, /**< bank account type -- don't use this
|
||||
ACCT_TYPE_MONEYMRKT = 17, /**< bank account type -- don't use this
|
||||
* for now, see NUM_ACCOUNT_TYPES */
|
||||
ACCT_TYPE_CREDITLINE = 17, /**< line of credit -- don't use this for
|
||||
ACCT_TYPE_CREDITLINE = 18, /**< line of credit -- don't use this for
|
||||
* now, see NUM_ACCOUNT_TYPES */
|
||||
} GNCAccountType;
|
||||
|
||||
|
@ -689,7 +689,7 @@ add_closing_balances (Account *parent,
|
||||
/* We need to carry a balance on any account that is not
|
||||
* and income or expense or equity account */
|
||||
if ((ACCT_TYPE_INCOME != tip) && (ACCT_TYPE_EXPENSE != tip) &&
|
||||
(ACCT_TYPE_EQUITY != tip))
|
||||
(ACCT_TYPE_EQUITY != tip && ACCT_TYPE_TRADING != tip))
|
||||
{
|
||||
gnc_numeric baln;
|
||||
baln = xaccAccountGetBalance (candidate);
|
||||
|
@ -48,7 +48,7 @@
|
||||
*
|
||||
* This routine will also create 'equity transactions' in
|
||||
* order to preserve the balances on accounts. For any
|
||||
* account that is not of income, expense or equity type,
|
||||
* account that is not of income, expense, trading or equity type,
|
||||
* this routine wil find the closing balance of each account
|
||||
* in the closed book. It will then create an 'equity
|
||||
* transaction' in the open book, creating an opening balance
|
||||
|
@ -72,6 +72,7 @@ static void
|
||||
TransScrubOrphansFast (Transaction *trans, Account *root)
|
||||
{
|
||||
GList *node;
|
||||
gchar *accname;
|
||||
|
||||
if (!trans) return;
|
||||
g_return_if_fail (root);
|
||||
@ -85,7 +86,12 @@ TransScrubOrphansFast (Transaction *trans, Account *root)
|
||||
|
||||
DEBUG ("Found an orphan \n");
|
||||
|
||||
orph = xaccScrubUtilityGetOrMakeAccount (root, trans->common_currency, _("Orphan"));
|
||||
accname = g_strconcat (_("Orphan"), "-",
|
||||
gnc_commodity_get_mnemonic (trans->common_currency),
|
||||
NULL);
|
||||
orph = xaccScrubUtilityGetOrMakeAccount (root, trans->common_currency,
|
||||
accname, ACCT_TYPE_BANK, FALSE);
|
||||
g_free (accname);
|
||||
if (!orph) continue;
|
||||
|
||||
xaccSplitSetAccount(split, orph);
|
||||
@ -353,28 +359,15 @@ xaccTransScrubCurrencyFromSplits(Transaction *trans)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xaccTransScrubImbalance (Transaction *trans, Account *root,
|
||||
Account *account)
|
||||
static Split *
|
||||
get_balance_split (Transaction *trans, Account *root, Account *account,
|
||||
gnc_commodity *commodity)
|
||||
{
|
||||
Split *balance_split = NULL;
|
||||
gnc_numeric imbalance;
|
||||
|
||||
if (!trans) return;
|
||||
|
||||
ENTER ("()");
|
||||
|
||||
/* Must look for orphan splits even if there is no imbalance. */
|
||||
xaccTransScrubSplits (trans);
|
||||
|
||||
/* If the transaction is balanced, nothing more to do */
|
||||
imbalance = xaccTransGetImbalance (trans);
|
||||
if (gnc_numeric_zero_p (imbalance)) {
|
||||
LEAVE("zero imbalance");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!account)
|
||||
Split *balance_split;
|
||||
gchar *accname;
|
||||
|
||||
if (!account ||
|
||||
!gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account)))
|
||||
{
|
||||
if (!root)
|
||||
{
|
||||
@ -383,16 +376,17 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
|
||||
{
|
||||
/* This can't occur, things should be in books */
|
||||
PERR ("Bad data corruption, no root account in book");
|
||||
LEAVE("");
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
account = xaccScrubUtilityGetOrMakeAccount (root,
|
||||
trans->common_currency, _("Imbalance"));
|
||||
accname = g_strconcat (_("Imbalance"), "-",
|
||||
gnc_commodity_get_mnemonic (commodity), NULL);
|
||||
account = xaccScrubUtilityGetOrMakeAccount (root, commodity,
|
||||
accname, ACCT_TYPE_BANK, FALSE);
|
||||
g_free (accname);
|
||||
if (!account) {
|
||||
PERR ("Can't get balancing account");
|
||||
LEAVE("");
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,36 +402,403 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
|
||||
xaccSplitSetAccount(balance_split, account);
|
||||
xaccTransCommitEdit (trans);
|
||||
}
|
||||
|
||||
return balance_split;
|
||||
}
|
||||
|
||||
PINFO ("unbalanced transaction");
|
||||
|
||||
/* Get the trading split for a given commodity, creating it (and the
|
||||
necessary accounts) if it doesn't exist. */
|
||||
static Split *
|
||||
get_trading_split (Transaction *trans, Account *root,
|
||||
gnc_commodity *commodity)
|
||||
{
|
||||
Split *balance_split;
|
||||
Account *trading_account;
|
||||
Account *ns_account;
|
||||
Account *account;
|
||||
gnc_commodity *default_currency = NULL;
|
||||
|
||||
if (!root)
|
||||
{
|
||||
const gnc_commodity *currency;
|
||||
const gnc_commodity *commodity;
|
||||
gnc_numeric old_value, new_value;
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
|
||||
currency = xaccTransGetCurrency (trans);
|
||||
old_value = xaccSplitGetValue (balance_split);
|
||||
|
||||
/* Note: We have to round for the commodity's fraction, NOT any
|
||||
* already existing denominator (bug #104343), because either one
|
||||
* of the denominators might already be reduced. */
|
||||
new_value = gnc_numeric_sub (old_value, imbalance,
|
||||
gnc_commodity_get_fraction(currency),
|
||||
GNC_HOW_RND_ROUND);
|
||||
|
||||
xaccSplitSetValue (balance_split, new_value);
|
||||
|
||||
commodity = xaccAccountGetCommodity (account);
|
||||
if (gnc_commodity_equiv (currency, commodity))
|
||||
root = gnc_book_get_root_account (xaccTransGetBook (trans));
|
||||
if (NULL == root)
|
||||
{
|
||||
xaccSplitSetAmount (balance_split, new_value);
|
||||
/* This can't occur, things should be in books */
|
||||
PERR ("Bad data corruption, no root account in book");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the default currency. This is harder than it seems. It's not
|
||||
possible to call gnc_default_currency() since it's a UI function. One
|
||||
might think that the currency of the root account would do, but the root
|
||||
account has no currency. Instead look for the Income placeholder account
|
||||
and use its currency. */
|
||||
default_currency = xaccAccountGetCommodity(gnc_account_lookup_by_name(root,
|
||||
_("Income")));
|
||||
if (! default_currency) {
|
||||
default_currency = commodity;
|
||||
}
|
||||
|
||||
trading_account = xaccScrubUtilityGetOrMakeAccount (root,
|
||||
default_currency,
|
||||
_("Trading"),
|
||||
ACCT_TYPE_TRADING, TRUE);
|
||||
if (!trading_account) {
|
||||
PERR ("Can't get trading account");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ns_account = xaccScrubUtilityGetOrMakeAccount (trading_account,
|
||||
default_currency,
|
||||
gnc_commodity_get_namespace(commodity),
|
||||
ACCT_TYPE_TRADING, TRUE);
|
||||
if (!ns_account) {
|
||||
PERR ("Can't get namespace account");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
account = xaccScrubUtilityGetOrMakeAccount (ns_account, commodity,
|
||||
gnc_commodity_get_mnemonic(commodity),
|
||||
ACCT_TYPE_TRADING, FALSE);
|
||||
if (!account) {
|
||||
PERR ("Can't get commodity account");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
balance_split = xaccTransFindSplitByAccount(trans, account);
|
||||
|
||||
/* Put split into account before setting split value */
|
||||
if (!balance_split)
|
||||
{
|
||||
balance_split = xaccMallocSplit (qof_instance_get_book(trans));
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
xaccSplitSetParent(balance_split, trans);
|
||||
xaccSplitSetAccount(balance_split, account);
|
||||
xaccTransCommitEdit (trans);
|
||||
}
|
||||
|
||||
return balance_split;
|
||||
}
|
||||
|
||||
/* Find the trading split for a commodity, but don't create any splits
|
||||
or accounts if they don't already exist. */
|
||||
static Split *
|
||||
find_trading_split (Transaction *trans, Account *root,
|
||||
gnc_commodity *commodity)
|
||||
{
|
||||
Account *trading_account;
|
||||
Account *ns_account;
|
||||
Account *account;
|
||||
|
||||
if (!root)
|
||||
{
|
||||
root = gnc_book_get_root_account (xaccTransGetBook (trans));
|
||||
if (NULL == root)
|
||||
{
|
||||
/* This can't occur, things should be in books */
|
||||
PERR ("Bad data corruption, no root account in book");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
trading_account = gnc_account_lookup_by_name (root, _("Trading"));
|
||||
if (!trading_account) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ns_account = gnc_account_lookup_by_name (trading_account,
|
||||
gnc_commodity_get_namespace(commodity));
|
||||
if (!ns_account) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
account = gnc_account_lookup_by_name (ns_account,
|
||||
gnc_commodity_get_mnemonic(commodity));
|
||||
if (!account) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return xaccTransFindSplitByAccount(trans, account);
|
||||
}
|
||||
|
||||
static void
|
||||
add_balance_split (Transaction *trans, gnc_numeric imbalance,
|
||||
Account *root, Account *account)
|
||||
{
|
||||
const gnc_commodity *commodity;
|
||||
gnc_numeric old_value, new_value;
|
||||
Split *balance_split;
|
||||
gnc_commodity *currency = xaccTransGetCurrency (trans);
|
||||
|
||||
balance_split = get_balance_split(trans, root, account, currency);
|
||||
if (!balance_split)
|
||||
{
|
||||
/* Error already logged */
|
||||
LEAVE("");
|
||||
return;
|
||||
}
|
||||
account = xaccSplitGetAccount(balance_split);
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
|
||||
old_value = xaccSplitGetValue (balance_split);
|
||||
|
||||
/* Note: We have to round for the commodity's fraction, NOT any
|
||||
* already existing denominator (bug #104343), because either one
|
||||
* of the denominators might already be reduced. */
|
||||
new_value = gnc_numeric_sub (old_value, imbalance,
|
||||
gnc_commodity_get_fraction(currency),
|
||||
GNC_HOW_RND_ROUND);
|
||||
|
||||
xaccSplitSetValue (balance_split, new_value);
|
||||
|
||||
commodity = xaccAccountGetCommodity (account);
|
||||
if (gnc_commodity_equiv (currency, commodity))
|
||||
{
|
||||
xaccSplitSetAmount (balance_split, new_value);
|
||||
}
|
||||
|
||||
xaccSplitScrub (balance_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
}
|
||||
|
||||
void
|
||||
xaccTransScrubImbalance (Transaction *trans, Account *root,
|
||||
Account *account)
|
||||
{
|
||||
const gnc_commodity *currency;
|
||||
|
||||
if (!trans) return;
|
||||
|
||||
ENTER ("()");
|
||||
|
||||
/* Must look for orphan splits even if there is no imbalance. */
|
||||
xaccTransScrubSplits (trans);
|
||||
|
||||
/* Return immediately if things are balanced. */
|
||||
if (xaccTransIsBalanced (trans))
|
||||
return;
|
||||
|
||||
currency = xaccTransGetCurrency (trans);
|
||||
|
||||
if (! xaccTransUseTradingAccounts (trans))
|
||||
{
|
||||
gnc_numeric imbalance;
|
||||
|
||||
/* Make the value sum to zero */
|
||||
imbalance = xaccTransGetImbalanceValue (trans);
|
||||
if (! gnc_numeric_zero_p (imbalance))
|
||||
{
|
||||
PINFO ("Value unbalanced transaction");
|
||||
|
||||
add_balance_split (trans, imbalance, root, account);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MonetaryList *imbal_list;
|
||||
MonetaryList *imbalance_commod;
|
||||
GList *splits;
|
||||
gnc_numeric imbalance;
|
||||
Split *balance_split = NULL;
|
||||
|
||||
/* If there are existing trading splits, adjust the price or exchange
|
||||
rate in each of them to agree with the non-trading splits for the
|
||||
same commodity. If there are multiple non-trading splits for the
|
||||
same commodity in the transaction this will use the exchange rate in
|
||||
the last such split. This shouldn't happen, and if it does then there's
|
||||
not much we can do about it anyway.
|
||||
|
||||
While we're at it, compute the value imbalance ignoring existing
|
||||
trading splits. */
|
||||
|
||||
imbalance = gnc_numeric_zero();
|
||||
|
||||
for (splits = trans->splits; splits; splits = splits->next)
|
||||
{
|
||||
Split *split = splits->data;
|
||||
gnc_numeric value, amount;
|
||||
gnc_commodity *commodity;
|
||||
|
||||
if (! xaccTransStillHasSplit (trans, split)) continue;
|
||||
|
||||
commodity = xaccAccountGetCommodity (xaccSplitGetAccount(split));
|
||||
if (!commodity)
|
||||
{
|
||||
PERR("Split has no commodity");
|
||||
continue;
|
||||
}
|
||||
|
||||
balance_split = find_trading_split (trans, root, commodity);
|
||||
|
||||
if (balance_split != split)
|
||||
/* this is not a trading split */
|
||||
imbalance = gnc_numeric_add(imbalance, xaccSplitGetValue (split),
|
||||
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
|
||||
|
||||
/* Ignore splits where value or amount is zero */
|
||||
value = xaccSplitGetValue (split);
|
||||
amount = xaccSplitGetAmount (split);
|
||||
if (gnc_numeric_zero_p(amount) || gnc_numeric_zero_p(value))
|
||||
continue;
|
||||
|
||||
if (balance_split && balance_split != split)
|
||||
{
|
||||
gnc_numeric convrate = gnc_numeric_div (amount, value,
|
||||
GNC_DENOM_AUTO, GNC_DENOM_REDUCE);
|
||||
gnc_numeric old_value, new_value;
|
||||
old_value = xaccSplitGetValue(balance_split);
|
||||
new_value = gnc_numeric_div (xaccSplitGetAmount(balance_split),
|
||||
convrate,
|
||||
gnc_commodity_get_fraction(currency),
|
||||
GNC_RND_ROUND);
|
||||
if (! gnc_numeric_equal (old_value, new_value))
|
||||
{
|
||||
xaccTransBeginEdit (trans);
|
||||
xaccSplitSetValue (balance_split, new_value);
|
||||
xaccSplitScrub (balance_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xaccSplitScrub (balance_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
/* Balance the value, ignoring existing trading splits */
|
||||
if (! gnc_numeric_zero_p (imbalance))
|
||||
{
|
||||
PINFO ("Value unbalanced transaction");
|
||||
|
||||
add_balance_split (trans, imbalance, root, account);
|
||||
}
|
||||
|
||||
/* If the transaction is balanced, nothing more to do */
|
||||
imbal_list = xaccTransGetImbalance (trans);
|
||||
if (!imbal_list)
|
||||
{
|
||||
LEAVE("()");
|
||||
return;
|
||||
}
|
||||
|
||||
PINFO ("Currency unbalanced transaction");
|
||||
|
||||
for (imbalance_commod = imbal_list; imbalance_commod;
|
||||
imbalance_commod = imbalance_commod->next) {
|
||||
gnc_monetary *imbal_mon = imbalance_commod->data;
|
||||
gnc_commodity *commodity;
|
||||
gnc_numeric convrate;
|
||||
gnc_numeric old_amount, new_amount;
|
||||
gnc_numeric old_value, new_value, val_imbalance;
|
||||
GList *splits;
|
||||
|
||||
commodity = gnc_monetary_commodity (*imbal_mon);
|
||||
|
||||
balance_split = get_trading_split(trans, root, commodity);
|
||||
if (!balance_split)
|
||||
{
|
||||
/* Error already logged */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
LEAVE("");
|
||||
return;
|
||||
}
|
||||
|
||||
account = xaccSplitGetAccount(balance_split);
|
||||
|
||||
if (! gnc_commodity_equal (currency, commodity)) {
|
||||
/* Find the value imbalance in this commodity */
|
||||
val_imbalance = gnc_numeric_zero();
|
||||
for (splits = trans->splits; splits; splits = splits->next) {
|
||||
Split *split = splits->data;
|
||||
if (xaccTransStillHasSplit (trans, split) &&
|
||||
gnc_commodity_equal (commodity,
|
||||
xaccAccountGetCommodity(xaccSplitGetAccount(split))))
|
||||
val_imbalance = gnc_numeric_add (val_imbalance, xaccSplitGetValue (split),
|
||||
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
|
||||
}
|
||||
}
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
|
||||
old_amount = xaccSplitGetAmount (balance_split);
|
||||
new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
|
||||
gnc_commodity_get_fraction(commodity),
|
||||
GNC_HOW_RND_ROUND);
|
||||
|
||||
xaccSplitSetAmount (balance_split, new_amount);
|
||||
|
||||
if (gnc_commodity_equal (currency, commodity)) {
|
||||
/* Imbalance commodity is the transaction currency, value in the
|
||||
split must be the same as the amount */
|
||||
xaccSplitSetValue (balance_split, new_amount);
|
||||
}
|
||||
else {
|
||||
old_value = xaccSplitGetValue (balance_split);
|
||||
new_value = gnc_numeric_sub (old_value, val_imbalance,
|
||||
gnc_commodity_get_fraction(currency),
|
||||
GNC_HOW_RND_ROUND);
|
||||
|
||||
xaccSplitSetValue (balance_split, new_value);
|
||||
}
|
||||
|
||||
xaccSplitScrub (balance_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
}
|
||||
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
|
||||
if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans))) {
|
||||
/* This is probably because there are splits with zero amount
|
||||
and non-zero value. These are usually realized gain/loss
|
||||
splits. Add a reversing split for each of them to balance
|
||||
the value. */
|
||||
|
||||
/* Copy the split list so we don't see the splits we're adding */
|
||||
GList *splits_dup = g_list_copy(trans->splits);
|
||||
for (splits = splits_dup; splits; splits = splits->next) {
|
||||
Split *split = splits->data;
|
||||
if (! xaccTransStillHasSplit(trans, split)) continue;
|
||||
if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) &&
|
||||
gnc_numeric_zero_p(xaccSplitGetAmount(split))) {
|
||||
gnc_commodity *commodity;
|
||||
gnc_numeric old_value, new_value;
|
||||
|
||||
commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
|
||||
if (!commodity) {
|
||||
PERR("Split has no commodity");
|
||||
continue;
|
||||
}
|
||||
balance_split = get_trading_split(trans, root, commodity);
|
||||
if (!balance_split)
|
||||
{
|
||||
/* Error already logged */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
LEAVE("");
|
||||
return;
|
||||
}
|
||||
account = xaccSplitGetAccount(balance_split);
|
||||
|
||||
xaccTransBeginEdit (trans);
|
||||
|
||||
old_value = xaccSplitGetValue (balance_split);
|
||||
new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
|
||||
gnc_commodity_get_fraction(currency),
|
||||
GNC_HOW_RND_ROUND);
|
||||
xaccSplitSetValue (balance_split, new_value);
|
||||
|
||||
/* Don't change the balance split's amount since the amount
|
||||
is zero in the split we're working on */
|
||||
|
||||
xaccSplitScrub (balance_split);
|
||||
xaccTransCommitEdit (trans);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_free(splits_dup);
|
||||
|
||||
if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
|
||||
PERR("Balancing currencies unbalanced value");
|
||||
}
|
||||
}
|
||||
LEAVE ("()");
|
||||
}
|
||||
@ -836,9 +1197,9 @@ xaccAccountScrubKvp (Account *account)
|
||||
|
||||
Account *
|
||||
xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
|
||||
const char *name_root)
|
||||
const char *accname, GNCAccountType acctype,
|
||||
gboolean placeholder)
|
||||
{
|
||||
char * accname;
|
||||
Account * acc;
|
||||
|
||||
g_return_val_if_fail (root, NULL);
|
||||
@ -850,9 +1211,6 @@ xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
accname = g_strconcat (name_root, "-",
|
||||
gnc_commodity_get_mnemonic (currency), NULL);
|
||||
|
||||
/* See if we've got one of these going already ... */
|
||||
acc = gnc_account_lookup_by_name(root, accname);
|
||||
|
||||
@ -863,15 +1221,14 @@ xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
|
||||
xaccAccountBeginEdit (acc);
|
||||
xaccAccountSetName (acc, accname);
|
||||
xaccAccountSetCommodity (acc, currency);
|
||||
xaccAccountSetType (acc, ACCT_TYPE_BANK);
|
||||
xaccAccountSetType (acc, acctype);
|
||||
xaccAccountSetPlaceholder (acc, placeholder);
|
||||
|
||||
/* Hang the account off the root. */
|
||||
gnc_account_append_child (root, acc);
|
||||
xaccAccountCommitEdit (acc);
|
||||
}
|
||||
|
||||
g_free (accname);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@
|
||||
|
||||
/* Utility to make account by name. Not for public use. */
|
||||
Account * xaccScrubUtilityGetOrMakeAccount (Account *root,
|
||||
gnc_commodity * currency, const char *name_root);
|
||||
gnc_commodity * currency, const char *accname,
|
||||
GNCAccountType acctype, gboolean placeholder);
|
||||
|
||||
|
||||
#endif /* XACC_SCRUB_P_H */
|
||||
|
@ -1056,7 +1056,7 @@ xaccSplitConvertAmount (const Split *split, Account * account)
|
||||
* rate and just return the 'other' split amount.
|
||||
*/
|
||||
txn = xaccSplitGetParent (split);
|
||||
if (txn && gnc_numeric_zero_p (xaccTransGetImbalance (txn))) {
|
||||
if (txn && xaccTransIsBalanced (txn)) {
|
||||
const Split *osplit = xaccSplitGetOtherSplit (split);
|
||||
|
||||
if (osplit)
|
||||
@ -1668,6 +1668,9 @@ xaccSplitGetOtherSplit (const Split *split)
|
||||
int count, num_splits;
|
||||
Split *other = NULL;
|
||||
KvpValue *sva;
|
||||
Account *trading_account = NULL;
|
||||
Account *root_account = NULL;
|
||||
gboolean trading_accts;
|
||||
|
||||
if (!split) return NULL;
|
||||
trans = split->parent;
|
||||
@ -1684,16 +1687,20 @@ xaccSplitGetOtherSplit (const Split *split)
|
||||
return s1;
|
||||
#endif
|
||||
|
||||
trading_accts = xaccTransUseTradingAccounts (trans);
|
||||
num_splits = xaccTransCountSplits(trans);
|
||||
count = num_splits;
|
||||
sva = kvp_frame_get_slot (split->inst.kvp_data, "lot-split");
|
||||
if (!sva && (2 != count)) return NULL;
|
||||
if (!sva && !trading_accts && (2 != count)) return NULL;
|
||||
|
||||
for (i = 0; i < num_splits; i++) {
|
||||
Split *s = xaccTransGetSplit(trans, i);
|
||||
if (s == split) { --count; continue; }
|
||||
if (kvp_frame_get_slot (s->inst.kvp_data, "lot-split"))
|
||||
{ --count; continue; }
|
||||
if (trading_accts &&
|
||||
xaccAccountGetType(xaccSplitGetAccount(s)) == ACCT_TYPE_TRADING)
|
||||
{ --count; continue; }
|
||||
other = s;
|
||||
}
|
||||
return (1 == count) ? other : NULL;
|
||||
|
@ -649,6 +649,18 @@ xaccTransEqual(const Transaction *ta, const Transaction *tb,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
xaccTransUseTradingAccounts
|
||||
|
||||
Returns true if the transaction should include trading account splits if
|
||||
it involves more than one commodity.
|
||||
\********************************************************************/
|
||||
|
||||
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
|
||||
{
|
||||
return qof_book_use_trading_accounts(qof_instance_get_book (trans));
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
\********************************************************************/
|
||||
|
||||
@ -665,7 +677,7 @@ xaccTransLookup (const GUID *guid, QofBook *book)
|
||||
\********************************************************************/
|
||||
|
||||
gnc_numeric
|
||||
xaccTransGetImbalance (const Transaction * trans)
|
||||
xaccTransGetImbalanceValue (const Transaction * trans)
|
||||
{
|
||||
gnc_numeric imbal = gnc_numeric_zero();
|
||||
if (!trans) return imbal;
|
||||
@ -680,6 +692,91 @@ xaccTransGetImbalance (const Transaction * trans)
|
||||
return imbal;
|
||||
}
|
||||
|
||||
MonetaryList *
|
||||
xaccTransGetImbalance (const Transaction * trans)
|
||||
{
|
||||
/* imbal_value is used if either (1) the transaction has a non currency
|
||||
split or (2) all the splits are in the same currency. If there are
|
||||
no non-currency splits and not all splits are in the same currency then
|
||||
imbal_list is used to compute the imbalance. */
|
||||
MonetaryList *imbal_list = NULL;
|
||||
gnc_numeric imbal_value = gnc_numeric_zero();
|
||||
gboolean trading_accts;
|
||||
|
||||
if (!trans) return imbal_list;
|
||||
|
||||
ENTER("(trans=%p)", trans);
|
||||
|
||||
trading_accts = xaccTransUseTradingAccounts (trans);
|
||||
|
||||
/* If using trading accounts and there is at least one split that is not
|
||||
in the transaction currency or a split that has a price or exchange
|
||||
rate other than 1, then compute the balance in each commodity in the
|
||||
transaction. Otherwise (all splits are in the transaction's currency)
|
||||
then compute the balance using the value fields.
|
||||
|
||||
Optimize for the common case of only one currency and a balanced
|
||||
transaction. */
|
||||
FOR_EACH_SPLIT(trans, {
|
||||
gnc_commodity *commodity;
|
||||
commodity = xaccAccountGetCommodity(xaccSplitGetAccount(s));
|
||||
if (trading_accts &&
|
||||
(imbal_list ||
|
||||
! gnc_commodity_equiv(commodity, trans->common_currency) ||
|
||||
! gnc_numeric_equal(xaccSplitGetAmount(s), xaccSplitGetValue(s)))) {
|
||||
/* Need to use (or already are using) a list of imbalances in each of
|
||||
the currencies used in the transaction. */
|
||||
if (! imbal_list) {
|
||||
/* All previous splits have been in the transaction's common
|
||||
currency, so imbal_value is in this currency. */
|
||||
imbal_list = gnc_monetary_list_add_value(imbal_list,
|
||||
trans->common_currency,
|
||||
imbal_value);
|
||||
}
|
||||
imbal_list = gnc_monetary_list_add_value(imbal_list, commodity,
|
||||
xaccSplitGetAmount(s));
|
||||
}
|
||||
|
||||
/* Add it to the value accumulator in case we need it. */
|
||||
imbal_value = gnc_numeric_add(imbal_value, xaccSplitGetValue(s),
|
||||
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
|
||||
} );
|
||||
|
||||
|
||||
if (!imbal_list && !gnc_numeric_zero_p(imbal_value)) {
|
||||
/* Not balanced and no list, create one. If we found multiple currencies
|
||||
and no non-currency commodity then imbal_list will already exist and
|
||||
we won't get here. */
|
||||
imbal_list = gnc_monetary_list_add_value(imbal_list,
|
||||
trans->common_currency,
|
||||
imbal_value);
|
||||
}
|
||||
|
||||
/* Delete all the zero entries from the list, perhaps leaving an
|
||||
empty list */
|
||||
imbal_list = gnc_monetary_list_delete_zeros(imbal_list);
|
||||
|
||||
LEAVE("(trans=%p), imbal=%p", trans, imbal_list);
|
||||
return imbal_list;
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccTransIsBalanced (const Transaction *trans)
|
||||
{
|
||||
MonetaryList *imbal_list;
|
||||
gboolean result;
|
||||
if (! gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
|
||||
return FALSE;
|
||||
|
||||
if (!xaccTransUseTradingAccounts (trans))
|
||||
return TRUE;
|
||||
|
||||
imbal_list = xaccTransGetImbalance(trans);
|
||||
result = imbal_list == NULL;
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
return result;
|
||||
}
|
||||
|
||||
gnc_numeric
|
||||
xaccTransGetAccountValue (const Transaction *trans,
|
||||
const Account *acc)
|
||||
@ -716,22 +813,28 @@ xaccTransGetAccountConvRate(Transaction *txn, Account *acc)
|
||||
GList *splits;
|
||||
Split *s;
|
||||
gboolean found_acc_match = FALSE;
|
||||
gnc_commodity *acc_commod = xaccAccountGetCommodity(acc);
|
||||
|
||||
/* We need to compute the conversion rate into _this account_. So,
|
||||
* find the first split into this account, compute the conversion
|
||||
* rate (based on amount/value), and then return this conversion
|
||||
* rate.
|
||||
*/
|
||||
if (gnc_commodity_equal(xaccAccountGetCommodity(acc),
|
||||
xaccTransGetCurrency(txn)))
|
||||
if (gnc_commodity_equal(acc_commod, xaccTransGetCurrency(txn)))
|
||||
return gnc_numeric_create(1, 1);
|
||||
|
||||
for (splits = txn->splits; splits; splits = splits->next) {
|
||||
Account *split_acc;
|
||||
gnc_commodity *split_commod;
|
||||
|
||||
s = splits->data;
|
||||
|
||||
if (!xaccTransStillHasSplit(txn, s))
|
||||
continue;
|
||||
if (xaccSplitGetAccount (s) != acc)
|
||||
split_acc = xaccSplitGetAccount (s);
|
||||
split_commod = xaccAccountGetCommodity (split_acc);
|
||||
if (! (split_acc == acc ||
|
||||
gnc_commodity_equal (split_commod, acc_commod)))
|
||||
continue;
|
||||
|
||||
found_acc_match = TRUE;
|
||||
@ -1932,7 +2035,7 @@ static QofObject trans_object_def = {
|
||||
static gboolean
|
||||
trans_is_balanced_p (const Transaction *trans)
|
||||
{
|
||||
return trans ? gnc_numeric_zero_p(xaccTransGetImbalance(trans)) : FALSE;
|
||||
return trans ? xaccTransIsBalanced(trans) : FALSE;
|
||||
}
|
||||
|
||||
gboolean xaccTransRegister (void)
|
||||
@ -1954,7 +2057,7 @@ gboolean xaccTransRegister (void)
|
||||
{ TRANS_DATE_DUE, QOF_TYPE_DATE,
|
||||
(QofAccessFunc)xaccTransRetDateDueTS, NULL },
|
||||
{ TRANS_IMBALANCE, QOF_TYPE_NUMERIC,
|
||||
(QofAccessFunc)xaccTransGetImbalance, NULL },
|
||||
(QofAccessFunc)xaccTransGetImbalanceValue, NULL },
|
||||
{ TRANS_NOTES, QOF_TYPE_STRING,
|
||||
(QofAccessFunc)xaccTransGetNotes,
|
||||
(QofSetterFunc)qofTransSetNotes },
|
||||
|
@ -244,6 +244,10 @@ guint gnc_book_count_transactions(QofBook *book);
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Determine whether this transaction should use commodity trading accounts
|
||||
*/
|
||||
gboolean xaccTransUseTradingAccounts(const Transaction *trans);
|
||||
|
||||
/** Sorts the splits in a transaction, putting the debits first,
|
||||
* followed by the credits.
|
||||
*/
|
||||
@ -342,15 +346,34 @@ gboolean xaccTransHasSplitsInStateByAccount (const Transaction *trans,
|
||||
/** Set the commodity of this transaction. */
|
||||
void xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr);
|
||||
|
||||
/** The xaccTransGetImbalance() method returns the total value of the
|
||||
/** The xaccTransGetImbalanceValue() method returns the total value of the
|
||||
* transaction. In a pure double-entry system, this imbalance
|
||||
* should be exactly zero, and if it is not, something is broken.
|
||||
* However, when double-entry semantics are not enforced, unbalanced
|
||||
* transactions can sneak in, and this routine can be used to find
|
||||
* out how much things are off by. The value returned is denominated
|
||||
* in the currency that is returned by the xaccTransFindCommonCurrency()
|
||||
* method. */
|
||||
gnc_numeric xaccTransGetImbalance (const Transaction * trans);
|
||||
* method.
|
||||
*
|
||||
* If the use of currency exchange accounts is enabled then the a
|
||||
* a transaction must be balanced in each currency it uses to be considered
|
||||
* to be balanced. The method xaccTransGetImbalance is used by most
|
||||
* code to take this into consideration. This method is only used in a few
|
||||
* places that want the transaction value even if currency exchange accounts
|
||||
* are enabled. */
|
||||
gnc_numeric xaccTransGetImbalanceValue (const Transaction * trans);
|
||||
|
||||
/** The xaccTransGetImbalance method returns a list giving the value of
|
||||
* the transaction in each currency for which the balance is not zero.
|
||||
* If the use of currency accounts is disabled, then this will be only
|
||||
* the common currency for the transaction and xaccTransGetImbalance
|
||||
* becomes equivalent to xaccTransGetImbalanceValue. Otherwise it will
|
||||
* return a list containing the imbalance in each currency. */
|
||||
MonetaryList *xaccTransGetImbalance (const Transaction * trans);
|
||||
|
||||
/** Returns true if the transaction is balanced according to the rules
|
||||
* currently in effect. */
|
||||
gboolean xaccTransIsBalanced(const Transaction * trans);
|
||||
|
||||
/** The xaccTransGetAccountValue() method returns the total value applied
|
||||
* to a particular account. In some cases there may be multiple Splits
|
||||
|
@ -227,6 +227,8 @@ static KvpFrame * gnc_book_get_slots(QofBook *book) {
|
||||
SET_ENUM("ACCT-TYPE-EQUITY");
|
||||
SET_ENUM("ACCT-TYPE-RECEIVABLE");
|
||||
SET_ENUM("ACCT-TYPE-PAYABLE");
|
||||
SET_ENUM("ACCT-TYPE-ROOT");
|
||||
SET_ENUM("ACCT-TYPE-TRADING");
|
||||
SET_ENUM("NUM-ACCOUNT-TYPES");
|
||||
SET_ENUM("ACCT-TYPE-CHECKING");
|
||||
SET_ENUM("ACCT-TYPE-SAVINGS");
|
||||
|
@ -2416,4 +2416,61 @@ gnc_commodity_table_register (void)
|
||||
return qof_object_register (&commodity_table_object_def);
|
||||
}
|
||||
|
||||
/* *******************************************************************
|
||||
* gnc_monetary methods
|
||||
********************************************************************/
|
||||
|
||||
/** Add a gnc_monetary to the list */
|
||||
MonetaryList *
|
||||
gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
|
||||
{
|
||||
MonetaryList *l = list, *tmp;
|
||||
for (tmp = list; tmp; tmp = tmp->next) {
|
||||
gnc_monetary *list_mon = tmp->data;
|
||||
if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity)) {
|
||||
list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
|
||||
GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we found an entry, and add one if not */
|
||||
if (tmp == NULL) {
|
||||
gnc_monetary *new_mon = g_new0(gnc_monetary, 1);
|
||||
*new_mon = add_mon;
|
||||
l = g_list_prepend(l, new_mon);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Delete all entries in the list that have zero value. Return list
|
||||
pointer will be a null pointer if there are no non-zero entries **/
|
||||
MonetaryList *
|
||||
gnc_monetary_list_delete_zeros(MonetaryList *list)
|
||||
{
|
||||
MonetaryList *node, *next;
|
||||
for (node = list; node; node = next) {
|
||||
gnc_monetary *mon = node->data;
|
||||
next = node->next;
|
||||
if (gnc_numeric_zero_p(mon->value)) {
|
||||
g_free(mon);
|
||||
list = g_list_delete_link(list, node);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Free a MonetaryList and all the monetaries it points to */
|
||||
void
|
||||
gnc_monetary_list_free(MonetaryList *list)
|
||||
{
|
||||
MonetaryList *tmp;
|
||||
for (tmp = list; tmp; tmp = tmp->next) {
|
||||
g_free(tmp->data);
|
||||
}
|
||||
|
||||
g_list_free(list);
|
||||
}
|
||||
|
||||
/* ========================= END OF FILE ============================== */
|
||||
|
@ -973,6 +973,70 @@ void gnc_commodity_commit_edit (gnc_commodity *cm);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name Monetary value, commodity identity and numeric value
|
||||
@{
|
||||
*/
|
||||
struct _gnc_monetary
|
||||
{
|
||||
gnc_commodity *commodity;
|
||||
gnc_numeric value;
|
||||
};
|
||||
|
||||
typedef struct _gnc_monetary gnc_monetary;
|
||||
|
||||
/* A list of monetary values. This could be a hash table, but as currently
|
||||
* used it rarely contains more than one or two different commodities so
|
||||
* it doesn't seem worth the trouble.
|
||||
*/
|
||||
typedef GList MonetaryList;
|
||||
|
||||
/** @name Constructors
|
||||
@{
|
||||
Make a gnc_monetary from a gnc_commodity and gnc_numeric */
|
||||
static inline
|
||||
gnc_monetary gnc_monetary_create(gnc_commodity *commod, gnc_numeric val) {
|
||||
gnc_monetary out;
|
||||
out.commodity = commod;
|
||||
out.value = val;
|
||||
return out;
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** @name Accessors
|
||||
@{
|
||||
*/
|
||||
static inline
|
||||
gnc_commodity * gnc_monetary_commodity(gnc_monetary a) { return a.commodity; }
|
||||
|
||||
static inline
|
||||
gnc_numeric gnc_monetary_value(gnc_monetary a) { return a.value; }
|
||||
/** @} */
|
||||
|
||||
/** @name Manipulate MonetaryList lists
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Add a gnc_monetary to the list */
|
||||
MonetaryList *gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary mon);
|
||||
|
||||
/** Add something to the list given a commodity and value */
|
||||
static inline
|
||||
MonetaryList *gnc_monetary_list_add_value(MonetaryList *list,
|
||||
gnc_commodity *commod,
|
||||
gnc_numeric value)
|
||||
{
|
||||
return gnc_monetary_list_add_monetary(list,
|
||||
gnc_monetary_create(commod, value));
|
||||
}
|
||||
|
||||
/** Delete all the zero-value entries from a list */
|
||||
MonetaryList *gnc_monetary_list_delete_zeros(MonetaryList *list);
|
||||
|
||||
/** Free a monetary list and all the items it points to */
|
||||
void gnc_monetary_list_free(MonetaryList *list);
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* GNC_COMMODITY_H */
|
||||
/** @} */
|
||||
|
@ -169,7 +169,9 @@ gnc_account_commodity_from_type (AccountWindow * aw, gboolean update)
|
||||
{
|
||||
dialog_commodity_mode new_mode;
|
||||
|
||||
if ((aw->type == ACCT_TYPE_STOCK) || (aw->type == ACCT_TYPE_MUTUAL))
|
||||
if (aw->type == ACCT_TYPE_TRADING)
|
||||
new_mode = DIAG_COMM_ALL;
|
||||
else if ((aw->type == ACCT_TYPE_STOCK) || (aw->type == ACCT_TYPE_MUTUAL))
|
||||
new_mode = DIAG_COMM_NON_CURRENCY;
|
||||
else
|
||||
new_mode = DIAG_COMM_CURRENCY;
|
||||
@ -1026,7 +1028,8 @@ gnc_account_type_changed_cb (GtkTreeSelection *selection, gpointer data)
|
||||
sensitive = (aw->type != ACCT_TYPE_EQUITY &&
|
||||
aw->type != ACCT_TYPE_CURRENCY &&
|
||||
aw->type != ACCT_TYPE_STOCK &&
|
||||
aw->type != ACCT_TYPE_MUTUAL);
|
||||
aw->type != ACCT_TYPE_MUTUAL &&
|
||||
aw->type != ACCT_TYPE_TRADING);
|
||||
}
|
||||
|
||||
gtk_widget_set_sensitive (aw->opening_balance_page, sensitive);
|
||||
|
@ -2146,6 +2146,19 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
|
||||
|
||||
g_return_val_if_fail(txn_cur, TRUE);
|
||||
|
||||
if (xaccTransUseTradingAccounts (txn)) {
|
||||
/* If we're using commodity trading accounts then "amount" is
|
||||
really the split's amount and it's in xfer_com. We need an
|
||||
exchange rate that will convert this amount into a value in
|
||||
the transaction currency. */
|
||||
if (gnc_commodity_equal(xfer_com, txn_cur)) {
|
||||
/* Transaction is in the same currency as the split, exchange
|
||||
rate is 1. */
|
||||
*exch_rate = gnc_numeric_create(1, 1);
|
||||
return FALSE;
|
||||
}
|
||||
swap_amounts = TRUE;
|
||||
|
||||
/* We know that "amount" is always in the reg_com currency.
|
||||
* Unfortunately it is possible that neither xfer_com or txn_cur are
|
||||
* the same as reg_com, in which case we need to convert to the txn
|
||||
@ -2153,7 +2166,7 @@ gboolean gnc_xfer_dialog_run_exchange_dialog(
|
||||
* need to flip-flop the commodities and the exchange rates.
|
||||
*/
|
||||
|
||||
if (gnc_commodity_equal(reg_com, txn_cur)) {
|
||||
} else if (gnc_commodity_equal(reg_com, txn_cur)) {
|
||||
/* we're working in the txn currency. Great. Nothing to do! */
|
||||
swap_amounts = FALSE;
|
||||
|
||||
|
@ -293,6 +293,12 @@ gnc_ui_accounts_recurse (Account *parent, GList **currency_list,
|
||||
case ACCT_TYPE_EQUITY:
|
||||
/* no-op, see comments at top about summing assets */
|
||||
break;
|
||||
/**
|
||||
* @fixme I don't know if this is right or if trading accounts should be
|
||||
* treated like income and expense accounts.
|
||||
**/
|
||||
case ACCT_TYPE_TRADING:
|
||||
break;
|
||||
case ACCT_TYPE_CURRENCY:
|
||||
default:
|
||||
break;
|
||||
|
@ -706,7 +706,8 @@ balance_cell_data_func (GtkTreeViewColumn *tree_column,
|
||||
string = xaccPrintAmount (balance, print_info);
|
||||
}
|
||||
|
||||
if (xaccAccountGetType(account) == ACCT_TYPE_EQUITY) {
|
||||
if (xaccAccountGetType(account) == ACCT_TYPE_EQUITY ||
|
||||
xaccAccountGetType(account) == ACCT_TYPE_TRADING) {
|
||||
allow_value = FALSE;
|
||||
string=_("zero");
|
||||
} else {
|
||||
|
@ -122,7 +122,10 @@ gboolean
|
||||
gnc_import_TransInfo_is_balanced (const GNCImportTransInfo *info)
|
||||
{
|
||||
g_assert (info);
|
||||
if(gnc_numeric_zero_p(xaccTransGetImbalance(gnc_import_TransInfo_get_trans(info))))
|
||||
/* Assume that the importer won't create a transaction that involves two or more
|
||||
currencies and no non-currency commodity. In that case can use the simpler
|
||||
value imbalance check. */
|
||||
if(gnc_numeric_zero_p(xaccTransGetImbalanceValue(gnc_import_TransInfo_get_trans(info))))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@ -860,9 +863,11 @@ gnc_import_process_trans_item (GncImportMatchMap *matchmap,
|
||||
(gnc_import_TransInfo_get_trans (trans_info)));*/
|
||||
{
|
||||
/* This is a quick workaround for the bug described in
|
||||
http://gnucash.org/pipermail/gnucash-devel/2003-August/009982.html */
|
||||
http://gnucash.org/pipermail/gnucash-devel/2003-August/009982.html
|
||||
Assume that importers won't create transactions involving two or more
|
||||
currencies so we can use xaccTransGetImbalanceValue. */
|
||||
gnc_numeric v =
|
||||
gnc_numeric_neg (xaccTransGetImbalance
|
||||
gnc_numeric_neg (xaccTransGetImbalanceValue
|
||||
(gnc_import_TransInfo_get_trans (trans_info)));
|
||||
xaccSplitSetValue (split, v);
|
||||
xaccSplitSetAmount (split, v);
|
||||
|
@ -605,10 +605,12 @@ refresh_model_row (GNCImportMainMatcher *gui,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Assume that importers won't create transactions in two or more
|
||||
currencies so we can use xaccTransGetImbalanceValue */
|
||||
imbalance =
|
||||
g_strdup
|
||||
(xaccPrintAmount
|
||||
(gnc_numeric_neg(xaccTransGetImbalance
|
||||
(gnc_numeric_neg(xaccTransGetImbalanceValue
|
||||
(gnc_import_TransInfo_get_trans(info) )),
|
||||
gnc_commodity_print_info
|
||||
(xaccTransGetCurrency(gnc_import_TransInfo_get_trans (info)),
|
||||
|
@ -151,7 +151,10 @@ downloaded_transaction_append(GNCImportMatchPicker * matcher,
|
||||
gtk_list_store_set(store, &iter, DOWNLOADED_COL_MEMO, ro_text, -1);
|
||||
|
||||
/*Imbalance*/
|
||||
ro_text = xaccPrintAmount(xaccTransGetImbalance(trans),
|
||||
/* Assume that the importer won't create a transaction that involves two or more
|
||||
currencies and no non-currency commodity. In that case can use the simpler
|
||||
value imbalance check. */
|
||||
ro_text = xaccPrintAmount(xaccTransGetImbalanceValue(trans),
|
||||
gnc_default_print_info(TRUE));
|
||||
gtk_list_store_set(store, &iter, DOWNLOADED_COL_BALANCED, ro_text, -1);
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libguile.h>
|
||||
|
||||
#include "qof.h"
|
||||
#include "qofevent-p.h"
|
||||
@ -439,6 +440,48 @@ qof_book_get_counter (const QofBook *book, const char *counter_name)
|
||||
return counter;
|
||||
}
|
||||
|
||||
static char *get_scm_string(const char *str_name)
|
||||
{
|
||||
SCM scm_string = scm_c_eval_string (str_name);
|
||||
if (! SCM_STRINGP(scm_string))
|
||||
return NULL;
|
||||
|
||||
return g_strdup(SCM_STRING_CHARS(scm_string));
|
||||
}
|
||||
|
||||
/* Determine whether this book uses trading accounts */
|
||||
gboolean qof_book_use_trading_accounts (const QofBook *book)
|
||||
{
|
||||
static char *options_name = NULL;
|
||||
static char *acct_section = NULL;
|
||||
static char *trading_opt = NULL;
|
||||
|
||||
const char *opt;
|
||||
kvp_value *kvp_val;
|
||||
|
||||
if (options_name == NULL)
|
||||
{
|
||||
options_name = get_scm_string ("(car gnc:*kvp-option-path*)");
|
||||
acct_section = get_scm_string ("gnc:*book-label*");
|
||||
trading_opt = get_scm_string ("gnc:*trading-accounts*");
|
||||
if (options_name == NULL || acct_section == NULL || trading_opt == NULL)
|
||||
{
|
||||
PWARN ("Unable to find trading account preference");
|
||||
}
|
||||
}
|
||||
|
||||
kvp_val = kvp_frame_get_slot_path (qof_book_get_slots (book), options_name, acct_section,
|
||||
trading_opt, NULL);
|
||||
if (kvp_val == NULL)
|
||||
return FALSE;
|
||||
|
||||
opt = kvp_value_get_string (kvp_val);
|
||||
|
||||
if (opt && opt[0] == 't' && opt[1] == 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* QofObject function implementation and registration */
|
||||
gboolean qof_book_register (void)
|
||||
{
|
||||
|
@ -213,6 +213,9 @@ void qof_book_set_data_fin (QofBook *book, const gchar *key, gpointer data,
|
||||
/** Retrieves arbitrary pointers to structs stored by qof_book_set_data. */
|
||||
gpointer qof_book_get_data (const QofBook *book, const gchar *key);
|
||||
|
||||
/** Returns flag indicating whether this book uses trading accounts */
|
||||
gboolean qof_book_use_trading_accounts (const QofBook *book);
|
||||
|
||||
/** Is the book shutting down? */
|
||||
gboolean qof_book_shutting_down (const QofBook *book);
|
||||
|
||||
|
@ -281,6 +281,9 @@ gnc_get_reg_type (Account *leader, GNCLedgerDisplayType ld_type)
|
||||
|
||||
case ACCT_TYPE_CURRENCY:
|
||||
return CURRENCY_REGISTER;
|
||||
|
||||
case ACCT_TYPE_TRADING:
|
||||
return TRADING_REGISTER;
|
||||
|
||||
default:
|
||||
PERR ("unknown account type %d\n", account_type);
|
||||
@ -327,6 +330,7 @@ gnc_get_reg_type (Account *leader, GNCLedgerDisplayType ld_type)
|
||||
break;
|
||||
|
||||
case ACCT_TYPE_EQUITY:
|
||||
case ACCT_TYPE_TRADING:
|
||||
reg_type = GENERAL_LEDGER;
|
||||
break;
|
||||
|
||||
|
@ -59,12 +59,40 @@ gnc_split_register_balance_trans (SplitRegister *reg, Transaction *trans)
|
||||
Split *split;
|
||||
Split *other_split;
|
||||
gboolean two_accounts;
|
||||
gboolean multi_currency;
|
||||
|
||||
|
||||
imbalance = xaccTransGetImbalance (trans);
|
||||
if (gnc_numeric_zero_p (imbalance))
|
||||
return FALSE;
|
||||
|
||||
if (xaccTransIsBalanced (trans))
|
||||
return FALSE;
|
||||
|
||||
if (xaccTransUseTradingAccounts (trans)) {
|
||||
MonetaryList *imbal_list;
|
||||
gnc_monetary *imbal_mon;
|
||||
imbal_list = xaccTransGetImbalance (trans);
|
||||
|
||||
/* See if the imbalance is only in the transaction's currency */
|
||||
if (!imbal_list)
|
||||
/* Value imbalance, but not commodity imbalance. This shouldn't
|
||||
be something that scrubbing can cause to happen. Perhaps someone
|
||||
entered invalid splits. */
|
||||
multi_currency = TRUE;
|
||||
else {
|
||||
imbal_mon = imbal_list->data;
|
||||
if (!imbal_list->next &&
|
||||
gnc_commodity_equiv(gnc_monetary_commodity(*imbal_mon),
|
||||
xaccTransGetCurrency(trans)))
|
||||
multi_currency = FALSE;
|
||||
else
|
||||
multi_currency = TRUE;
|
||||
}
|
||||
|
||||
/* We're done with the imbalance list, the real work will be done
|
||||
by xaccTransScrubImbalance which will get it again. */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
}
|
||||
else
|
||||
multi_currency = FALSE;
|
||||
|
||||
split = xaccTransGetSplit (trans, 0);
|
||||
other_split = xaccSplitGetOtherSplit (split);
|
||||
|
||||
@ -75,7 +103,7 @@ gnc_split_register_balance_trans (SplitRegister *reg, Transaction *trans)
|
||||
if (split) other_split = xaccSplitGetOtherSplit (split);
|
||||
else split = xaccTransGetSplit (trans, 0);
|
||||
}
|
||||
if (other_split == NULL)
|
||||
if (other_split == NULL || multi_currency)
|
||||
{
|
||||
two_accounts = FALSE;
|
||||
other_account = NULL;
|
||||
@ -107,7 +135,7 @@ gnc_split_register_balance_trans (SplitRegister *reg, Transaction *trans)
|
||||
radio_list = g_list_append (radio_list,
|
||||
_("Let GnuCash _add an adjusting split"));
|
||||
|
||||
if (reg->type < NUM_SINGLE_REGISTER_TYPES)
|
||||
if (reg->type < NUM_SINGLE_REGISTER_TYPES && !multi_currency)
|
||||
{
|
||||
radio_list = g_list_append (radio_list,
|
||||
_("Adjust current account _split total"));
|
||||
|
@ -92,6 +92,7 @@ gnc_split_register_set_cells (SplitRegister *reg, TableLayout *layout)
|
||||
case INCOME_REGISTER:
|
||||
case EXPENSE_REGISTER:
|
||||
case EQUITY_REGISTER:
|
||||
case TRADING_REGISTER:
|
||||
{
|
||||
curs = gnc_table_layout_get_cursor (layout,
|
||||
CURSOR_SINGLE_LEDGER);
|
||||
@ -458,6 +459,7 @@ gnc_split_register_layout_add_cursors (SplitRegister *reg,
|
||||
case INCOME_REGISTER:
|
||||
case EXPENSE_REGISTER:
|
||||
case EQUITY_REGISTER:
|
||||
case TRADING_REGISTER:
|
||||
num_cols = 9;
|
||||
break;
|
||||
|
||||
|
@ -411,9 +411,66 @@ gnc_split_register_save_amount_values (SRSaveData *sd, SplitRegister *reg)
|
||||
{
|
||||
Account *acc;
|
||||
gnc_numeric new_amount, convrate, amtconv, value;
|
||||
gnc_commodity *curr, *reg_com, *xfer_com;
|
||||
Account *xfer_acc;
|
||||
|
||||
new_amount = gnc_split_register_debcred_cell_value (reg);
|
||||
acc = gnc_split_register_get_default_account (reg);
|
||||
|
||||
xfer_acc = xaccSplitGetAccount (sd->split);
|
||||
xfer_com = xaccAccountGetCommodity (xfer_acc);
|
||||
reg_com = xaccAccountGetCommodity (acc);
|
||||
curr = xaccTransGetCurrency (sd->trans);
|
||||
|
||||
/* First, compute the conversion rate to convert the value to the
|
||||
* amount.
|
||||
*/
|
||||
amtconv = convrate = gnc_split_register_get_rate_cell (reg, RATE_CELL);
|
||||
if (gnc_split_register_needs_conv_rate (reg, sd->trans, acc)) {
|
||||
|
||||
/* If we are in an expanded register and the xfer_acc->comm !=
|
||||
* reg_acc->comm then we need to compute the convrate here.
|
||||
* Otherwise, we _can_ use the rate_cell!
|
||||
*/
|
||||
if (sd->reg_expanded && ! gnc_commodity_equal (reg_com, xfer_com))
|
||||
amtconv = xaccTransGetAccountConvRate(sd->trans, acc);
|
||||
}
|
||||
|
||||
if (xaccTransUseTradingAccounts (sd->trans)) {
|
||||
/* Using currency accounts, the amount is probably really the
|
||||
amount and not the value. */
|
||||
gboolean is_amount;
|
||||
if (reg->type == STOCK_REGISTER ||
|
||||
reg->type == CURRENCY_REGISTER ||
|
||||
reg->type == PORTFOLIO_LEDGER) {
|
||||
if (xaccAccountIsPriced(xfer_acc) ||
|
||||
!gnc_commodity_is_iso(xaccAccountGetCommodity(xfer_acc)))
|
||||
is_amount = FALSE;
|
||||
else
|
||||
is_amount = TRUE;
|
||||
}
|
||||
else {
|
||||
is_amount = TRUE;
|
||||
}
|
||||
|
||||
if (is_amount) {
|
||||
xaccSplitSetAmount(sd->split, new_amount);
|
||||
if (gnc_split_register_split_needs_amount (reg, sd->split)) {
|
||||
value = gnc_numeric_div(new_amount, amtconv,
|
||||
gnc_commodity_get_fraction(curr),
|
||||
GNC_RND_ROUND);
|
||||
xaccSplitSetValue(sd->split, value);
|
||||
}
|
||||
else
|
||||
xaccSplitSetValue(sd->split, new_amount);
|
||||
}
|
||||
else {
|
||||
xaccSplitSetValue(sd->split, new_amount);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* How to interpret new_amount depends on our view of this
|
||||
* transaction. If we're sitting in an account with the same
|
||||
* commodity as the transaction, then we can set the Value and then
|
||||
@ -422,34 +479,12 @@ gnc_split_register_save_amount_values (SRSaveData *sd, SplitRegister *reg)
|
||||
* 'value' by dividing by the convrate in order to set the value.
|
||||
*/
|
||||
|
||||
/* First, compute the conversion rate to convert the value to the
|
||||
* amount.
|
||||
*/
|
||||
convrate = gnc_split_register_get_rate_cell (reg, RATE_CELL);
|
||||
|
||||
/* Now compute/set the split value. Amount is in the register
|
||||
* currency but we need to convert to the txn currency.
|
||||
*/
|
||||
acc = gnc_split_register_get_default_account (reg);
|
||||
if (gnc_split_register_needs_conv_rate (reg, sd->trans, acc)) {
|
||||
gnc_commodity *curr, *reg_com, *xfer_com;
|
||||
Account *xfer_acc;
|
||||
|
||||
xfer_acc = xaccSplitGetAccount (sd->split);
|
||||
xfer_com = xaccAccountGetCommodity (xfer_acc);
|
||||
reg_com = xaccAccountGetCommodity (acc);
|
||||
|
||||
/* If we are in an expanded register and the xfer_acc->comm !=
|
||||
* reg_acc->comm then we need to compute the convrate here.
|
||||
* Otherwise, we _can_ use the rate_cell!
|
||||
*/
|
||||
if (sd->reg_expanded && ! gnc_commodity_equal (reg_com, xfer_com))
|
||||
amtconv = xaccTransGetAccountConvRate(sd->trans, acc);
|
||||
else
|
||||
amtconv = convrate;
|
||||
|
||||
/* convert the amount to the Value ... */
|
||||
curr = xaccTransGetCurrency (sd->trans);
|
||||
value = gnc_numeric_div (new_amount, amtconv,
|
||||
gnc_commodity_get_fraction (curr),
|
||||
GNC_RND_ROUND);
|
||||
|
@ -163,6 +163,11 @@ gnc_split_register_use_security_cells (SplitRegister *reg,
|
||||
if (!account)
|
||||
return TRUE;
|
||||
|
||||
if (xaccTransUseTradingAccounts (xaccSplitGetParent (split))) {
|
||||
if (!gnc_commodity_is_iso(xaccAccountGetCommodity(account)))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return xaccAccountIsPriced(account);
|
||||
}
|
||||
|
||||
@ -687,7 +692,7 @@ gnc_split_register_get_debcred_bg_color (VirtualLocation virt_loc,
|
||||
trans = gnc_split_register_get_trans (reg, virt_loc.vcell_loc);
|
||||
|
||||
if (trans)
|
||||
*hatching = !gnc_numeric_zero_p (xaccTransGetImbalance (trans));
|
||||
*hatching = !xaccTransIsBalanced (trans);
|
||||
else
|
||||
*hatching = FALSE;
|
||||
}
|
||||
@ -1504,6 +1509,7 @@ gnc_split_reg_has_rate_cell (SplitRegisterType type)
|
||||
case INCOME_REGISTER:
|
||||
case EXPENSE_REGISTER:
|
||||
case EQUITY_REGISTER:
|
||||
case TRADING_REGISTER:
|
||||
case GENERAL_LEDGER:
|
||||
case INCOME_LEDGER:
|
||||
case PORTFOLIO_LEDGER:
|
||||
@ -1569,10 +1575,43 @@ gnc_split_register_get_debcred_entry (VirtualLocation virt_loc,
|
||||
gnc_numeric imbalance;
|
||||
Account *acc;
|
||||
|
||||
imbalance = xaccTransGetImbalance (trans);
|
||||
|
||||
imbalance = xaccTransGetImbalanceValue (trans);
|
||||
|
||||
if (gnc_numeric_zero_p (imbalance))
|
||||
return NULL;
|
||||
|
||||
if (xaccTransUseTradingAccounts (trans)) {
|
||||
MonetaryList *imbal_list;
|
||||
gnc_monetary *imbal_mon;
|
||||
imbal_list = xaccTransGetImbalance (trans);
|
||||
|
||||
if (!imbal_list) {
|
||||
/* No commodity imbalance, there shouldn't be a value imablance. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (imbal_list->next) {
|
||||
/* Multiple currency imbalance. */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
imbal_mon = imbal_list->data;
|
||||
if (!gnc_commodity_equal(gnc_monetary_commodity(*imbal_mon), currency)) {
|
||||
/* Imbalance is in wrong currency */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!gnc_numeric_equal (gnc_monetary_value(*imbal_mon), imbalance)) {
|
||||
/* Value and commodity imbalances differ */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Done with the imbalance list */
|
||||
gnc_monetary_list_free(imbal_list);
|
||||
}
|
||||
|
||||
imbalance = gnc_numeric_neg (imbalance);
|
||||
|
||||
@ -1604,33 +1643,74 @@ gnc_split_register_get_debcred_entry (VirtualLocation virt_loc,
|
||||
|
||||
{
|
||||
gnc_numeric amount;
|
||||
gnc_commodity *split_commodity;
|
||||
GNCPrintAmountInfo print_info;
|
||||
Account *account;
|
||||
gnc_commodity * commodity;
|
||||
|
||||
account = gnc_split_register_get_default_account (reg);
|
||||
commodity = xaccAccountGetCommodity (account);
|
||||
split_commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
|
||||
|
||||
/* If this account is not a stock/mutual/currency account, and
|
||||
* currency != the account commodity, then use the SplitAmount
|
||||
* instead of the SplitValue.
|
||||
*/
|
||||
switch (reg->type) {
|
||||
case STOCK_REGISTER:
|
||||
case CURRENCY_REGISTER:
|
||||
amount = xaccSplitGetValue (split);
|
||||
print_info = gnc_commodity_print_info (currency, FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
Account *account;
|
||||
gnc_commodity * commodity;
|
||||
|
||||
account = gnc_split_register_get_default_account (reg);
|
||||
commodity = xaccAccountGetCommodity (account);
|
||||
|
||||
if (commodity && !gnc_commodity_equal (commodity, currency))
|
||||
/* Convert this to the "local" value */
|
||||
amount = xaccSplitConvertAmount(split, account);
|
||||
else
|
||||
amount = xaccSplitGetValue (split);
|
||||
print_info = gnc_account_print_info (account, FALSE);
|
||||
if (xaccTransUseTradingAccounts (trans)) {
|
||||
gboolean use_symbol, is_current;
|
||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||
virt_loc.vcell_loc);
|
||||
|
||||
if (reg->type == STOCK_REGISTER ||
|
||||
reg->type == CURRENCY_REGISTER ||
|
||||
reg->type == PORTFOLIO_LEDGER) {
|
||||
gnc_commodity *amount_commodity;
|
||||
/* security register. If this split has price and shares columns,
|
||||
use the value, otherwise use the amount. */
|
||||
if (gnc_split_register_use_security_cells(reg, virt_loc)) {
|
||||
amount = xaccSplitGetValue(split);
|
||||
amount_commodity = currency;
|
||||
}
|
||||
else {
|
||||
amount = xaccSplitGetAmount(split);
|
||||
amount_commodity = split_commodity;
|
||||
}
|
||||
/* Show the currency if it is not the default currency */
|
||||
if (is_current ||
|
||||
gnc_commodity_equiv(amount_commodity, gnc_default_currency()))
|
||||
use_symbol = FALSE;
|
||||
else
|
||||
use_symbol = TRUE;
|
||||
print_info = gnc_commodity_print_info(amount_commodity, use_symbol);
|
||||
}
|
||||
else {
|
||||
/* non-security register, always use the split amount. */
|
||||
amount = xaccSplitGetAmount(split);
|
||||
if (is_current ||
|
||||
gnc_commodity_equiv(split_commodity, commodity))
|
||||
use_symbol = FALSE;
|
||||
else
|
||||
use_symbol = TRUE;
|
||||
print_info = gnc_commodity_print_info(split_commodity, use_symbol);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If this account is not a stock/mutual/currency account, and
|
||||
* currency != the account commodity, then use the SplitAmount
|
||||
* instead of the SplitValue.
|
||||
*/
|
||||
switch (reg->type) {
|
||||
case STOCK_REGISTER:
|
||||
case CURRENCY_REGISTER:
|
||||
amount = xaccSplitGetValue (split);
|
||||
print_info = gnc_commodity_print_info (currency, FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (commodity && !gnc_commodity_equal (commodity, currency))
|
||||
/* Convert this to the "local" value */
|
||||
amount = xaccSplitConvertAmount(split, account);
|
||||
else
|
||||
amount = xaccSplitGetValue (split);
|
||||
print_info = gnc_account_print_info (account, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1782,6 +1782,16 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
|
||||
if (!price_changed && !amount_changed && !shares_changed)
|
||||
return TRUE;
|
||||
|
||||
/* If we are using commodity trading accounts then the value may
|
||||
not really be the value. Punt if so. */
|
||||
if (xaccTransUseTradingAccounts (xaccSplitGetParent (split))) {
|
||||
gnc_commodity *acc_commodity;
|
||||
acc_commodity = xaccAccountGetCommodity (account);
|
||||
if (! (xaccAccountIsPriced (account) ||
|
||||
!gnc_commodity_is_iso (acc_commodity)))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (shares_changed)
|
||||
{
|
||||
cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
|
||||
@ -2053,6 +2063,8 @@ gnc_split_register_type_to_account_type (SplitRegisterType sr_type)
|
||||
return ACCT_TYPE_STOCK;
|
||||
case CURRENCY_REGISTER:
|
||||
return ACCT_TYPE_CURRENCY;
|
||||
case TRADING_REGISTER:
|
||||
return ACCT_TYPE_TRADING;
|
||||
case GENERAL_LEDGER:
|
||||
return ACCT_TYPE_NONE;
|
||||
case EQUITY_REGISTER:
|
||||
@ -2253,6 +2265,7 @@ gnc_split_register_config_action (SplitRegister *reg)
|
||||
gnc_combo_cell_add_menu_item (cell, _("Paycheck"));
|
||||
break;
|
||||
case EXPENSE_REGISTER:
|
||||
case TRADING_REGISTER:
|
||||
gnc_combo_cell_add_menu_item (cell, _("Increase"));
|
||||
gnc_combo_cell_add_menu_item (cell, _("Decrease"));
|
||||
gnc_combo_cell_add_menu_item (cell, _("Buy"));
|
||||
|
@ -163,6 +163,7 @@ typedef enum
|
||||
CURRENCY_REGISTER,
|
||||
RECEIVABLE_REGISTER,
|
||||
PAYABLE_REGISTER,
|
||||
TRADING_REGISTER,
|
||||
NUM_SINGLE_REGISTER_TYPES,
|
||||
|
||||
GENERAL_LEDGER = NUM_SINGLE_REGISTER_TYPES,
|
||||
|
@ -96,7 +96,8 @@
|
||||
ACCT-TYPE-CREDITLINE))
|
||||
(cons ACCT-TYPE-EQUITY (list ACCT-TYPE-EQUITY))
|
||||
(cons ACCT-TYPE-INCOME (list ACCT-TYPE-INCOME))
|
||||
(cons ACCT-TYPE-EXPENSE (list ACCT-TYPE-EXPENSE)))))
|
||||
(cons ACCT-TYPE-EXPENSE (list ACCT-TYPE-EXPENSE))
|
||||
(cons ACCT-TYPE-TRADING (list ACCT-TYPE-TRADING)))))
|
||||
|
||||
;; Returns the name of the account type as a string, and in its plural
|
||||
;; form (as opposed to xaccAccountGetTypeStr which gives the
|
||||
@ -120,7 +121,8 @@
|
||||
(cons ACCT-TYPE-MONEYMRKT (_ "Money Market"))
|
||||
(cons ACCT-TYPE-RECEIVABLE (_ "Accounts Receivable"))
|
||||
(cons ACCT-TYPE-PAYABLE (_ "Accounts Payable"))
|
||||
(cons ACCT-TYPE-CREDITLINE (_ "Credit Lines")))
|
||||
(cons ACCT-TYPE-CREDITLINE (_ "Credit Lines"))
|
||||
(cons ACCT-TYPE-TRADING (_ "Trading Accounts")))
|
||||
type))
|
||||
|
||||
;; Get the list of all different commodities that are used within the
|
||||
|
@ -411,6 +411,7 @@
|
||||
(lambda (s)
|
||||
(if (and (not (or (split-account-type? s ACCT-TYPE-EXPENSE)
|
||||
(split-account-type? s ACCT-TYPE-INCOME)
|
||||
(split-account-type? s ACCT-TYPE-TRADING)
|
||||
(split-account-type? s ACCT-TYPE-ROOT)))
|
||||
(not (same-account? current (xaccSplitGetAccount s))))
|
||||
(begin
|
||||
|
@ -189,7 +189,8 @@
|
||||
ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY
|
||||
ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY
|
||||
ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE
|
||||
ACCT-TYPE-TRADING)
|
||||
(gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
|
||||
#f #t))
|
||||
(gnc:options-add-account-levels!
|
||||
@ -358,6 +359,8 @@
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-EXPENSE)))
|
||||
(equity-accounts
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-EQUITY))
|
||||
(trading-accounts
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-TRADING))
|
||||
|
||||
(doc (gnc:make-html-document))
|
||||
;; this can occasionally put extra (blank) columns in our
|
||||
@ -441,6 +444,8 @@
|
||||
(equity-balance #f)
|
||||
(neg-retained-earnings #f) ;; credit, income - expenses, < 0
|
||||
(retained-earnings #f)
|
||||
(neg-trading-balance #f)
|
||||
(trading-balance #f)
|
||||
(unrealized-gain-collector #f)
|
||||
(total-equity-balance #f)
|
||||
(liability-plus-equity #f)
|
||||
@ -501,6 +506,13 @@
|
||||
(retained-earnings 'minusmerge
|
||||
neg-retained-earnings
|
||||
#f)
|
||||
(set! neg-trading-balance
|
||||
(gnc:accountlist-get-comm-balance-at-date
|
||||
trading-accounts date-tp))
|
||||
(set! trading-balance (gnc:make-commodity-collector))
|
||||
(trading-balance 'minusmerge
|
||||
neg-trading-balance
|
||||
#f)
|
||||
(gnc:report-percent-done 14)
|
||||
;; sum any unrealized gains
|
||||
;;
|
||||
@ -537,6 +549,9 @@
|
||||
(total-equity-balance 'merge
|
||||
unrealized-gain-collector
|
||||
#f)
|
||||
(total-equity-balance 'merge
|
||||
trading-balance
|
||||
#f)
|
||||
(gnc:report-percent-done 18)
|
||||
(set! liability-plus-equity (gnc:make-commodity-collector))
|
||||
(liability-plus-equity 'merge
|
||||
@ -649,6 +664,12 @@
|
||||
(_ "Retained Earnings")
|
||||
(_ "Retained Losses")
|
||||
retained-earnings))
|
||||
(and (not (gnc-commodity-collector-allzero?
|
||||
trading-balance))
|
||||
(add-subtotal-line right-table
|
||||
(_ "Trading Gains")
|
||||
(_ "Trading Losses")
|
||||
trading-balance))
|
||||
(and (not (gnc-commodity-collector-allzero?
|
||||
unrealized-gain-collector))
|
||||
(add-subtotal-line right-table
|
||||
|
@ -133,7 +133,8 @@
|
||||
ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY
|
||||
ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY
|
||||
ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE
|
||||
ACCT-TYPE-TRADING)
|
||||
(gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
|
||||
#f #t))
|
||||
|
||||
@ -245,7 +246,8 @@
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-LIABILITY))
|
||||
(income-expense-accounts
|
||||
(append (assoc-ref split-up-accounts ACCT-TYPE-INCOME)
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-EXPENSE)))
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-EXPENSE)
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-TRADING)))
|
||||
(equity-accounts
|
||||
(assoc-ref split-up-accounts ACCT-TYPE-EQUITY))
|
||||
|
||||
|
@ -612,7 +612,8 @@
|
||||
ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY
|
||||
ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY
|
||||
ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE
|
||||
ACCT-TYPE-TRADING)
|
||||
(gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
|
||||
#f #t))
|
||||
|
||||
|
@ -184,7 +184,8 @@
|
||||
ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY
|
||||
ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY
|
||||
ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)
|
||||
ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE
|
||||
ACCT-TYPE-TRADING)
|
||||
(gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
|
||||
#f #t))
|
||||
(gnc:options-add-account-levels!
|
||||
|
Loading…
Reference in New Issue
Block a user