static gnc_numeric gnc_transaction_adjust_trading_splits (Transaction* trans, Account *root) { GList* splits; gnc_numeric imbalance = gnc_numeric_zero(); for (splits = trans->splits; splits; splits = splits->next) { Split *split = splits->data; Split *balance_split = NULL; gnc_numeric value, amount; gnc_commodity *commodity, *txn_curr = xaccTransGetCurrency (trans); 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_HOW_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(txn_curr), GNC_HOW_RND_ROUND_HALF_UP); if (! gnc_numeric_equal (old_value, new_value)) { xaccTransBeginEdit (trans); xaccSplitSetValue (balance_split, new_value); xaccSplitScrub (balance_split); xaccTransCommitEdit (trans); } } } return imbalance; }
/* Set the value for the given input amount */ void gnc_tree_util_set_value_for_amount (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input) { gnc_numeric split_rate; gnc_numeric amount; gnc_numeric value, new_value; int denom; ENTER("trans %p and split %p and input is %s", trans, split, gnc_numeric_to_string (input)); if (gnc_numeric_zero_p (input)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE("zero"); return; } amount = xaccSplitGetAmount (split); value = xaccSplitGetValue (split); denom = gtu_sr_get_value_denom (split); split_rate = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); if (gnc_numeric_check (split_rate) != GNC_ERROR_OK) split_rate = gnc_numeric_create (100,100); new_value = gnc_numeric_mul (input, split_rate, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, new_value); xaccSplitSetAmount (split, input); LEAVE(""); }
gnc_numeric gnc_numeric_div_with_error(gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric * error) { gnc_numeric quot = gnc_numeric_div(a, b, denom, how); gnc_numeric exact = gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); gnc_numeric err = gnc_numeric_sub(quot, exact, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); if (error) { *error = err; } return quot; }
gnc_numeric gnc_convert_to_euro(const gnc_commodity * currency, gnc_numeric value) { gnc_euro_rate_struct *result; if (currency == NULL) return gnc_numeric_zero (); if (!gnc_commodity_is_iso(currency)) return gnc_numeric_zero (); result = bsearch(currency, gnc_euro_rates, sizeof(gnc_euro_rates) / sizeof(gnc_euro_rate_struct), sizeof(gnc_euro_rate_struct), gnc_euro_rate_compare); if (result == NULL) return gnc_numeric_zero (); /* round to 2 decimal places */ { gnc_numeric rate; rate = double_to_gnc_numeric (result->rate, 100000, GNC_HOW_RND_ROUND_HALF_UP); /* EC Regulation 1103/97 states we should use "Round half away from zero" * See http://europa.eu/legislation_summaries/economic_and_monetary_affairs/institutional_and_economic_framework/l25025_en.htm */ return gnc_numeric_div (value, rate, 100, GNC_HOW_RND_ROUND_HALF_UP); } }
/* Get the rate */ gnc_numeric gnc_tree_util_get_rate_for (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gboolean is_blank) { gnc_numeric num; ENTER("trans %p and split %p is_blank %d", trans, split, is_blank); num = gnc_tree_util_split_reg_get_value_for (view, trans, split, is_blank); //FIXME Not sure about this... if (xaccTransUseTradingAccounts (trans)) num = gnc_numeric_div (num, xaccSplitGetValue (split), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); else num = gnc_numeric_div (xaccSplitGetAmount (split), num, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); LEAVE("split %p and return num is %s", split, gnc_numeric_to_string (num)); return num; }
// Share Price / Conversion factor static gchar* add_price (gchar *so_far, Split *split, gboolean t_void, CsvExportInfo *info) { const gchar *string_amount; gchar *conv; gchar *result; if (t_void) { gnc_numeric cf = gnc_numeric_div (xaccSplitVoidFormerValue (split), xaccSplitVoidFormerAmount (split), GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND_HALF_UP); string_amount = xaccPrintAmount (cf, gnc_split_amount_print_info (split, FALSE)); } else string_amount = xaccPrintAmount (xaccSplitGetSharePrice (split), gnc_split_amount_print_info (split, FALSE)); conv = csv_txn_test_field_string (info, string_amount); result = g_strconcat (so_far, conv, info->end_sep, EOLSTR, NULL); g_free (conv); return result; }
static void * numeric_ops(char op_sym, void *left_value, void *right_value) { ParserNum *left = left_value; ParserNum *right = right_value; ParserNum *result; if ((left == NULL) || (right == NULL)) return NULL; result = (op_sym == ASN_OP) ? left : g_new0(ParserNum, 1); switch (op_sym) { case ADD_OP: result->value = gnc_numeric_add (left->value, right->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); break; case SUB_OP: result->value = gnc_numeric_sub (left->value, right->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); break; case DIV_OP: result->value = gnc_numeric_div (left->value, right->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); break; case MUL_OP: result->value = gnc_numeric_mul (left->value, right->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); break; case ASN_OP: result->value = right->value; break; } return result; }
/* Get the rate from the price db */ static gnc_numeric gtu_sr_get_rate_from_db (gnc_commodity *from, gnc_commodity *to) { GNCPrice *prc; gnc_numeric rate_split; gboolean have_rate = FALSE; QofBook *book = gnc_get_current_book (); /* Do we have a rate allready */ prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), from, to); if (prc) { rate_split = gnc_price_get_value (prc); gnc_price_unref (prc); have_rate = TRUE; } /* Lets try reversing the commodities */ if (!have_rate) { prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), to, from); if (prc) { rate_split = gnc_numeric_div (gnc_numeric_create (100, 100), gnc_price_get_value (prc), GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); gnc_price_unref (prc); have_rate = TRUE; } } /* No rate, set to 1/1 */ if (!have_rate) rate_split = gnc_numeric_create (100, 100); return rate_split; }
void gnc_tree_util_split_reg_save_amount_values (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input) { GncTreeModelSplitReg *model; Account *acc; gnc_numeric new_amount, convrate, amtconv, value; gnc_commodity *curr, *reg_com, *xfer_com; Account *xfer_acc; ENTER("View is %p, trans is %p, split is %p, input is %s", view, trans, split, gnc_numeric_to_string (input)); model = gnc_tree_view_split_reg_get_model_from_view (view); new_amount = input; acc = gnc_tree_model_split_reg_get_anchor (model); xfer_acc = xaccSplitGetAccount (split); xfer_com = xaccAccountGetCommodity (xfer_acc); reg_com = xaccAccountGetCommodity (acc); curr = xaccTransGetCurrency (trans); if (!xaccTransGetRateForCommodity (trans, reg_com, NULL, &convrate)) convrate = gnc_numeric_create (100, 100); amtconv = convrate; if (gnc_tree_util_split_reg_needs_conv_rate (view, 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 (gnc_commodity_equal (reg_com, xfer_com)) amtconv = xaccTransGetAccountConvRate (trans, acc); } if (xaccTransUseTradingAccounts (trans)) { /* Using currency accounts, the amount is probably really the amount and not the value. */ gboolean is_amount; if (model->type == STOCK_REGISTER2 || model->type == CURRENCY_REGISTER2 || model->type == PORTFOLIO_LEDGER2) { 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 (split, new_amount); if (gnc_tree_util_split_reg_needs_amount (view, split)) { value = gnc_numeric_div (new_amount, amtconv, gnc_commodity_get_fraction (curr), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } else xaccSplitSetValue (split, new_amount); } else { xaccSplitSetValue (split, new_amount); } LEAVE(" "); 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 * compute the amount. Otherwise we are setting the "converted * value". This means we need to convert new_amount to the actual * 'value' by dividing by the convrate in order to set the value. */ /* Now compute/set the split value. Amount is in the register * currency but we need to convert to the txn currency. */ if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc)) { /* convert the amount to the Value ... */ value = gnc_numeric_div (new_amount, amtconv, gnc_commodity_get_fraction (curr), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } else { xaccSplitSetValue (split, new_amount); } /* Now re-compute the Amount from the Value. We may need to convert * from the Value back to the amount here using the convrate from * earlier. */ value = xaccSplitGetValue (split); if (gnc_tree_util_split_reg_needs_amount (view, split)) { acc = xaccSplitGetAccount (split); new_amount = gnc_numeric_mul (value, convrate, xaccAccountGetCommoditySCU (acc), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (split, new_amount); } else { xaccSplitSetAmount (split, value); } LEAVE(" "); }
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)) { LEAVE ("transaction is balanced"); 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_HOW_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_HOW_RND_ROUND_HALF_UP); if (! gnc_numeric_equal (old_value, new_value)) { xaccTransBeginEdit (trans); xaccSplitSetValue (balance_split, new_value); 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("transaction is balanced"); 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 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_HALF_UP); 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_HALF_UP); 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_HALF_UP); 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 ("()"); }
static void gnc_split_register_save_cells (gpointer save_data, gpointer user_data) { SRSaveData *sd = save_data; SplitRegister *reg = user_data; Split *other_split; gnc_commodity *txn_cur; gnc_numeric rate = gnc_numeric_zero(); g_return_if_fail (sd != NULL); if (!sd->do_scrub) return; other_split = xaccSplitGetOtherSplit (sd->split); txn_cur = xaccTransGetCurrency (sd->trans); xaccSplitScrub (sd->split); rate = gnc_split_register_get_rate_cell (reg, RATE_CELL); if (other_split && !sd->reg_expanded) { gnc_numeric amount, value = xaccSplitGetValue (sd->split); Account *acc; gboolean split_needs_amount; split_needs_amount = gnc_split_register_split_needs_amount(reg, sd->split); /* We are changing the rate on the current split, but it was not * handled in the debcred handler, so we need to do it here. */ if (!sd->handled_dc && split_needs_amount && !gnc_numeric_zero_p (rate)) { gnc_numeric amount = xaccSplitGetAmount (sd->split); value = gnc_numeric_div( amount, rate, gnc_commodity_get_fraction(txn_cur), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (sd->split, value); /* XXX: do we need to set the amount on the other split? */ } /* Now reverse the value for the other split */ value = gnc_numeric_neg (value); if (gnc_split_register_split_needs_amount (reg, other_split)) { acc = xaccSplitGetAccount (other_split); /* If we don't have an exchange rate then figure it out. Or, if * BOTH splits require an amount, then most likely we're in the * strange case of having a transaction currency different than * _both_ accounts -- so grab the other exchange rate. */ if (gnc_numeric_zero_p (rate) || split_needs_amount) rate = xaccTransGetAccountConvRate(xaccSplitGetParent (other_split), acc); amount = gnc_numeric_mul (value, rate, xaccAccountGetCommoditySCU (acc), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (other_split, amount); } xaccSplitSetValue (other_split, value); xaccSplitScrub (other_split); } else if (gnc_split_register_split_needs_amount (reg, sd->split) && ! gnc_numeric_zero_p (rate)) { /* this is either a multi-split or expanded transaction, so only * deal with this split... In particular we need to reset the * Value if the conv-rate changed. * * If we handled the debcred then no need to do anything there -- * the debcred handler did all the computation. If NOT, then the * convrate changed -- reset the value from the amount. */ if (!sd->handled_dc) { gnc_split_register_save_amount_values (sd, reg); #if 0 gnc_numeric value, amount; amount = xaccSplitGetAmount (sd->split); value = gnc_numeric_div (amount, rate, gnc_commodity_get_fraction (txn_cur), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (sd->split, value); #endif } } }
static void 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 (acc && 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_HOW_RND_ROUND_HALF_UP); 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 * compute the amount. Otherwise we are setting the "converted * value". This means we need to convert new_amount to the actual * 'value' by dividing by the convrate in order to set the value. */ /* Now compute/set the split value. Amount is in the register * currency but we need to convert to the txn currency. */ if (gnc_split_register_needs_conv_rate (reg, sd->trans, acc)) { /* convert the amount to the Value ... */ value = gnc_numeric_div (new_amount, amtconv, gnc_commodity_get_fraction (curr), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (sd->split, value); } else xaccSplitSetValue (sd->split, new_amount); /* Now re-compute the Amount from the Value. We may need to convert * from the Value back to the amount here using the convrate from * earlier. */ value = xaccSplitGetValue (sd->split); if (gnc_split_register_split_needs_amount (reg, sd->split)) { acc = xaccSplitGetAccount (sd->split); new_amount = gnc_numeric_mul (value, convrate, xaccAccountGetCommoditySCU (acc), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (sd->split, new_amount); } }
/* * This is the logic of computing the total for an Entry, so you know * what values to put into various Splits or to display in the ledger. * In other words, we combine the quantity, unit-price, discount and * taxes together, depending on various flags. * * There are four potental ways to combine these numbers: * Discount: Pre-Tax Post-Tax * Tax : Included Not-Included * * The process is relatively simple: * * 1) compute the agregate price (price*qty) * 2) if taxincluded, then back-compute the agregate pre-tax price * 3) apply discount and taxes in the appropriate order * 4) return the requested results. * * step 2 can be done with agregate taxes; no need to compute them all * unless the caller asked for the tax_value. * * Note that the returned "value" is such that value + tax == "total * to pay," which means in the case of tax-included that the returned * "value" may be less than the agregate price, even without a * discount. If you want to display the tax-included value, you need * to add the value and taxes together. In other words, the value is * the amount the merchant gets; the taxes are the amount the gov't * gets, and the customer pays the sum or value + taxes. * * The SCU is the denominator to convert the value. * * The discount return value is just for entertainment -- you may want * to let a consumer know how much they saved. */ void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, const GncTaxTable *tax_table, gboolean tax_included, gnc_numeric discount, GncAmountType discount_type, GncDiscountHow discount_how, int SCU, gnc_numeric *value, gnc_numeric *discount_value, GList **tax_value) { gnc_numeric aggregate; gnc_numeric pretax; gnc_numeric result; gnc_numeric tax; gnc_numeric percent = gnc_numeric_create (100, 1); gnc_numeric tpercent = gnc_numeric_zero (); gnc_numeric tvalue = gnc_numeric_zero (); GList * entries = gncTaxTableGetEntries (tax_table); GList * node; /* Step 1: compute the aggregate price */ aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Step 2: compute the pre-tax aggregate */ /* First, compute the aggregate tpercent and tvalue numbers */ for (node = entries; node; node = node->next) { GncTaxTableEntry *entry = node->data; gnc_numeric amount = gncTaxTableEntryGetAmount (entry); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_VALUE: tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; case GNC_AMT_TYPE_PERCENT: tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; default: g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry)); } } /* now we need to convert from 5% -> .05 */ tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Next, actually compute the pre-tax aggregate value based on the * taxincluded flag. */ if (tax_table && tax_included) { /* Back-compute the pre-tax aggregate value. * We know that aggregate = pretax + pretax*tpercent + tvalue, so * pretax = (aggregate-tvalue)/(1+tpercent) */ pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); pretax = gnc_numeric_div (pretax, gnc_numeric_add (tpercent, gnc_numeric_create (1, 1), GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } else { pretax = aggregate; } /* Step 3: apply discount and taxes in the appropriate order */ /* * There are two ways to apply discounts and taxes. In one way, you * always compute the discount off the pretax number, and compute * the taxes off of either the pretax value or "pretax-discount" * value. In the other way, you always compute the tax on "pretax", * and compute the discount on either "pretax" or "pretax+taxes". * * I don't know which is the "correct" way. */ /* * Type: discount tax * PRETAX pretax pretax-discount * SAMETIME pretax pretax * POSTTAX pretax+tax pretax */ switch (discount_how) { case GNC_DISC_PRETAX: case GNC_DISC_SAMETIME: /* compute the discount from pretax */ if (discount_type == GNC_AMT_TYPE_PERCENT) { discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Figure out when to apply the tax, pretax or pretax-discount */ if (discount_how == GNC_DISC_PRETAX) pretax = result; break; case GNC_DISC_POSTTAX: /* compute discount on pretax+taxes */ if (discount_type == GNC_AMT_TYPE_PERCENT) { gnc_numeric after_tax; tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; default: g_warning ("unknown DiscountHow value: %d", discount_how); } /* Step 4: return the requested results. */ /* result == amount merchant gets * discount == amount of discount * need to compute taxes (based on 'pretax') if the caller wants it. */ if (discount_value != NULL) { if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND); *discount_value = discount; } if (value != NULL) { if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND); *value = result; } /* Now... Compute the list of tax values (if the caller wants it) */ if (tax_value != NULL) { GList * taxes = NULL; for (node = entries; node; node = node->next) { GncTaxTableEntry *entry = node->data; Account *acc = gncTaxTableEntryGetAccount (entry); gnc_numeric amount = gncTaxTableEntryGetAmount (entry); g_return_if_fail (acc); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_VALUE: if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND); taxes = gncAccountValueAdd (taxes, acc, amount); break; case GNC_AMT_TYPE_PERCENT: amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND); taxes = gncAccountValueAdd (taxes, acc, tax); break; default: break; } } *tax_value = taxes; } return; }
static void check_mult_div (void) { int i, j; gint64 v; gnc_numeric c, d; gnc_numeric amt_a, amt_tot, frac, val_tot, val_a; gnc_numeric a, b; a = gnc_numeric_create(-100, 100); b = gnc_numeric_create(1, 1); check_binary_op (gnc_numeric_create(-100, 100), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s div exact"); a = gnc_numeric_create(-100, 100); b = gnc_numeric_create(-1, 1); check_binary_op (gnc_numeric_create(100, 100), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s div exact"); a = gnc_numeric_create(-100, 100); b = gnc_numeric_create(-1, 1); check_binary_op (gnc_numeric_create(100, 100), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s * %s mult exact"); a = gnc_numeric_create(2, 6); b = gnc_numeric_create(1, 4); check_binary_op (gnc_numeric_create(2, 24), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s * %s for mult exact"); check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s * %s for mult reduce"); check_binary_op (gnc_numeric_create(8, 100), gnc_numeric_mul(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s * %s for mult 100th's"); check_binary_op (gnc_numeric_create(8, 6), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s for div exact"); check_binary_op (gnc_numeric_create(4, 3), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s / %s for div reduce"); check_binary_op (gnc_numeric_create(133, 100), gnc_numeric_div(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s * %s for div 100th's"); /* Check for math with 2^63 < num*num < 2^64 which previously failed * see https://bugs.gnucash.org/show_bug.cgi?id=144980 */ v = 1000000; a = gnc_numeric_create(1 * v, v); b = gnc_numeric_create(10000000 * v, v); check_binary_op (b, gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), a, b, "expected %s got %s = %s * %s for multiply"); /* Multiply some random numbers. This test presumes that * RAND_MAX is approx 2^32 */ for (i = 0; i < NREPS; i++) { gint64 deno = 1; gint64 na = rand(); gint64 nb = rand(); gint64 ne; /* avoid 0 */ if (nb / 4 == 0) { i--; continue; } /* avoid overflow; */ na /= 2; nb /= 2; ne = na * nb; a = gnc_numeric_create(na, deno); b = gnc_numeric_create(nb, deno); check_binary_op_equal (gnc_numeric_create(ne, 1), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s * %s for mult exact"); /* Force 128-bit math to come into play */ for (j = 1; j < 31; j++) { a = gnc_numeric_create(na << j, 1 << j); b = gnc_numeric_create(nb << j, 1 << j); check_binary_op (gnc_numeric_create(ne, 1), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s * %s for mult reduce"); } /* Do some hokey random 128-bit division too */ b = gnc_numeric_create(deno, nb); check_binary_op_equal (gnc_numeric_create(ne, 1), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s for div exact"); /* avoid overflow; */ na /= 2; nb /= 2; ne = na * nb; for (j = 1; j < 16; j++) { a = gnc_numeric_create(na << j, 1 << j); b = gnc_numeric_create(1 << j, nb << j); check_binary_op (gnc_numeric_create(ne, 1), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s / %s for div reduce"); } } a = gnc_numeric_create(INT64_C(1173888083434299), 93773); b = gnc_numeric_create(INT64_C(2222554708930978), 89579); /* Dividing the above pair overflows, in that after * the division the denominator won't fit into a * 64-bit quantity. This can be seen from * the factorization into primes: * 1173888083434299 = 3 * 2283317 * 171371749 * (yes, thats a seven and a nine digit prime) * 2222554708930978 = 2 * 1111277354465489 * (yes, that's a sixteen-digit prime number) * 93773 = 79*1187 * 89579 = 67*7*191 * If the rounding method is exact/no-round, then * an overflow error should be signalled; else the * divide routine should shift down the results till * the overflow is eliminated. */ check_binary_op (gnc_numeric_error (GNC_ERROR_OVERFLOW), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER | GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s for div exact"); check_binary_op (gnc_numeric_create(504548, 1000000), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s / %s for div round"); /* The below is a 'typical' value calculation: * value_frac = value_tot * amt_frace / amt_tot * and has some typical potential-overflow values. * 82718 = 2 * 59 * 701 * 47497125586 = 2 * 1489 * 15949337 * 69100955 = 5 * 7 * 11 * 179483 * 32005637020 = 4 * 5 * 7 * 43 * 71 * 103 * 727 */ a = gnc_numeric_create (-47497125586LL, 82718); b = gnc_numeric_create (-69100955LL, 55739); c = gnc_numeric_mul (a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); d = gnc_numeric_create (-32005637020LL, 55739); check_binary_op (gnc_numeric_create(-102547458LL, 82718), gnc_numeric_div(c, d, 82718, GNC_HOW_DENOM_EXACT), c, d, "expected %s got %s = %s / %s for div round"); /* If we specify GNC_HOW_RND_NEVER, then we shoukld get an error, * since the exact result won't fit into a 64-bit quantity. */ check_binary_op (gnc_numeric_error (GNC_ERROR_REMAINDER), gnc_numeric_div(c, d, 82718, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER), c, d, "expected %s got %s = %s / %s for div round"); /* A simple irreducible ratio, involving negative numbers */ amt_a = gnc_numeric_create (-6005287905LL, 40595); amt_tot = gnc_numeric_create (-8744187958LL, 40595); frac = gnc_numeric_div (amt_a, amt_tot, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); check_binary_op (gnc_numeric_create(6005287905LL, 8744187958LL), frac, amt_a, amt_tot, "expected %s got %s = %s / %s for div reduce"); /* Another overflow-prone condition */ val_tot = gnc_numeric_create (-4280656418LL, 19873); val_a = gnc_numeric_mul (frac, val_tot, gnc_numeric_denom(val_tot), GNC_HOW_RND_ROUND | GNC_HOW_DENOM_REDUCE); check_binary_op (gnc_numeric_create(-2939846940LL, 19873), val_a, val_tot, frac, "expected %s got %s = %s * %s for mult round"); frac = gnc_numeric_create (396226789777979LL, 328758834367851752LL); val_tot = gnc_numeric_create (467013515494988LL, 100); val_a = gnc_numeric_mul (frac, val_tot, gnc_numeric_denom(val_tot), GNC_HOW_RND_ROUND | GNC_HOW_DENOM_REDUCE); check_binary_op (gnc_numeric_create(562854124919LL, 100), val_a, val_tot, frac, "expected %s got %s = %s * %s for mult round"); /* Yet another bug from bugzilla ... */ a = gnc_numeric_create (40066447153986554LL, 4518); b = gnc_numeric_create (26703286457229LL, 3192); frac = gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND); check_binary_op (gnc_numeric_create(106007, 100), frac, a, b, "expected %s got %s = %s / %s for mult sigfigs"); }
void xaccScrubSubSplitPrice (Split *split, int maxmult, int maxamtscu) { gnc_numeric src_amt, src_val; SplitList *node; if (FALSE == is_subsplit (split)) return; ENTER (" "); /* Get 'price' of the indicated split */ src_amt = xaccSplitGetAmount (split); src_val = xaccSplitGetValue (split); /* Loop over splits, adjust each so that it has the same * ratio (i.e. price). Change the value to get things * right; do not change the amount */ for (node = split->parent->splits; node; node = node->next) { Split *s = node->data; Transaction *txn = s->parent; gnc_numeric dst_amt, dst_val, target_val; gnc_numeric frac, delta; int scu; /* Skip the reference split */ if (s == split) continue; scu = gnc_commodity_get_fraction (txn->common_currency); dst_amt = xaccSplitGetAmount (s); dst_val = xaccSplitGetValue (s); frac = gnc_numeric_div (dst_amt, src_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); target_val = gnc_numeric_mul (frac, src_val, scu, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP); if (gnc_numeric_check (target_val)) { PERR ("Numeric overflow of value\n" "\tAcct=%s txn=%s\n" "\tdst_amt=%s src_val=%s src_amt=%s\n", xaccAccountGetName (s->acc), xaccTransGetDescription(txn), gnc_num_dbg_to_string(dst_amt), gnc_num_dbg_to_string(src_val), gnc_num_dbg_to_string(src_amt)); continue; } /* If the required price changes are 'small', do nothing. * That is a case that the user will have to deal with * manually. This routine is really intended only for * a gross level of synchronization. */ delta = gnc_numeric_sub_fixed (target_val, dst_val); delta = gnc_numeric_abs (delta); if (maxmult * delta.num < delta.denom) continue; /* If the amount is small, pass on that too */ if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue; /* Make the actual adjustment */ xaccTransBeginEdit (txn); xaccSplitSetValue (s, target_val); xaccTransCommitEdit (txn); } LEAVE (" "); }
/* 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(""); }
static void check_reciprocal(void) { gnc_numeric a, b, ans, val; double flo; val = gnc_numeric_create(-60, 20); check_unary_op (gnc_numeric_eq, gnc_numeric_create (-3, -1), gnc_numeric_convert(val, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER), val, "expected %s got %s = (%s as RECIP(1))"); a = gnc_numeric_create(200, 100); b = gnc_numeric_create(300, 100); /* 2 + 3 = 5 */ ans = gnc_numeric_add(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(5, -1), ans, a, b, "expected %s got %s = %s + %s for reciprocal"); /* 2 + 3 = 5 */ a = gnc_numeric_create(2, -1); b = gnc_numeric_create(300, 100); ans = gnc_numeric_add(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(5, -1), ans, a, b, "expected %s got %s = %s + %s for reciprocal"); /* check gnc_numeric_to_double */ flo = gnc_numeric_to_double(gnc_numeric_create(5, -1)); do_test ((5.0 == flo), "reciprocal conversion"); /* check gnc_numeric_compare */ a = gnc_numeric_create(2, 1); b = gnc_numeric_create(2, -1); do_test((0 == gnc_numeric_compare(a, b)), " 2 == 2 "); a = gnc_numeric_create(2, 1); b = gnc_numeric_create(3, -1); do_test((-1 == gnc_numeric_compare(a, b)), " 2 < 3 "); a = gnc_numeric_create(-2, 1); b = gnc_numeric_create(2, -1); do_test((-1 == gnc_numeric_compare(a, b)), " -2 < 2 "); a = gnc_numeric_create(2, -1); b = gnc_numeric_create(3, -1); do_test((-1 == gnc_numeric_compare(a, b)), " 2 < 3 "); /* check for equality */ a = gnc_numeric_create(2, 1); b = gnc_numeric_create(2, -1); do_test(gnc_numeric_equal(a, b), " 2 == 2 "); /* check gnc_numeric_mul */ a = gnc_numeric_create(2, 1); b = gnc_numeric_create(3, -1); ans = gnc_numeric_mul(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(6, -1), ans, a, b, "expected %s got %s = %s * %s for reciprocal"); /* check gnc_numeric_div */ /* -60 / 20 = -3 */ a = gnc_numeric_create(-60, 1); b = gnc_numeric_create(2, -10); ans = gnc_numeric_div(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(-3, -1), ans, a, b, "expected %s got %s = %s / %s for reciprocal"); /* 60 / 20 = 3 */ a = gnc_numeric_create(60, 1); b = gnc_numeric_create(2, -10); ans = gnc_numeric_div(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(3, -1), ans, a, b, "expected %s got %s = %s / %s for reciprocal"); }
/* Either sets the value and amount for split and returns TRUE, or does nothing and returns FALSE. */ static gboolean gtu_sr_handle_exchange_rate (GncTreeViewSplitReg *view, gnc_numeric amount, Transaction *trans, Split *split, gboolean force) { GncTreeModelSplitReg *model; XferDialog *xfer; gboolean rate_split_ok, rate_reg_ok; gnc_numeric rate_split, rate_reg, value; Account *reg_acc; gnc_commodity *xfer_comm = xaccAccountGetCommodity (xaccSplitGetAccount (split)); gnc_commodity *reg_comm = gnc_tree_view_split_reg_get_reg_commodity (view); gnc_commodity *trans_curr = xaccTransGetCurrency (trans); gboolean expanded; gboolean have_rate = TRUE; ENTER("handle_exchange_rate amount %s, trans %p and split %p force %d", gnc_numeric_to_string (amount), trans, split, force); model = gnc_tree_view_split_reg_get_model_from_view (view); reg_acc = gnc_tree_model_split_reg_get_anchor (model); /* Rate from trans-curr to split-comm */ rate_split_ok = xaccTransGetRateForCommodity (trans, xfer_comm, split, &rate_split); DEBUG("rate_split_ok %d and xfer_comm %s", rate_split_ok, gnc_commodity_get_fullname (xfer_comm)); /* Rate from trans-curr to reg-comm */ rate_reg_ok = xaccTransGetRateForCommodity (trans, reg_comm, split, &rate_reg); DEBUG("rate_reg_ok %d and reg_comm %s", rate_reg_ok, gnc_commodity_get_fullname (reg_comm)); /* Are we expanded */ expanded = gnc_tree_view_split_reg_trans_expanded (view, trans); if (gnc_commodity_equal (trans_curr, xfer_comm) && rate_split_ok) { xaccSplitSetAmount (split, amount); xaccSplitSetValue (split, amount); return TRUE; } if (rate_reg_ok && rate_split_ok && !force) { value = gnc_numeric_div (amount, rate_reg, gnc_commodity_get_fraction (trans_curr), GNC_HOW_DENOM_REDUCE); amount = gnc_numeric_mul (value, rate_split, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); } else { if (!rate_split_ok) rate_split = gtu_sr_get_rate_from_db (reg_comm, xfer_comm); /* create the exchange-rate dialog */ xfer = gnc_xfer_dialog (NULL, NULL); gnc_xfer_dialog_is_exchange_dialog (xfer, &rate_split); /* fill in the dialog entries */ gnc_xfer_dialog_set_description (xfer, xaccTransGetDescription (trans)); gnc_xfer_dialog_set_memo (xfer, xaccSplitGetMemo (split)); /* Get per book option */ gnc_xfer_dialog_set_num (xfer, gnc_get_num_action (trans, split)); gnc_xfer_dialog_set_date (xfer, timespecToTime64 (xaccTransRetDatePostedTS (trans))); value = amount; if (gnc_xfer_dialog_run_exchange_dialog (xfer, &rate_split, value, reg_acc, trans, xfer_comm, expanded)) { if (!rate_split_ok) rate_split = gnc_numeric_create (1, 1); have_rate = FALSE; } else have_rate = TRUE; amount = gnc_numeric_mul (value, rate_split, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); } xaccSplitSetAmount (split, amount); xaccSplitSetValue (split, value); LEAVE("handle_exchange_rate set split %p amt=%s; and val=%s", split, gnc_numeric_to_string (amount), gnc_numeric_to_string (value)); return have_rate; }