Merge branch 'single-price' into maint

This commit is contained in:
John Ralls 2015-09-15 12:06:46 -07:00
commit bc9285bbfb
21 changed files with 899 additions and 602 deletions

View File

@ -1170,9 +1170,9 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data)
}
else
{
exchange = gnc_numeric_div(gnc_numeric_create(1,1),
gnc_price_get_value(price),
1000, GNC_HOW_RND_ROUND_HALF_UP);
exchange = gnc_numeric_invert(gnc_price_get_value(price));
exchange = gnc_numeric_convert(exchange, 1000,
GNC_HOW_RND_ROUND_HALF_UP);
}
}
else
@ -1779,4 +1779,3 @@ GHashTable* gnc_sx_all_instantiate_cashflow_all(GDate range_start, GDate range_e
result_map, NULL);
return result_map;
}

View File

@ -203,7 +203,7 @@ write_price( GNCPrice* p, gpointer data )
g_return_val_if_fail( p != NULL, FALSE );
g_return_val_if_fail( data != NULL, FALSE );
if ( s->is_ok )
if ( s->is_ok && gnc_price_get_source(p) == PRICE_SOURCE_INVOICE)
{
s->is_ok = save_price( s->be, QOF_INSTANCE(p) );
}

View File

@ -120,7 +120,7 @@ price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
{
char *text = dom_tree_to_text(sub_node);
if (!text) return FALSE;
gnc_price_set_source(p, text);
gnc_price_set_source_string(p, text);
g_free(text);
}
else if (g_strcmp0("price:type", (char*)sub_node->name) == 0)
@ -440,7 +440,7 @@ gnc_price_to_dom_tree(const xmlChar *tag, GNCPrice *price)
tmpnode = timespec_to_dom_tree("price:time", &timesp);
if (!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
sourcestr = gnc_price_get_source(price);
sourcestr = gnc_price_get_source_string(price);
if (sourcestr && (strlen(sourcestr) != 0))
{
tmpnode = text_to_dom_tree("price:source", sourcestr);

View File

@ -3112,7 +3112,7 @@ price_parse_xml_sub_node(GNCPrice *p, xmlNodePtr sub_node, QofBook *book)
{
char *text = dom_tree_to_text(sub_node);
if (!text) return FALSE;
gnc_price_set_source(p, text);
gnc_price_set_source_string(p, text);
g_free(text);
}
else if (g_strcmp0("price:type", (char*)sub_node->name) == 0)

View File

@ -901,11 +901,8 @@ gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params)
gnc_price_set_commodity (convprice, account_currency);
gnc_price_set_currency (convprice, gncInvoiceGetCurrency (invoice));
gnc_price_set_time (convprice, postdate);
gnc_price_set_source (convprice, "user:invoice-post");
/* Yes, magic strings are evil but I can't find any defined constants
for this..*/
gnc_price_set_typestr (convprice, "last");
gnc_price_set_source (convprice, PRICE_SOURCE_INVOICE);
gnc_price_set_typestr (convprice, PRICE_TYPE_LAST);
gnc_price_set_value (convprice, exch_rate);
gncInvoiceAddPrice(invoice, convprice);
gnc_price_commit_edit (convprice);
@ -1702,7 +1699,7 @@ gnc_invoice_update_window (InvoiceWindow *iw, GtkWidget *widget)
}
/* Set the type label */
gtk_label_set_text (GTK_LABEL(iw->type_label), iw->is_credit_note ? _("Credit Note")
gtk_label_set_text (GTK_LABEL(iw->type_label), iw->is_credit_note ? _("Credit Note")
: gtk_label_get_text (GTK_LABEL(iw->type_label)));
if (iw->owner_choice)
@ -1820,14 +1817,14 @@ gnc_invoice_update_window (InvoiceWindow *iw, GtkWidget *widget)
gtk_widget_hide (hide);
hide = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide4"));
gtk_widget_hide (hide);
show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "posted_label"));
gtk_widget_show (show);
gtk_widget_show (iw->posted_date_hbox);
show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "acct_label"));
gtk_widget_show (show);
gtk_widget_show (acct_entry);
show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide1"));
gtk_widget_show (show);
show = GTK_WIDGET (gtk_builder_get_object (iw->builder, "hide2"));
@ -2347,17 +2344,17 @@ gnc_invoice_create_page (InvoiceWindow *iw, gpointer page)
{
case GNC_OWNER_VENDOR:
gtk_label_set_text (GTK_LABEL(iw->info_label), _("Bill Information"));
gtk_label_set_text (GTK_LABEL(iw->type_label), _("Bill"));
gtk_label_set_text (GTK_LABEL(iw->id_label), _("Bill ID"));
gtk_label_set_text (GTK_LABEL(iw->type_label), _("Bill"));
gtk_label_set_text (GTK_LABEL(iw->id_label), _("Bill ID"));
break;
case GNC_OWNER_EMPLOYEE:
gtk_label_set_text (GTK_LABEL(iw->info_label), _("Voucher Information"));
gtk_label_set_text (GTK_LABEL(iw->type_label), _("Voucher"));
gtk_label_set_text (GTK_LABEL(iw->id_label), _("Voucher ID"));
gtk_label_set_text (GTK_LABEL(iw->id_label), _("Voucher ID"));
default:
break;
}
entry_ledger = gnc_entry_ledger_new (iw->book, ledger_type);
/* Save the ledger... */
@ -2430,7 +2427,7 @@ gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
const GncOwner *start_owner;
GncBillTerm *owner_terms = NULL;
GncOwnerType owner_type;
g_assert (dialog_type == NEW_INVOICE || dialog_type == MOD_INVOICE || dialog_type == DUP_INVOICE);
if (invoice)
@ -2513,20 +2510,20 @@ gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
iw->id_label = GTK_WIDGET (gtk_builder_get_object (builder, "label14"));
iw->info_label = GTK_WIDGET (gtk_builder_get_object (builder, "label1"));
invoice_radio = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_invoice_type"));
iw->type_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_choice_hbox"));
iw->type_choice = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_type_invoice"));
/* The default GUI lables are for invoices, so change them if it isn't. */
owner_type = gncOwnerGetType (&iw->owner);
switch(owner_type)
{
case GNC_OWNER_VENDOR:
gtk_label_set_text (GTK_LABEL(iw->info_label), _("Bill Information"));
gtk_label_set_text (GTK_LABEL(iw->type_label), _("Bill"));
gtk_label_set_text (GTK_LABEL(iw->type_label), _("Bill"));
gtk_button_set_label (GTK_BUTTON(invoice_radio), _("Bill"));
gtk_label_set_text (GTK_LABEL(iw->id_label), _("Bill ID"));
break;
case GNC_OWNER_EMPLOYEE:
gtk_label_set_text (GTK_LABEL(iw->info_label), _("Voucher Information"));
@ -2536,7 +2533,7 @@ gnc_invoice_window_new_invoice (InvoiceDialogType dialog_type, QofBook *bookp,
default:
break;
}
/* configure the type related widgets based on dialog type and invoice type */
switch (dialog_type)
{
@ -3306,4 +3303,3 @@ gnc_invoice_remind_bills_due_cb (void)
gnc_invoice_remind_bills_due();
}

View File

@ -334,7 +334,7 @@ KvpValue * kvp_frame_get_slot_path_gslist (KvpFrame *frame, GSList *key_path);
SET_ENUM("TRANS-DATE-POSTED");
SET_ENUM("TRANS-DESCRIPTION");
SET_ENUM("TRANS-NUM");
SET_ENUM("KVP-OPTION-PATH");
SET_ENUM("OPTION-SECTION-ACCOUNTS");
@ -355,6 +355,15 @@ KvpValue * kvp_frame_get_slot_path_gslist (KvpFrame *frame, GSList *key_path);
SET_ENUM("GNC-HOW-RND-ROUND");
SET_ENUM("GNC-HOW-RND-NEVER");
SET_ENUM("PRICE-SOURCE-EDIT-DLG");
SET_ENUM("PRICE-SOURCE-FQ");
SET_ENUM("PRICE-SOURCE-USER-PRICE");
SET_ENUM("PRICE-SOURCE-XFER-DLG-VAL");
SET_ENUM("PRICE-SOURCE-SPLIT-REG");
SET_ENUM("PRICE-SOURCE-STOCK-SPLIT");
SET_ENUM("PRICE-SOURCE-INVOICE");
SET_ENUM("PRICE-SOURCE-INVALID");
#undef SET_ENUM
}

