gnc_numeric gnc_numeric_mul_with_error(gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric * error) { gnc_numeric prod = gnc_numeric_mul(a, b, denom, how); gnc_numeric exact = gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); gnc_numeric err = gnc_numeric_sub(prod, exact, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); if (error) { *error = err; } return prod; }
int main(int argc, char ** argv) { gnc_numeric a = gnc_numeric_create(1, 3); gnc_numeric b = gnc_numeric_create(1, 4); gnc_numeric c; gnc_numeric err; c = gnc_numeric_add_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("add 100ths/error : %s + %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); c = gnc_numeric_sub_with_error(a, b, 100, GNC_HOW_RND_FLOOR, &err); printf("sub 100ths/error : %s - %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); c = gnc_numeric_mul_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("mul 100ths/error : %s * %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); c = gnc_numeric_div_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("div 100ths/error : %s / %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); printf("multiply (EXACT): %s * %s = %s\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT))); printf("multiply (REDUCE): %s * %s = %s\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE))); return 0; }
static void run_tests (void) { int i; for (i = 0; i < 50; i++) { gnc_numeric n; gnc_numeric n1; n = get_random_gnc_numeric (GNC_DENOM_AUTO); IS_VALID_NUM(n, n); test_num (n); n1 = gnc_numeric_mul (n, n, n.denom, GNC_HOW_RND_ROUND_HALF_UP); IS_VALID_NUM(n1, n); test_num (n); n1 = gnc_numeric_mul (n, n, n.denom, GNC_HOW_RND_ROUND_HALF_UP); IS_VALID_NUM(n1, n); test_num (n); } }
/* Copy the values in the financial_info structure to the GUI */ static void fi_to_gui(FinCalcDialog *fcd) { const gnc_commodity *commodity; static char string[64]; gnc_numeric total; gnc_numeric npp; gnc_numeric pmt; int i; if (fcd == NULL) return; npp = gnc_numeric_create (fcd->financial_info.npp, 1); gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]), npp); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[INTEREST_RATE]), fcd->financial_info.ir); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[PRESENT_VALUE]), fcd->financial_info.pv); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[PERIODIC_PAYMENT]), fcd->financial_info.pmt); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[FUTURE_VALUE]), -fcd->financial_info.fv); pmt = double_to_gnc_numeric (fcd->financial_info.pmt, 100000, GNC_HOW_RND_ROUND_HALF_UP); commodity = gnc_default_currency (); total = gnc_numeric_mul (npp, pmt, gnc_commodity_get_fraction (commodity), GNC_HOW_RND_ROUND_HALF_UP); xaccSPrintAmount (string, total, gnc_default_print_info (FALSE)); gtk_label_set_text (GTK_LABEL(fcd->payment_total_label), string); i = normalize_period(&fcd->financial_info.CF); gtk_combo_box_set_active(GTK_COMBO_BOX(fcd->compounding_combo), i); i = normalize_period(&fcd->financial_info.PF); gtk_combo_box_set_active(GTK_COMBO_BOX(fcd->payment_combo), i); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fcd->end_of_period_radio), !fcd->financial_info.bep); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fcd->discrete_compounding_radio), fcd->financial_info.disc); }
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; }
/* * 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"); }
gboolean gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places) { guint8 decimal_places = 0; gnc_numeric converted_val; gint64 fraction; g_return_val_if_fail(a, FALSE); if (gnc_numeric_check(*a) != GNC_ERROR_OK) return FALSE; converted_val = *a; if (converted_val.denom <= 0) { converted_val = gnc_numeric_convert(converted_val, 1, GNC_HOW_DENOM_EXACT); if (gnc_numeric_check(converted_val) != GNC_ERROR_OK) return FALSE; *a = converted_val; if (max_decimal_places) *max_decimal_places = decimal_places; return TRUE; } /* Zero is easily converted. */ if (converted_val.num == 0) converted_val.denom = 1; fraction = converted_val.denom; while (fraction != 1) { switch (fraction % 10) { case 0: fraction = fraction / 10; break; case 5: converted_val = gnc_numeric_mul(converted_val, gnc_numeric_create(2, 2), GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER); if (gnc_numeric_check(converted_val) != GNC_ERROR_OK) return FALSE; fraction = fraction / 5; break; case 2: case 4: case 6: case 8: converted_val = gnc_numeric_mul(converted_val, gnc_numeric_create(5, 5), GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER); if (gnc_numeric_check(converted_val) != GNC_ERROR_OK) return FALSE; fraction = fraction / 2; break; default: return FALSE; } decimal_places += 1; } if (max_decimal_places) *max_decimal_places = decimal_places; *a = converted_val; return TRUE; }
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(" "); }
/* 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; }
/* 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(""); }
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 (" "); }
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"); }
GNCLot * gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, Account *posted_acc, Account *xfer_acc, gnc_numeric amount, gnc_numeric exch, Timespec date, const char *memo, const char *num) { QofBook *book; Split *split; const char *name; gnc_commodity *commodity; Split *xfer_split = NULL; GNCLot *payment_lot; /* Verify our arguments */ if (!owner || !posted_acc || !xfer_acc) return NULL; g_return_val_if_fail (owner->owner.undefined != NULL, NULL); /* Compute the ancillary data */ book = gnc_account_get_book (posted_acc); name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner)); commodity = gncOwnerGetCurrency (owner); // reverse = use_reversed_payment_amounts(owner); if (txn) { /* Pre-existing transaction was specified. We completely clear it, * except for the split in the transfer account, unless the * transaction can't be reused (wrong currency, wrong transfer account). * In that case, the transaction is simply removed and an new * one created. */ xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc); if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner)) { g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction."); xfer_split = NULL; } if (!xfer_split) { g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one."); xaccTransBeginEdit (txn); xaccTransDestroy (txn); xaccTransCommitEdit (txn); txn = NULL; } else { int i = 0; xaccTransBeginEdit (txn); while (i < xaccTransCountSplits(txn)) { Split *split = xaccTransGetSplit (txn, i); if (split == xfer_split) { gnc_set_num_action (NULL, split, num, _("Payment")); ++i; } else { xaccSplitDestroy(split); } } /* Note: don't commit transaction now - that would insert an imbalance split.*/ } } /* Create the transaction if we don't have one yet */ if (!txn) { txn = xaccMallocTransaction (book); xaccTransBeginEdit (txn); } /* Insert a split for the transfer account if we don't have one yet */ if (!xfer_split) { /* Set up the transaction */ xaccTransSetDescription (txn, name ? name : ""); /* set per book option */ xaccTransSetCurrency (txn, commodity); xaccTransSetDateEnteredSecs (txn, gnc_time (NULL)); xaccTransSetDatePostedTS (txn, &date); /* The split for the transfer account */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set per book option */ gnc_set_num_action (NULL, split, num, _("Payment")); xaccAccountBeginEdit (xfer_acc); xaccAccountInsertSplit (xfer_acc, split); xaccAccountCommitEdit (xfer_acc); xaccTransAppendSplit (txn, split); if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity)) { xaccSplitSetBaseValue (split, amount, commodity); } else { /* Need to value the payment in terms of the owner commodity */ gnc_numeric payment_value = gnc_numeric_mul(amount, exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount(split, amount); xaccSplitSetValue(split, payment_value); } } /* Add a split in the post account */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set per book option */ gnc_set_num_action (NULL, split, num, _("Payment")); xaccAccountBeginEdit (posted_acc); xaccAccountInsertSplit (posted_acc, split); xaccAccountCommitEdit (posted_acc); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, gnc_numeric_neg (amount), commodity); /* Create a new lot for the payment */ payment_lot = gnc_lot_new (book); gncOwnerAttachToLot (owner, payment_lot); gnc_lot_add_split (payment_lot, split); /* Mark the transaction as a payment */ gnc_set_num_action (txn, NULL, num, _("Payment")); xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT); /* Commit this new transaction */ xaccTransCommitEdit (txn); return payment_lot; }
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); } }
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 } } }
gboolean gnc_tree_util_split_reg_get_debcred_entry (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gboolean is_blank, gnc_numeric *ret_num, GNCPrintAmountInfo *ret_print_info) { GncTreeModelSplitReg *model; gnc_commodity *currency; model = gnc_tree_view_split_reg_get_model_from_view (view); currency = xaccTransGetCurrency (trans); if (!currency) currency = gnc_default_currency (); if (is_blank) { gnc_numeric imbalance; Account *acc; imbalance = xaccTransGetImbalanceValue (trans); if (gnc_numeric_zero_p (imbalance)) return FALSE; 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 FALSE; } if (imbal_list->next) { /* Multiple currency imbalance. */ gnc_monetary_list_free (imbal_list); return FALSE; } 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 FALSE; } if (!gnc_numeric_equal (gnc_monetary_value (*imbal_mon), imbalance)) { /* Value and commodity imbalances differ */ gnc_monetary_list_free (imbal_list); return FALSE; } /* Done with the imbalance list */ gnc_monetary_list_free (imbal_list); } imbalance = gnc_numeric_neg (imbalance); acc = gnc_tree_model_split_reg_get_anchor (model); if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc)) { imbalance = gnc_numeric_mul (imbalance, xaccTransGetAccountConvRate (trans, acc), gnc_commodity_get_fraction (currency), GNC_HOW_RND_ROUND_HALF_UP); } else { imbalance = gnc_numeric_convert (imbalance, gnc_commodity_get_fraction (currency), GNC_HOW_RND_ROUND_HALF_UP); } *ret_num = imbalance; *ret_print_info = gnc_account_print_info (acc, FALSE); return TRUE; } { gnc_numeric amount; gnc_commodity *split_commodity; GNCPrintAmountInfo print_info; Account *account; gnc_commodity *commodity; account = gnc_tree_model_split_reg_get_anchor (model); commodity = xaccAccountGetCommodity (account); split_commodity = xaccAccountGetCommodity (xaccSplitGetAccount (split)); if (xaccTransUseTradingAccounts (trans)) { gboolean use_symbol, is_current = FALSE; Split *current_split = gnc_tree_view_split_reg_get_current_split (view); RowDepth depth = gnc_tree_view_reg_get_selected_row_depth (view); if ((split == current_split) && (depth == SPLIT3)) is_current = TRUE; if (model->type == STOCK_REGISTER2 || model->type == CURRENCY_REGISTER2 || model->type == PORTFOLIO_LEDGER2) { gnc_commodity *amount_commodity; /* security register. If this split has price and shares columns, use the value, otherwise use the amount. */ if (gtu_sr_use_security (view)) { 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 (model->type) { case STOCK_REGISTER2: case CURRENCY_REGISTER2: case PORTFOLIO_LEDGER2: 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); break; } } if (gnc_numeric_zero_p (amount)) return FALSE; *ret_num = amount; *ret_print_info = print_info; return TRUE; } }