static gnc_commodity * xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book) { gnc_commodity *ra, *rb, *retval; Split *split; if (!trans) return NULL; if (trans->splits == NULL) return NULL; g_return_val_if_fail (book, NULL); split = trans->splits->data; if (!split || NULL == split->acc) return NULL; ra = DxaccAccountGetCurrency (split->acc); rb = xaccAccountGetCommodity (split->acc); retval = FindCommonCurrency (trans->splits, ra, rb); if (retval && !gnc_commodity_is_currency(retval)) retval = NULL; return retval; }
void xaccTransScrubCurrency (Transaction *trans) { SplitList *node; gnc_commodity *currency; if (!trans) return; /* If there are any orphaned splits in a transaction, then the * this routine will fail. Therefore, we want to make sure that * there are no orphans (splits without parent account). */ xaccTransScrubOrphans (trans); currency = xaccTransGetCurrency (trans); if (currency && gnc_commodity_is_currency(currency)) return; currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans)); if (currency) { xaccTransBeginEdit (trans); xaccTransSetCurrency (trans, currency); xaccTransCommitEdit (trans); } else { if (NULL == trans->splits) { PWARN ("Transaction \"%s\" has no splits in it!", trans->description); } else { SplitList *node; char guid_str[GUID_ENCODING_LENGTH + 1]; guid_to_string_buff(xaccTransGetGUID(trans), guid_str); PWARN ("no common transaction currency found for trans=\"%s\" (%s);", trans->description, guid_str); for (node = trans->splits; node; node = node->next) { Split *split = node->data; if (NULL == split->acc) { PWARN (" split=\"%s\" is not in any account!", split->memo); } else { gnc_commodity *currency = xaccAccountGetCommodity(split->acc); PWARN ("setting to split=\"%s\" account=\"%s\" commodity=\"%s\"", split->memo, xaccAccountGetName(split->acc), gnc_commodity_get_mnemonic(currency)); xaccTransBeginEdit (trans); xaccTransSetCurrency (trans, currency); xaccTransCommitEdit (trans); return; } } } return; } for (node = trans->splits; node; node = node->next) { Split *sp = node->data; if (!gnc_numeric_equal(xaccSplitGetAmount (sp), xaccSplitGetValue (sp))) { gnc_commodity *acc_currency; acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : NULL; if (acc_currency == currency) { /* This Split needs fixing: The transaction-currency equals * the account-currency/commodity, but the amount/values are * inequal i.e. they still correspond to the security * (amount) and the currency (value). In the new model, the * value is the amount in the account-commodity -- so it * needs to be set to equal the amount (since the * account-currency doesn't exist anymore). * * Note: Nevertheless we lose some information here. Namely, * the information that the 'amount' in 'account-old-security' * was worth 'value' in 'account-old-currency'. Maybe it would * be better to store that information in the price database? * But then, for old currency transactions there is still the * 'other' transaction, which is going to keep that * information. So I don't bother with that here. -- cstim, * 2002/11/20. */ PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" " old amount %s %s, new amount %s", trans->description, sp->memo, gnc_num_dbg_to_string (xaccSplitGetAmount(sp)), gnc_commodity_get_mnemonic (currency), gnc_num_dbg_to_string (xaccSplitGetValue(sp))); xaccTransBeginEdit (trans); xaccSplitSetAmount (sp, xaccSplitGetValue(sp)); xaccTransCommitEdit (trans); } /*else { PINFO ("Ok: Split '%s' Amount %s %s, value %s %s", xaccSplitGetMemo (sp), gnc_num_dbg_to_string (amount), gnc_commodity_get_mnemonic (currency), gnc_num_dbg_to_string (value), gnc_commodity_get_mnemonic (acc_currency)); }*/ } } }
/** * @fixme Move this non-GUI code into the engine. **/ static void gnc_ui_accounts_recurse (Account *parent, GList **currency_list, GNCSummarybarOptions options) { gnc_numeric start_amount; gnc_numeric start_amount_default_currency; gnc_numeric end_amount; gnc_numeric end_amount_default_currency; GNCAccountType account_type; gnc_commodity * account_currency; gnc_commodity * euro_commodity; GNCCurrencyAcc *currency_accum = NULL; GNCCurrencyAcc *euro_accum = NULL; GNCCurrencyAcc *grand_total_accum = NULL; GNCCurrencyAcc *non_curr_accum = NULL; GList *children, *node; gboolean non_currency = FALSE; Timespec end_timespec; Timespec start_timespec; if (parent == NULL) return; children = gnc_account_get_children(parent); for (node = children; node; node = g_list_next(node)) { Account *account = node->data; account_type = xaccAccountGetType(account); account_currency = xaccAccountGetCommodity(account); if (options.grand_total) grand_total_accum = gnc_ui_get_currency_accumulator(currency_list, options.default_currency, TOTAL_GRAND_TOTAL); if (options.euro) { euro_commodity = gnc_get_euro (); euro_accum = gnc_ui_get_currency_accumulator(currency_list, euro_commodity, TOTAL_CURR_TOTAL); } else euro_commodity = NULL; if (!gnc_commodity_is_currency(account_currency)) { non_currency = TRUE; non_curr_accum = gnc_ui_get_currency_accumulator(currency_list, options.default_currency, TOTAL_NON_CURR_TOTAL); } if (!non_currency || options.non_currency) { currency_accum = gnc_ui_get_currency_accumulator(currency_list, account_currency, TOTAL_SINGLE); } switch (account_type) { case ACCT_TYPE_BANK: case ACCT_TYPE_CASH: case ACCT_TYPE_ASSET: case ACCT_TYPE_STOCK: case ACCT_TYPE_MUTUAL: case ACCT_TYPE_CREDIT: case ACCT_TYPE_LIABILITY: case ACCT_TYPE_PAYABLE: case ACCT_TYPE_RECEIVABLE: end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date); timespecFromTime_t(&end_timespec, options.end_date); end_amount_default_currency = xaccAccountConvertBalanceToCurrencyAsOfDate (account, end_amount, account_currency, options.default_currency, timespecToTime_t(timespecCanonicalDayTime(end_timespec))); if (!non_currency || options.non_currency) { currency_accum->assets = gnc_numeric_add (currency_accum->assets, end_amount, gnc_commodity_get_fraction (account_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (non_currency) { non_curr_accum->assets = gnc_numeric_add (non_curr_accum->assets, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (options.grand_total) { grand_total_accum->assets = gnc_numeric_add (grand_total_accum->assets, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (options.euro && (currency_accum != euro_accum)) { euro_accum->assets = gnc_numeric_add (euro_accum->assets, gnc_convert_to_euro(account_currency, end_amount), gnc_commodity_get_fraction (euro_commodity), GNC_HOW_RND_ROUND_HALF_UP); } gnc_ui_accounts_recurse(account, currency_list, options); break; case ACCT_TYPE_INCOME: case ACCT_TYPE_EXPENSE: start_amount = xaccAccountGetBalanceAsOfDate(account, options.start_date); timespecFromTime_t(&start_timespec, options.start_date); start_amount_default_currency = xaccAccountConvertBalanceToCurrencyAsOfDate (account, start_amount, account_currency, options.default_currency, timespecToTime_t(timespecCanonicalDayTime(start_timespec))); end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date); timespecFromTime_t(&end_timespec, options.end_date); end_amount_default_currency = xaccAccountConvertBalanceToCurrencyAsOfDate (account, end_amount, account_currency, options.default_currency, timespecToTime_t(timespecCanonicalDayTime(end_timespec))); if (!non_currency || options.non_currency) { currency_accum->profits = gnc_numeric_add (currency_accum->profits, start_amount, gnc_commodity_get_fraction (account_currency), GNC_HOW_RND_ROUND_HALF_UP); currency_accum->profits = gnc_numeric_sub (currency_accum->profits, end_amount, gnc_commodity_get_fraction (account_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (non_currency) { non_curr_accum->profits = gnc_numeric_add (non_curr_accum->profits, start_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); non_curr_accum->profits = gnc_numeric_sub (non_curr_accum->profits, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (options.grand_total) { grand_total_accum->profits = gnc_numeric_add (grand_total_accum->profits, start_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); grand_total_accum->profits = gnc_numeric_sub (grand_total_accum->profits, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (options.euro && (currency_accum != euro_accum)) { euro_accum->profits = gnc_numeric_add (euro_accum->profits, gnc_convert_to_euro(account_currency, start_amount), gnc_commodity_get_fraction (euro_commodity), GNC_HOW_RND_ROUND_HALF_UP); euro_accum->profits = gnc_numeric_sub (euro_accum->profits, gnc_convert_to_euro(account_currency, end_amount), gnc_commodity_get_fraction (euro_commodity), GNC_HOW_RND_ROUND_HALF_UP); } gnc_ui_accounts_recurse(account, currency_list, options); break; 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; } } g_list_free(children); }
static gnc_commodity * xaccTransFindCommonCurrency (Transaction *trans, QofBook *book) { gnc_commodity *com_scratch; GList *node = NULL; GSList *comlist = NULL, *found = NULL; if (!trans) return NULL; if (trans->splits == NULL) return NULL; g_return_val_if_fail (book, NULL); /* Find the most commonly used currency among the splits. If a given split is in a non-currency commodity, then look for an ancestor account in a currency, but prefer currencies used directly in splits. Ignore trading account splits in this whole process, they don't add any value to this algorithm. */ for (node = trans->splits; node; node = node->next) { Split *s = node->data; unsigned int curr_weight; if (s == NULL || s->acc == NULL) continue; if (xaccAccountGetType(s->acc) == ACCT_TYPE_TRADING) continue; com_scratch = xaccAccountGetCommodity(s->acc); if (com_scratch && gnc_commodity_is_currency(com_scratch)) { curr_weight = 3; } else { com_scratch = gnc_account_get_currency_or_parent(s->acc); if (com_scratch == NULL) continue; curr_weight = 1; } if ( comlist ) { found = g_slist_find_custom(comlist, com_scratch, commodity_equal); } if (comlist == NULL || found == NULL) { CommodityCount *count = g_slice_new0(CommodityCount); count->commodity = com_scratch; count->count = curr_weight; comlist = g_slist_append(comlist, count); } else { CommodityCount *count = (CommodityCount*)(found->data); count->count += curr_weight; } } found = g_slist_sort( comlist, commodity_compare); if ( found && found->data && (((CommodityCount*)(found->data))->commodity != NULL)) { return ((CommodityCount*)(found->data))->commodity; } /* We didn't find a currency in the current account structure, so try * an old one. */ return xaccTransFindOldCommonCurrency( trans, book ); }
/* Takes the input with column and sets the price / amount / value so they are consistent */ void gnc_tree_util_set_number_for_input (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input, gint viewcol) { GncTreeModelSplitReg *model; gnc_numeric price; gnc_numeric amount; gnc_numeric value; gboolean price_changed = FALSE; // Price of each share gboolean value_changed = FALSE; // Total value of shares gboolean amount_changed = FALSE; // No of shares gboolean recalc_amount = FALSE; gboolean recalc_price = FALSE; gboolean recalc_value = FALSE; gboolean expanded = FALSE; int denom; Account *account = NULL; ENTER("trans %p and split %p and input is %s and viewcol is %d", trans, split, gnc_numeric_to_string (input), viewcol); model = gnc_tree_view_split_reg_get_model_from_view (view); /* Check for sub account view */ if (!gnc_tree_model_split_reg_get_sub_account (model)) account = gnc_tree_model_split_reg_get_anchor (model); expanded = gnc_tree_view_split_reg_trans_expanded (view, trans); if (!account) account = xaccSplitGetAccount (split); if (!xaccAccountIsPriced (account)) return; /* 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; } if (gnc_numeric_zero_p (input)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE("zero"); return; } amount = xaccSplitGetAmount (split); value = xaccSplitGetValue (split); if (viewcol == COL_AMTVAL && !expanded) { value_changed = TRUE; if (gnc_numeric_zero_p (amount)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } else if (viewcol == COL_AMTVAL && expanded) { amount_changed = TRUE; if (gnc_numeric_zero_p (value)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } if (viewcol == COL_PRICE) { price_changed = TRUE; if (gnc_numeric_zero_p (value)) { amount = gnc_numeric_create (1,1); value = gnc_numeric_mul (input, amount, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); xaccSplitSetValue (split, input); xaccSplitSetAmount (split, amount); LEAVE(""); return; } } if ((viewcol == COL_CREDIT || viewcol == COL_DEBIT) && !expanded) { amount_changed = TRUE; if (gnc_numeric_zero_p (value)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } else if ((viewcol == COL_CREDIT || viewcol == COL_DEBIT) && expanded) { value_changed = TRUE; if (gnc_numeric_zero_p (value)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } DEBUG("value_changed %d, price_changed %d, amount_changed %d", value_changed, price_changed, amount_changed); { int choice; int default_value; GList *node; GList *radio_list = NULL; const char *title = _("Recalculate Transaction"); const char *message = _("The values entered for this transaction " "are inconsistent. Which value would you " "like to have recalculated?"); if (amount_changed) radio_list = g_list_append (radio_list, g_strdup_printf ("%s (%s)", _("_Shares"), _("Changed"))); else radio_list = g_list_append (radio_list, g_strdup (_("_Shares"))); if (price_changed) radio_list = g_list_append (radio_list, g_strdup_printf ("%s (%s)", _("_Price"), _("Changed"))); else radio_list = g_list_append (radio_list, g_strdup (_("_Price"))); if (value_changed) radio_list = g_list_append (radio_list, g_strdup_printf ("%s (%s)", _("_Value"), _("Changed"))); else radio_list = g_list_append (radio_list, g_strdup (_("_Value"))); if(expanded) { if (price_changed) default_value = 2; /* change the value */ else default_value = 1; /* change the price */ } else { if (price_changed) default_value = 0; /* change the amount / shares */ else default_value = 1; /* change the price */ } choice = gnc_choose_radio_option_dialog (gnc_tree_view_split_reg_get_parent (view), title, message, _("_Recalculate"), default_value, radio_list); for (node = radio_list; node; node = node->next) g_free (node->data); g_list_free (radio_list); switch (choice) { case 0: /* Modify number of shares */ recalc_amount = TRUE; break; case 1: /* Modify the share price */ recalc_price = TRUE; break; case 2: /* Modify total value */ recalc_value = TRUE; break; default: /* Cancel */ LEAVE(" " ); return; } } DEBUG("recalc_value %d, recalc_price %d, recalc_amount %d", recalc_value, recalc_price, recalc_amount); if (recalc_amount) { denom = gtu_sr_get_amount_denom (split); if (amount_changed) { LEAVE(""); return; } if (price_changed) price = input; else price = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); if (value_changed) { xaccSplitSetValue (split, input); amount = gnc_numeric_div (input, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (split, amount); } else { amount = gnc_numeric_div (value, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (split, amount); } } if (recalc_price) { if (price_changed) { LEAVE(""); return; } if (amount_changed) { xaccSplitSetAmount (split, input); xaccSplitSetValue (split, value); } if (value_changed) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, amount); } } if (recalc_value) { denom = gtu_sr_get_value_denom (split); if (value_changed) { LEAVE(""); return; } if (price_changed) price = input; else price = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); if (amount_changed) { xaccSplitSetAmount (split, input); value = gnc_numeric_mul (input, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } else { value = gnc_numeric_mul (amount, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } } /* If the number of splits is two, change other split to balance */ if (!gnc_tree_util_split_reg_is_multi (split) && expanded) { Split *osplit; gnc_commodity *osplit_com; osplit = xaccSplitGetOtherSplit (split); value = xaccSplitGetValue (split); osplit_com = xaccAccountGetCommodity (xaccSplitGetAccount (osplit)); if (gnc_commodity_is_currency (osplit_com)) { xaccSplitSetValue (osplit, gnc_numeric_neg (value)); xaccSplitSetAmount (osplit, gnc_numeric_neg (value)); } } LEAVE(""); }