View File

@ -39,7 +39,7 @@ struct gnc_price_s
gnc_commodity *commodity;
gnc_commodity *currency;
Timespec tmspec;
char *source;
PriceSource source;
char *type;
gnc_numeric value;

View File

@ -58,9 +58,32 @@ gnc_price_init(GNCPrice* price)
price->refcount = 1;
price->value = gnc_numeric_zero();
price->type = NULL;
price->source = NULL;
price->source = PRICE_SOURCE_INVALID;
}
/* Array of char constants for converting price-source enums. Be sure to keep in
* sync with the enum values in gnc-pricedb.h The string user:price-editor is
* explicitly used by price_to_gui() in dialog-price-editor.c and the string
* Finance::Quote is explicitly used by fq-results->commod-tz-quote-triples in
* price-quotes.scm. Take care to keep them in sync if you make changes. Beware
* that the strings are used to store the enum values in the backends so any
* changes will affect backward data compatibility.
*/
static const char* source_names[] =
{
/* sync with price_to_gui in dialog-price-editor.c */
"user:price-editor",
/* sync with commidity-tz-quote->price in price-quotes.scm */
"Finance::Quote",
"user:price",
/* String retained for backwards compatibility. */
"user:xfer-dialog",
"user:split-register",
"user:stock-split",
"user:invoice-post",
"invalid"
};
static void
gnc_price_dispose(GObject *pricep)
{
@ -90,7 +113,7 @@ gnc_price_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec
switch (prop_id)
{
case PROP_SOURCE:
g_value_set_string(value, price->source);
g_value_set_string(value, gnc_price_get_source_string(price));
break;
case PROP_TYPE:
g_value_set_string(value, price->type);
@ -126,7 +149,7 @@ gnc_price_set_property(GObject* object, guint prop_id, const GValue* value, GPar
switch (prop_id)
{
case PROP_SOURCE:
gnc_price_set_source(price, g_value_get_string(value));
gnc_price_set_source_string(price, g_value_get_string(value));
break;
case PROP_TYPE:
gnc_price_set_typestr(price, g_value_get_string(value));
@ -188,10 +211,10 @@ gnc_price_class_init(GNCPriceClass *klass)
PROP_SOURCE,
g_param_spec_string ("source",
"Price source",
"The price source is a string describing the "
"source of a price quote. It will be something "
"like this: 'Finance::Quote', 'user:misc', "
"'user:foo', etc.",
"The price source is PriceSource enum describing how"
" the price was created. This property works on the"
" string values in source_names for SQL database"
" compatibility.",
NULL,
G_PARAM_READWRITE));
@ -252,7 +275,6 @@ gnc_price_destroy (GNCPrice *p)
qof_event_gen (&p->inst, QOF_EVENT_DESTROY, NULL);
if (p->type) CACHE_REMOVE(p->type);
if (p->source) CACHE_REMOVE(p->source);
/* qof_instance_release (&p->inst); */
g_object_unref(p);
@ -439,22 +461,29 @@ gnc_price_set_time(GNCPrice *p, Timespec t)
}
void
gnc_price_set_source(GNCPrice *p, const char *s)
gnc_price_set_source(GNCPrice *p, PriceSource s)
{
if (!p) return;
if (g_strcmp0(p->source, s) != 0)
{
char *tmp;
gnc_price_begin_edit (p);
tmp = CACHE_INSERT((gpointer) s);
if (p->source) CACHE_REMOVE(p->source);
p->source = tmp;
gnc_price_set_dirty(p);
gnc_price_commit_edit (p);
}
gnc_price_begin_edit (p);
p->source = s;
gnc_price_set_dirty(p);
gnc_price_commit_edit(p);
}
void
gnc_price_set_source_string(GNCPrice *p, const char* str)
{
if (!p) return;
for (PriceSource s = PRICE_SOURCE_EDIT_DLG;
s < PRICE_SOURCE_INVALID; ++s)
if (strcmp(source_names[s], str) == 0)
{
gnc_price_set_source(p, s);
return;
}
}
void
gnc_price_set_typestr(GNCPrice *p, const char* type)
{
@ -518,13 +547,20 @@ gnc_price_get_time(const GNCPrice *p)
return p->tmspec;
}
const char *
PriceSource
gnc_price_get_source(const GNCPrice *p)
{
if (!p) return NULL;
if (!p) return PRICE_SOURCE_INVALID;
return p->source;
}
const char*
gnc_price_get_source_string(const GNCPrice *p)
{
if (!p) return NULL;
return source_names[p->source];
}
const char *
gnc_price_get_typestr(const GNCPrice *p)
{
@ -573,8 +609,7 @@ gnc_price_equal (const GNCPrice *p1, const GNCPrice *p2)
if (!timespec_equal (&ts1, &ts2))
return FALSE;
if (g_strcmp0 (gnc_price_get_source (p1),
gnc_price_get_source (p2)) != 0)
if (gnc_price_get_source (p1) != gnc_price_get_source (p2))
return FALSE;
if (g_strcmp0 (gnc_price_get_typestr (p1),
@ -966,6 +1001,18 @@ gnc_pricedb_equal (GNCPriceDB *db1, GNCPriceDB *db2)
return equal_data.equal;
}
static gboolean
insert_or_replace_price(GNCPriceDB *db, GNCPrice *p)
{
GNCPrice *old_price = gnc_pricedb_lookup_day (db, p->commodity,
p->currency, p->tmspec);
if (old_price == NULL)
return TRUE;
if (p->source < old_price->source)
return TRUE;
return FALSE;
}
/* ==================================================================== */
/* The add_price() function is a utility that only manages the
* dual hash table instertion */
@ -1030,6 +1077,12 @@ add_price(GNCPriceDB *db, GNCPrice *p)
LEAVE (" no price list");
return FALSE;
}
if (!insert_or_replace_price(db, p))
{
LEAVE("A better price already exists");
return FALSE;
}
g_hash_table_insert(currency_hash, currency, price_list);
p->db = db;
qof_event_gen (&p->inst, QOF_EVENT_ADD, NULL);
@ -1192,7 +1245,7 @@ static gboolean
check_one_price_date (GNCPrice *price, gpointer user_data)
{
remove_info *data = user_data;
const gchar *source;
PriceSource source;
Timespec pt;
ENTER("price %p (%s), data %p", price,
@ -1201,7 +1254,7 @@ check_one_price_date (GNCPrice *price, gpointer user_data)
if (!data->delete_user)
{
source = gnc_price_get_source (price);
if (g_strcmp0(source, "Finance::Quote") != 0)
if (source != PRICE_SOURCE_FQ)
{
LEAVE("Not an automatic quote");
return TRUE;
@ -2454,8 +2507,8 @@ gnc_price_print(GNCPrice *p, FILE *f, int indent)
str = str ? str : "(null)";
fprintf(f, "%s <cmdty:ref-id>%s</cmdty:ref-id>\n", istr, str);
fprintf(f, "%s </pdb:currency>\n", istr);
str = gnc_price_get_source(p);
str = str ? str : "(null)";
str = source_names[gnc_price_get_source(p)];
str = str ? str : "invalid";
fprintf(f, "%s %s\n", istr, str);
str = gnc_price_get_typestr(p);
str = str ? str : "(null)";

View File

@ -156,6 +156,26 @@ GType gnc_pricedb_get_type(void);
typedef struct gnc_price_lookup_s GNCPriceLookup;
typedef GList PriceList;
/** Price source enum. Be sure to keep in sync with the source_name array in
* gnc-pricedb.c. These are in preference order, so for example a quote with
* PRICE_SOURCE_EDIT_DLG will overwrite one with PRICE_SOURCE_FQ but not the
* other way around.
*/
typedef enum
{
PRICE_SOURCE_EDIT_DLG, // "user:price-editor"
PRICE_SOURCE_FQ, // "Finance::Quote"
PRICE_SOURCE_USER_PRICE, // "user:price"
PRICE_SOURCE_XFER_DLG_VAL, // "user:xfer-dialog"
PRICE_SOURCE_SPLIT_REG, // "user:split-register"
PRICE_SOURCE_STOCK_SPLIT, // "user:stock-split"
PRICE_SOURCE_INVOICE, // "user:invoice-post"
PRICE_SOURCE_INVALID,
} PriceSource;
#define PRICE_TYPE_LAST "last"
#define PRICE_TYPE_UNK "unknown"
#define PRICE_TYPE_TRN "transaction"
/* ------------------ */
/** @name Constructors
@{ */
@ -202,7 +222,8 @@ void gnc_price_commit_edit (GNCPrice *p);
void gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c);
void gnc_price_set_currency(GNCPrice *p, gnc_commodity *c);
void gnc_price_set_time(GNCPrice *p, Timespec t);
void gnc_price_set_source(GNCPrice *p, const char *source);
void gnc_price_set_source(GNCPrice *p, PriceSource source);
void gnc_price_set_source_string(GNCPrice *p, const char* s);
void gnc_price_set_typestr(GNCPrice *p, const char* type);
void gnc_price_set_value(GNCPrice *p, gnc_numeric value);
/** @} */
@ -213,13 +234,14 @@ void gnc_price_set_value(GNCPrice *p, gnc_numeric value);
to the GNCPrice, not copies, so don't free these values.
@{ */
GNCPrice * gnc_price_lookup (const GncGUID *guid, QofBook *book);
GNCPrice * gnc_price_lookup (const GncGUID *guid, QofBook *book);
/*@ dependent @*/
gnc_commodity * gnc_price_get_commodity(const GNCPrice *p);
/*@ dependent @*/
gnc_commodity * gnc_price_get_currency(const GNCPrice *p);
Timespec gnc_price_get_time(const GNCPrice *p);
const char * gnc_price_get_source(const GNCPrice *p);
PriceSource gnc_price_get_source(const GNCPrice *p);
const char * gnc_price_get_source_string(const GNCPrice *p);
const char * gnc_price_get_typestr(const GNCPrice *p);
gnc_numeric gnc_price_get_value(const GNCPrice *p);
gboolean gnc_price_equal(const GNCPrice *p1, const GNCPrice *p2);
@ -234,6 +256,15 @@ gboolean gnc_price_equal(const GNCPrice *p1, const GNCPrice *p2);
/** This simple function can be useful for debugging the price code */
void gnc_price_print(GNCPrice *db, FILE *f, int indent);
/** @} */
/** @name Denominator Constants Price policy: In order to avoid rounding
* problems, currency prices (often called exchange rates) are saved in terms of
* the smaller currency, so that price > 1, with a fixed denominator of
* 1/1000. Commodity prices in currency are always expressed as value per unit
* of the commodity with a fixed denominator of the pricing currency's
* SCU * 10000.
*/
#define CURRENCY_DENOM 10000
#define COMMODITY_DENOM_MULT 10000
/* ================================================================ */
/** @name GNCPrice lists

View File

@ -656,6 +656,7 @@ void
make_random_changes_to_price (QofBook *book, GNCPrice *p)
{
Timespec *ts;
PriceSource ps;
char *string;
gnc_commodity *c;
@ -673,9 +674,9 @@ make_random_changes_to_price (QofBook *book, GNCPrice *p)
gnc_price_set_time (p, *ts);
g_free (ts);
string = get_random_string ();
gnc_price_set_source (p, string);
g_free (string);
ps = (PriceSource)get_random_int_in_range((int)PRICE_SOURCE_EDIT_DLG,
(int)PRICE_SOURCE_INVALID);
gnc_price_set_source (p, ps);
string = get_random_string ();
gnc_price_set_typestr (p, string);

File diff suppressed because it is too large Load Diff

View File

@ -786,7 +786,7 @@ gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
break;
case GNC_TREE_MODEL_PRICE_COL_SOURCE:
g_value_init (value, G_TYPE_STRING);
g_value_set_string (value, gettext (gnc_price_get_source (price)));
g_value_set_string (value, gettext (gnc_price_get_source_string (price)));
break;
case GNC_TREE_MODEL_PRICE_COL_TYPE:
g_value_init (value, G_TYPE_STRING);

View File

@ -300,8 +300,7 @@ sort_by_source (GtkTreeModel *f_model,
return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
/* sort by source first */
result = safe_utf8_collate (gnc_price_get_source (price_a),
gnc_price_get_source (price_b));
result = gnc_price_get_source (price_a) < gnc_price_get_source (price_b);
if (result != 0)
return result;

View File

@ -399,8 +399,8 @@ gnc_stock_split_assistant_finish (GtkAssistant *assistant,
gnc_price_set_commodity (price, xaccAccountGetCommodity (account));
gnc_price_set_currency (price, gnc_currency_edit_get_currency (ce));
gnc_price_set_time (price, ts);
gnc_price_set_source (price, "user:stock-split");
gnc_price_set_typestr (price, "unknown");
gnc_price_set_source (price, PRICE_SOURCE_STOCK_SPLIT);
gnc_price_set_typestr (price, PRICE_TYPE_UNK);
gnc_price_set_value (price, amount);
gnc_price_commit_edit (price);

View File

@ -50,8 +50,6 @@
#define DIALOG_PRICE_EDIT_CM_CLASS "dialog-price-edit"
#define GNC_PREFS_GROUP "dialogs.price-editor"
#define DIALOG_PRICE_EDIT_SOURCE "user:price-editor"
/* This static indicates the debugging module that this .o belongs to. */
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
@ -163,7 +161,7 @@ price_to_gui (PriceEditDialog *pedit_dialog)
currency = gnc_price_get_currency (pedit_dialog->price);
date = gnc_price_get_time (pedit_dialog->price);
source = gnc_price_get_source (pedit_dialog->price);
source = gnc_price_get_source_string (pedit_dialog->price);
type = gnc_price_get_typestr (pedit_dialog->price);
value = gnc_price_get_value (pedit_dialog->price);
}
@ -172,7 +170,7 @@ price_to_gui (PriceEditDialog *pedit_dialog)
currency = gnc_default_currency ();
date.tv_sec = gnc_time (NULL);
date.tv_nsec = 0;
source = DIALOG_PRICE_EDIT_SOURCE;
source = "user:price-editor"; //Sync with source_names in gnc-pricedb.c
type = "";
value = gnc_numeric_zero ();
}
@ -239,7 +237,7 @@ gui_to_price (PriceEditDialog *pedit_dialog)
gnc_price_set_commodity (pedit_dialog->price, commodity);
gnc_price_set_currency (pedit_dialog->price, currency);
gnc_price_set_time (pedit_dialog->price, date);
gnc_price_set_source (pedit_dialog->price, source);
gnc_price_set_source_string (pedit_dialog->price, source);
gnc_price_set_typestr (pedit_dialog->price, type);
gnc_price_set_value (pedit_dialog->price, value);
gnc_price_commit_edit (pedit_dialog->price);
@ -553,7 +551,7 @@ gnc_price_edit_dialog (GtkWidget * parent,
price = gnc_price_clone(price, pedit_dialog->book);
// } else {
// price = gnc_price_create (pedit_dialog->book);
gnc_price_set_source (price, DIALOG_PRICE_EDIT_SOURCE);
gnc_price_set_source (price, PRICE_SOURCE_EDIT_DLG);
}
pedit_dialog->is_new = TRUE;

View File

@ -1149,7 +1149,26 @@ gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
return TRUE;
}
gnc_numeric
gnc_numeric_invert(gnc_numeric num)
{
if (num.num == 0)
return gnc_numeric_zero();
if (num.denom > 0)
{
if (num.num < 0)
return gnc_numeric_create (-num.denom, -num.num);
return gnc_numeric_create (num.denom, num.num);
}
else /* Negative denominator means multiply instead of divide. */
{
int64_t mult = (num.num < 0 ? INT64_C(-1) : INT64_C(1));
qofint128 denom = mult128(-num.denom, mult * num.num);
if (denom.hi)
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
return gnc_numeric_create (mult, denom.lo);
}
}
/* *******************************************************************
* double_to_gnc_numeric
********************************************************************/

View File

@ -504,6 +504,13 @@ gnc_numeric gnc_numeric_reduce(gnc_numeric n);
********************************************************************/
gboolean gnc_numeric_to_decimal(gnc_numeric * a,
guint8 * max_decimal_places);
/** Invert a gnc_numeric.
* Much faster than dividing 1 by it.
* @param num The number to be inverted
* @return a gnc_numeric that is the inverse of num
*/
gnc_numeric gnc_numeric_invert (gnc_numeric num);
/** @} */
/** @name GValue

View File

@ -166,6 +166,15 @@ if ($exchange eq "currency") {
while ($#ARGV >= 0) {
my $to = shift;
my $result = $q->currency($from, $to);
unless (defined($result) && $result >= 1) {
my $inv_res = $q->currency($to, $from);
if (defined($inv_res)) {
my $tmp = $to;
$to = $from;
$from = $tmp;
$result = $inv_res;
}
}
if (defined($result)) {
printf "1 $from = $result $to\n";
} else {

View File

@ -350,6 +350,19 @@ while(<>) {
last unless $to_currency;
my $price = $quoter->currency($from_currency, $to_currency);
my $inv_price = undef;
#Sometimes price quotes are available in only one direction, and if the
#direction we asked for results in a quote < 1 we want the other direction
#if it's available to get more significant digits.
unless (defined($price) && $price > 1) {
$inv_price = $quoter->currency($to_currency, $from_currency);
if (defined($inv_price)) {
my $tmp = $to_currency;
$to_currency = $from_currency;
$from_currency = $tmp;
$price = $inv_price;
}
}
$quote_data{$from_currency, "success"} = defined($price);
$quote_data{$from_currency, "symbol"} = $from_currency;

View File

@ -64,8 +64,9 @@ static QofLogModule log_module = GNC_MOD_LEDGER;
static CursorClass copied_class = CURSOR_CLASS_NONE;
static SCM copied_item = SCM_UNDEFINED;
static GncGUID copied_leader_guid;
/* A denominator representing number of digits to the right of the decimal point
* displayed in a price cell. */
static int PRICE_CELL_DENOM = 1000000;
/** static prototypes *****************************************************/
static gboolean gnc_split_register_save_to_scm (SplitRegister *reg,
@ -2043,7 +2044,8 @@ recalculate_value (Split *split, SplitRegister *reg,
}
static void
record_price (SplitRegister *reg, Account *account, gnc_numeric value)
record_price (SplitRegister *reg, Account *account, gnc_numeric value,
PriceSource source)
{
Transaction *trans = gnc_split_register_get_current_trans (reg);
QofBook *book = qof_instance_get_book (QOF_INSTANCE (account));
@ -2051,8 +2053,11 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
gnc_commodity *comm = xaccAccountGetCommodity (account);
gnc_commodity *curr = xaccTransGetCurrency (trans);
GNCPrice *price;
gnc_numeric price_value;
int scu = gnc_commodity_get_fraction(curr);
Timespec ts;
BasicCell *cell;
BasicCell *cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL);
gboolean swap = FALSE;
/* Only record the price for account types that don't have a
* "rate" cell. They'll get handled later by
@ -2060,14 +2065,58 @@ record_price (SplitRegister *reg, Account *account, gnc_numeric value)
*/
if (gnc_split_reg_has_rate_cell (reg->type))
return;
cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL);
gnc_date_cell_get_date ((DateCell*)cell, &ts);
price = gnc_pricedb_lookup_day (pricedb, comm, curr, ts);
if (!price)
{
price = gnc_pricedb_lookup_day (pricedb, curr, comm, ts);
if (price)
/* It might be better to raise an error here: We shouldn't be creating
* currency->commodity prices.
*/
swap = TRUE;
}
if (price)
{
price_value = gnc_price_get_value(price);
if (gnc_numeric_equal(swap ? gnc_numeric_invert(value) : value,
price_value))
{
gnc_price_unref (price);
return;
}
if (gnc_price_get_source(price) < PRICE_SOURCE_XFER_DLG_VAL)
{
/* Existing price is preferred over this one. */
gnc_price_unref(price);
return;
}
if (swap)
{
value = gnc_numeric_invert(value);
scu = gnc_commodity_get_fraction(comm);
}
value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
GNC_HOW_RND_ROUND_HALF_UP);
gnc_price_begin_edit (price);
gnc_price_set_time (price, ts);
gnc_price_set_source (price, source);
gnc_price_set_typestr (price, PRICE_TYPE_TRN);
gnc_price_set_value (price, value);
gnc_price_commit_edit (price);
gnc_price_unref (price);
return;
}
value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
GNC_HOW_RND_ROUND_HALF_UP);
price = gnc_price_create (book);
gnc_price_begin_edit (price);
gnc_price_set_commodity (price, comm);
gnc_price_set_currency (price, curr);
gnc_price_set_time (price, ts);
gnc_price_set_source (price, "user:split-register");
gnc_price_set_source (price, source);
gnc_price_set_typestr (price, PRICE_TYPE_TRN);
gnc_price_set_value (price, value);
gnc_pricedb_add_price (pricedb, price);
gnc_price_commit_edit (price);
@ -2090,6 +2139,7 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
Account *account;
int denom;
int choice;
PriceSource source = PRICE_SOURCE_USER_PRICE;
if (STOCK_REGISTER != reg->type &&
CURRENCY_REGISTER != reg->type &&
@ -2238,6 +2288,7 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
{
recalculate_price (split, reg, value, amount);
price_changed = TRUE;
source = PRICE_SOURCE_SPLIT_REG;
}
if (recalc_value)
recalculate_value (split, reg, price, amount, shares_changed);
@ -2248,7 +2299,7 @@ gnc_split_register_auto_calc (SplitRegister *reg, Split *split)
PRIC_CELL);
price = gnc_price_cell_get_value (cell);
if (gnc_numeric_positive_p(price))
record_price (reg, account, price);
record_price (reg, account, price, source);
}
return TRUE;
}
@ -2547,7 +2598,8 @@ gnc_split_register_config_cells (SplitRegister *reg)
/* Use 6 decimal places for prices and "exchange rates" */
gnc_price_cell_set_fraction
((PriceCell *)
gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL), 1000000);
gnc_table_layout_get_cell (reg->table->layout, PRIC_CELL),
PRICE_CELL_DENOM);
/* Initialize shares and share balance cells */
gnc_price_cell_set_print_info

View File

@ -1,17 +1,17 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; price-quotes.scm - manage sub-processes.
;;; Copyright 2001 Rob Browning <rlb@cs.utexas.edu>
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License as
;;; published by the Free Software Foundation; either version 2 of
;;; the License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License as
;;; published by the Free Software Foundation; either version 2 of
;;; the License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, contact:
;;;
@ -35,7 +35,7 @@
(define (item-list->hash! lst hash
getkey getval
hashref hashset
hashref hashset
list-duplicates?)
;; Takes a list of the form (item item item item) and returns a hash
;; formed by traversing the list, and getting the key and val from
@ -58,7 +58,7 @@
(if existing-val
(hashset hash key (cons val existing-val))
(hashset hash key (list val))))))
(for-each handle-item lst)
hash)
@ -205,7 +205,7 @@
;; a list of the corresponding commodities. Also perform a bit of
;; optimization, merging calls for symbols to the same
;; Finance::Quote method.
;;
;;
;; Returns a list of the info needed for a set of calls to
;; gnc-fq-helper. Each item will of the list will be of the
;; form:
@ -223,7 +223,7 @@
(commodity-list #f)
(currency-list (filter
(lambda (a) (not (gnc-commodity-equiv (cadr a) (caddr a))))
(call-with-values
(call-with-values
(lambda () (partition!
(lambda (cmd)
(not (string=? (car cmd) "currency")))
@ -257,7 +257,7 @@
;;
;; ("yahoo" (commodity-1 currency-1 tz-1)
;; (commodity-2 currency-2 tz-2) ...)
;;
;;
;; ("yahoo" "IBM" "AMD" ...)
;;
@ -370,8 +370,20 @@
(string? currency-str)
(gnc-commodity-table-lookup commodity-table
"ISO4217"
(string-upcase currency-str)))))
(string-upcase currency-str))))
(pricedb (gnc-pricedb-get-db book))
(saved-price #f)
(commodity-str (gnc-commodity-get-printname commodity))
)
(if (equal? (gnc-commodity-get-printname currency) commodity-str)
(let* ((symbol (assq-ref quote-data 'symbol))
(other-curr
(and commodity-table
(string? symbol)
(gnc-commodity-table-lookup commodity-table "ISO4217"
(string-upcase symbol)))))
(set! commodity other-curr))
)
(or-map (lambda (price-sym)
(let ((p (assq-ref quote-data price-sym)))
(if p
@ -403,27 +415,52 @@
(if (not (and commodity currency gnc-time price price-type))
(string-append
currency-str ":" (gnc-commodity-get-mnemonic commodity))
(let ((gnc-price (gnc-price-create book)))
(if (not gnc-price)
(string-append
currency-str ":" (gnc-commodity-get-mnemonic commodity))
(begin
(set! saved-price (gnc-pricedb-lookup-day pricedb
commodity currency
gnc-time))
(if (null? saved-price)
(begin
(gnc-price-begin-edit gnc-price)
(gnc-price-set-commodity gnc-price commodity)
(gnc-price-set-currency gnc-price currency)
(gnc-price-set-time gnc-price gnc-time)
(gnc-price-set-source gnc-price "Finance::Quote")
(gnc-price-set-typestr gnc-price price-type)
(gnc-price-set-value gnc-price price)
(gnc-price-commit-edit gnc-price)
gnc-price))))))
(set! saved-price (gnc-pricedb-lookup-day pricedb currency
commodity gnc-time))
(if (not (null? saved-price))
(set! price (gnc-numeric-invert(price))))))
(if (not (null? saved-price))
(if (> (gnc-price-get-source saved-price) PRICE-SOURCE-FQ)
(begin
(gnc-price-begin-edit saved-price)
(gnc-price-set-time saved-price gnc-time)
(gnc-price-set-source saved-price PRICE-SOURCE-FQ)
(gnc-price-set-typestr saved-price price-type)
(gnc-price-set-value saved-price price)
(gnc-price-commit-edit saved-price)
#f)
#f)
(let ((gnc-price (gnc-price-create book)))
(if (not gnc-price)
(string-append
currency-str ":" (gnc-commodity-get-mnemonic commodity))
(begin
(gnc-price-begin-edit gnc-price)
(gnc-price-set-commodity gnc-price commodity)
(gnc-price-set-currency gnc-price currency)
(gnc-price-set-time gnc-price gnc-time)
(gnc-price-set-source gnc-price PRICE-SOURCE-FQ)
(gnc-price-set-typestr gnc-price price-type)
(gnc-price-set-value gnc-price price)
(gnc-price-commit-edit gnc-price)
gnc-price)))))
)))
(define (book-add-prices! book prices)
(let ((pricedb (gnc-pricedb-get-db book)))
(for-each
(lambda (price)
(gnc-pricedb-add-price pricedb price)
(gnc-price-unref price))
(if price
(begin
(gnc-pricedb-add-price pricedb price)
(gnc-price-unref price)
#f)))
prices)))
;; FIXME: uses of gnc:warn in here need to be cleaned up. Right