mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Merge branch 'price-quotes-cpp'
This commit is contained in:
commit
939a77407c
@ -512,6 +512,14 @@ if (NOT PERL_FOUND)
|
||||
message(SEND_ERROR "Perl executable not found. Please set PERL_EXECUTABLE.")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
${PERL_EXECUTABLE} -MFinance::Quote -e ""
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE have_f_q)
|
||||
if (${have_f_q} EQUAL 0)
|
||||
set(HAVE_F_Q 1)
|
||||
endif()
|
||||
|
||||
get_filename_component(PERL_DIR ${PERL_EXECUTABLE} DIRECTORY)
|
||||
|
||||
find_program(POD2MAN_EXECUTABLE pod2man HINTS ${PERL_DIR})
|
||||
|
@ -74,22 +74,6 @@ Account * gnc_get_current_root_account (void);
|
||||
|
||||
|
||||
#if defined(SWIGGUILE)
|
||||
%typemap(out) GncCommodityList * {
|
||||
SCM list = SCM_EOL;
|
||||
GList *node;
|
||||
|
||||
for (node = $1; node; node = node->next)
|
||||
list = scm_cons(gnc_quoteinfo2scm(static_cast<gnc_commodity*>(node->data)), list);
|
||||
|
||||
$result = scm_reverse(list);
|
||||
}
|
||||
|
||||
%inline %{
|
||||
typedef GList GncCommodityList;
|
||||
|
||||
GncCommodityList *
|
||||
gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table);
|
||||
%}
|
||||
|
||||
gnc_commodity * gnc_default_currency (void);
|
||||
gnc_commodity * gnc_default_report_currency (void);
|
||||
|
@ -90,11 +90,6 @@ gchar * gnc_build_stdreports_path(const gchar *);
|
||||
%newobject gnc_build_reports_path;
|
||||
gchar * gnc_build_reports_path(const gchar *);
|
||||
|
||||
void gnc_scm_log_warn(const gchar *);
|
||||
void gnc_scm_log_error(const gchar *);
|
||||
void gnc_scm_log_msg(const gchar *);
|
||||
void gnc_scm_log_debug(const gchar *);
|
||||
|
||||
%newobject gnc_utf8_strip_invalid_strdup;
|
||||
gchar * gnc_utf8_strip_invalid_strdup(const gchar *);
|
||||
|
||||
|
@ -111,6 +111,15 @@ engine-common.i */
|
||||
%include "qoflog.h"
|
||||
|
||||
%inline %{
|
||||
static void gnc_log_warn(const char *msg)
|
||||
{ g_log("gnc.scm", G_LOG_LEVEL_WARNING, "%s", msg); }
|
||||
static void gnc_log_error(const char *msg)
|
||||
{ g_log("gnc.scm", G_LOG_LEVEL_CRITICAL, "%s", msg); }
|
||||
static void gnc_log_msg(const char *msg)
|
||||
{ g_log("gnc.scm", G_LOG_LEVEL_MESSAGE, "%s", msg); }
|
||||
static void gnc_log_debug(const char *msg)
|
||||
{ g_log("gnc.scm", G_LOG_LEVEL_DEBUG, "%s", msg); }
|
||||
|
||||
static const GncGUID * gncPriceGetGUID(GNCPrice *x)
|
||||
{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
|
||||
static const GncGUID * gncBudgetGetGUID(GncBudget *x)
|
||||
@ -248,34 +257,8 @@ SplitList * qof_query_run_subquery (QofQuery *q, const QofQuery *q);
|
||||
time64 time64CanonicalDayTime(time64 t);
|
||||
|
||||
%include <gnc-budget.h>
|
||||
|
||||
%typemap(in) GList * {
|
||||
SCM path_scm = $input;
|
||||
GList *path = NULL;
|
||||
|
||||
while (!scm_is_null (path_scm))
|
||||
{
|
||||
SCM key_scm = SCM_CAR (path_scm);
|
||||
char *key;
|
||||
if (!scm_is_string (key_scm))
|
||||
break;
|
||||
|
||||
key = scm_to_locale_string (key_scm);
|
||||
path = g_list_prepend (path, key);
|
||||
|
||||
path_scm = SCM_CDR (path_scm);
|
||||
}
|
||||
|
||||
$1 = g_list_reverse (path);
|
||||
}
|
||||
|
||||
%typemap (freearg) GList * "g_list_free_full ($1, g_free);"
|
||||
|
||||
void gnc_quote_source_set_fq_installed (const char* version_string,
|
||||
GList *sources_list);
|
||||
%clear GList *;
|
||||
%ignore gnc_quote_source_set_fq_installed;
|
||||
%ignore gnc_commodity_table_get_quotable_commodities;
|
||||
%include <gnc-commodity.h>
|
||||
|
||||
void gnc_hook_add_scm_dangler (const gchar *name, SCM proc);
|
||||
@ -475,8 +458,3 @@ void qof_book_set_string_option(QofBook* book, const char* opt_name, const char*
|
||||
}
|
||||
$1 = g_list_reverse (path);
|
||||
}
|
||||
Process *gnc_spawn_process_async(GList *argl, const gboolean search_path);
|
||||
%clear GList *;
|
||||
|
||||
gint gnc_process_get_fd(const Process *proc, const guint std_fd);
|
||||
void gnc_detach_process(Process *proc, const gboolean kill_it);
|
||||
|
@ -202,139 +202,3 @@ gnc_glist_string_p(SCM list)
|
||||
{
|
||||
return scm_is_list(list);
|
||||
}
|
||||
|
||||
struct _Process
|
||||
{
|
||||
GPid pid;
|
||||
gint fd_stdin;
|
||||
gint fd_stdout;
|
||||
gint fd_stderr;
|
||||
gboolean dead;
|
||||
gboolean detached;
|
||||
};
|
||||
|
||||
static void
|
||||
on_child_exit (GPid pid, gint status, gpointer data)
|
||||
{
|
||||
Process *proc = data;
|
||||
g_return_if_fail (proc && proc->pid == pid);
|
||||
|
||||
g_spawn_close_pid (proc->pid);
|
||||
|
||||
/* free if the process is both dead and detached */
|
||||
if (!proc->detached)
|
||||
proc->dead = TRUE;
|
||||
else
|
||||
g_free (proc);
|
||||
}
|
||||
|
||||
Process *
|
||||
gnc_spawn_process_async (GList *argl, const gboolean search_path)
|
||||
{
|
||||
gboolean retval;
|
||||
Process *proc;
|
||||
GList *l_iter;
|
||||
guint argc;
|
||||
gchar **argv, **v_iter;
|
||||
GSpawnFlags flags;
|
||||
GError *error = NULL;
|
||||
|
||||
proc = g_new0 (Process, 1);
|
||||
|
||||
argc = g_list_length (argl);
|
||||
argv = g_malloc ((argc + 1) * sizeof(gchar*));
|
||||
|
||||
for (l_iter = argl, v_iter = argv; l_iter; l_iter = l_iter->next, v_iter++)
|
||||
{
|
||||
*v_iter = (gchar*) l_iter->data;
|
||||
}
|
||||
*v_iter = NULL;
|
||||
g_list_free (argl);
|
||||
|
||||
flags = G_SPAWN_DO_NOT_REAP_CHILD;
|
||||
if (search_path)
|
||||
flags |= G_SPAWN_SEARCH_PATH;
|
||||
|
||||
retval = g_spawn_async_with_pipes (
|
||||
NULL, argv, NULL, flags, NULL, NULL, &proc->pid,
|
||||
&proc->fd_stdin, &proc->fd_stdout, &proc->fd_stderr, &error);
|
||||
|
||||
if (retval)
|
||||
{
|
||||
g_child_watch_add (proc->pid, on_child_exit, proc);
|
||||
}
|
||||
else
|
||||
{
|
||||
PWARN ("Could not spawn %s: %s", *argv ? *argv : "(null)",
|
||||
error->message ? error->message : "(null)");
|
||||
g_free (proc);
|
||||
proc = NULL;
|
||||
}
|
||||
g_strfreev (argv);
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
gint
|
||||
gnc_process_get_fd (const Process *proc, const gint std_fd)
|
||||
{
|
||||
const gint *retptr = NULL;
|
||||
g_return_val_if_fail (proc, -1);
|
||||
|
||||
if (std_fd == 0)
|
||||
retptr = &proc->fd_stdin;
|
||||
else if (std_fd == 1)
|
||||
retptr = &proc->fd_stdout;
|
||||
else if (std_fd == 2)
|
||||
retptr = &proc->fd_stderr;
|
||||
else
|
||||
g_return_val_if_reached (-1);
|
||||
|
||||
if (*retptr == -1)
|
||||
PWARN ("Pipe to child's file descriptor %d is -1", std_fd);
|
||||
return *retptr;
|
||||
}
|
||||
|
||||
void
|
||||
gnc_detach_process (Process *proc, const gboolean kill_it)
|
||||
{
|
||||
g_return_if_fail (proc && proc->pid);
|
||||
|
||||
errno = 0;
|
||||
close (proc->fd_stdin);
|
||||
if (errno)
|
||||
{
|
||||
PINFO ("Close of child's stdin (%d) failed: %s", proc->fd_stdin,
|
||||
g_strerror (errno));
|
||||
errno = 0;
|
||||
}
|
||||
close (proc->fd_stdout);
|
||||
if (errno)
|
||||
{
|
||||
PINFO ("Close of child's stdout (%d) failed: %s", proc->fd_stdout,
|
||||
g_strerror(errno));
|
||||
errno = 0;
|
||||
}
|
||||
close (proc->fd_stderr);
|
||||
if (errno)
|
||||
{
|
||||
PINFO ("Close of child's stderr (%d) failed: %s", proc->fd_stderr,
|
||||
g_strerror(errno));
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
if (kill_it && !proc->dead)
|
||||
{
|
||||
/* give it a chance to die */
|
||||
while (g_main_context_iteration (NULL, FALSE) && !proc->dead)
|
||||
;
|
||||
if (!proc->dead)
|
||||
gnc_gpid_kill (proc->pid);
|
||||
}
|
||||
|
||||
/* free if the process is both dead and detached */
|
||||
if (!proc->dead)
|
||||
proc->detached = TRUE;
|
||||
else
|
||||
g_free (proc);
|
||||
}
|
||||
|
@ -37,40 +37,4 @@ int gnc_glist_string_p(SCM list);
|
||||
|
||||
GSList * gnc_scm_to_gslist_string(SCM list);
|
||||
|
||||
/** An opaque process structure returned by gnc_spawn_process_async. */
|
||||
typedef struct _Process Process;
|
||||
|
||||
/** Wraps g_spawn_async_with_pipes minimally. Use gnc_process_get_fd to access
|
||||
* the file descriptors to the child. To close them and free the memory
|
||||
* allocated for the process once it has exited, call gnc_detach_process.
|
||||
*
|
||||
* @param argl A list of null-terminated strings used as arguments for spawning,
|
||||
* i.e. "perl" "-w" "my-perl-script". Will be freed inside.
|
||||
*
|
||||
* @param search_path Determines whether the first element of argl will be
|
||||
* looked for in the user's PATH.
|
||||
*
|
||||
* @return A pointer to a structure representing the process or NULL on failure.
|
||||
*/
|
||||
Process *gnc_spawn_process_async(GList *argl, const gboolean search_path);
|
||||
|
||||
/** Accesses a given process structure and returns the file descriptor connected
|
||||
* to the childs stdin, stdout or stderr.
|
||||
*
|
||||
* @param proc A process structure returned by gnc_spawn_process_async.
|
||||
*
|
||||
* @param std_fd 0, 1 or 2.
|
||||
*
|
||||
* @return The file descriptor to write to the child on 0, or read from the
|
||||
* childs output or error on 1 or 2, resp. */
|
||||
gint gnc_process_get_fd(const Process *proc, const gint std_fd);
|
||||
|
||||
/** Close the file descriptors to a given process and declare it as detached. If
|
||||
* it is both dead and detached, the allocated memory will be freed.
|
||||
*
|
||||
* @param proc A process structure returned by gnc_spawn_process_async.
|
||||
*
|
||||
* @param kill_it If TRUE, kill the process. */
|
||||
void gnc_detach_process(Process *proc, const gboolean kill_it);
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,6 @@ extern "C"
|
||||
#include "Account.h"
|
||||
#include "engine-helpers.h"
|
||||
#include "gnc-engine-guile.h"
|
||||
#include "glib-guile.h"
|
||||
#include "gnc-date.h"
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-session.h"
|
||||
|
@ -92,44 +92,3 @@ gnc_scm2printinfo(SCM info_scm)
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/* This is a scaled down version of the routine that would be needed
|
||||
* to fully convert a gnc-commodity to a scheme data structure. In an
|
||||
* attempt to optimize the speed of price quote retrieval, this
|
||||
* routine only converts the fields that price-quotes.scm uses. Since
|
||||
* it converts these fields all at once, it should prevent multiple
|
||||
* transitions back and forth from Scheme to C to extract
|
||||
* the data from a pointers to a gnc-commodity (the older method).
|
||||
* This is *not* a reversible conversion as it drops data.
|
||||
*
|
||||
* When this routine was written, gnucash retrieved all quotes into
|
||||
* the user's default currency. (Did earlier version do any
|
||||
* different?) This routine inserts that default currency into the
|
||||
* returned structure as another optimization.
|
||||
*/
|
||||
SCM
|
||||
gnc_quoteinfo2scm(gnc_commodity *comm)
|
||||
{
|
||||
gnc_quote_source *source;
|
||||
const char *name, *tz;
|
||||
SCM info_scm = SCM_EOL, comm_scm, def_comm_scm;
|
||||
|
||||
if (!comm)
|
||||
return SCM_EOL;
|
||||
|
||||
source = gnc_commodity_get_quote_source (comm);
|
||||
name = gnc_quote_source_get_internal_name (source);
|
||||
tz = gnc_commodity_get_quote_tz (comm);
|
||||
comm_scm = SWIG_NewPointerObj(comm, SWIG_TypeQuery("_p_gnc_commodity"), 0);
|
||||
def_comm_scm = SWIG_NewPointerObj(gnc_default_currency (),
|
||||
SWIG_TypeQuery("_p_gnc_commodity"), 0);
|
||||
|
||||
if (tz)
|
||||
info_scm = scm_cons (scm_from_utf8_string (tz), info_scm);
|
||||
else
|
||||
info_scm = scm_cons (SCM_BOOL_F, info_scm);
|
||||
info_scm = scm_cons (def_comm_scm, info_scm);
|
||||
info_scm = scm_cons (comm_scm, info_scm);
|
||||
info_scm = scm_cons (name ? scm_from_utf8_string (name) : SCM_BOOL_F, info_scm);
|
||||
return info_scm;
|
||||
}
|
||||
|
@ -31,16 +31,4 @@
|
||||
SCM gnc_printinfo2scm(GNCPrintAmountInfo info);
|
||||
GNCPrintAmountInfo gnc_scm2printinfo(SCM info_scm);
|
||||
|
||||
/** Given a pointer to a gnc-commodity data structure, build a Scheme
|
||||
* list containing the data needed by the code in price-quotes.scm.
|
||||
* This prevents flipping back and forth from Scheme to C while
|
||||
* extracting values from a pointer.
|
||||
*
|
||||
* @param com A pointer to the commodity to convert.
|
||||
*
|
||||
* @return A pointer to a Scheme list, or SCM_EOL on error.
|
||||
*/
|
||||
SCM gnc_quoteinfo2scm(gnc_commodity *com);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -40,17 +40,17 @@
|
||||
(string-join (map (lambda (x) (format #f "~A" x)) items) ""))
|
||||
|
||||
(define (gnc:warn . items)
|
||||
(gnc-scm-log-warn (strify items)))
|
||||
(gnc-log-warn (strify items)))
|
||||
|
||||
(define (gnc:error . items)
|
||||
(gnc-scm-log-error (strify items )))
|
||||
(gnc-log-error (strify items )))
|
||||
|
||||
(define (gnc:msg . items)
|
||||
(gnc-scm-log-msg (strify items)))
|
||||
(gnc-log-msg (strify items)))
|
||||
|
||||
(define (gnc:debug . items)
|
||||
(when (qof-log-check "gnc.scm" QOF-LOG-DEBUG)
|
||||
(gnc-scm-log-debug (strify items))))
|
||||
(gnc-log-debug (strify items))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; the following functions are initialized to log message to tracefile
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# Test file for price database stuff
|
||||
# To update the price database call
|
||||
# $PATH/gnucash --add-price-quotes $PATHTOFILE
|
||||
# $PATH/gnucash-cli --quotes get $PATHTOFILE
|
||||
# before running this.
|
||||
# Adding to a calling bash script would be better
|
||||
# Although calling it from here would be even better!
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# Another test file for price database stuff
|
||||
# To update the price database call
|
||||
# $PATH/gnucash --add-price-quotes $PATHTOFILE
|
||||
# $PATH/gnucash-cli --quotes get $PATHTOFILE
|
||||
# before running this.
|
||||
# Adding to a calling bash script would be better
|
||||
# Although calling it from here would be even better!
|
||||
|
@ -129,6 +129,9 @@
|
||||
/* Define to 1 if you have the `pthread' library (-lpthread). */
|
||||
#cmakedefine HAVE_LIBPTHREAD 1
|
||||
|
||||
/* Define to 1 if Finance::Quote is installed in perl. */
|
||||
#cmakedefine HAVE_F_Q 1
|
||||
|
||||
/* System has libsecret 0.18 or better */
|
||||
#cmakedefine HAVE_LIBSECRET 1
|
||||
|
||||
|
@ -267,15 +267,10 @@ foreach(gres_file ${gresource_files})
|
||||
endforeach()
|
||||
|
||||
|
||||
gnc_add_scheme_targets(price-quotes
|
||||
SOURCES price-quotes.scm
|
||||
OUTPUT_DIR gnucash
|
||||
DEPENDS "scm-engine;scm-app-utils;scm-gnome-utils")
|
||||
|
||||
set_local_dist(gnucash_DIST_local CMakeLists.txt environment.in generate-gnc-script
|
||||
gnucash.cpp gnucash-commands.cpp gnucash-cli.cpp gnucash-core-app.cpp
|
||||
gnucash-locale-macos.mm gnucash-locale-windows.c gnucash.rc.in gnucash-valgrind.in
|
||||
gnucash-gresources.xml ${gresource_files} price-quotes.scm
|
||||
gnucash-gresources.xml ${gresource_files}
|
||||
${gnucash_noinst_HEADERS} ${gnucash_EXTRA_DIST})
|
||||
|
||||
set (gnucash_DIST ${gnucash_DIST_local} ${gnome_DIST} ${gnome_search_DIST} ${gnome_utils_DIST}
|
||||
|
@ -43,7 +43,7 @@ set (gnome_utils_SOURCES
|
||||
dialog-reset-warnings.c
|
||||
dialog-tax-table.c
|
||||
dialog-totd.c
|
||||
dialog-transfer.c
|
||||
dialog-transfer.cpp
|
||||
dialog-userpass.c
|
||||
dialog-utils.c
|
||||
gnc-account-sel.c
|
||||
|
@ -27,7 +27,9 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <gnc-quotes.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include "dialog-transfer.h"
|
||||
#include "dialog-utils.h"
|
||||
#include "gnc-amount-edit.h"
|
||||
@ -43,13 +45,10 @@
|
||||
#include "gnc-ui.h"
|
||||
#include "Transaction.h"
|
||||
#include "Account.h"
|
||||
#include <libguile.h>
|
||||
#include "swig-runtime.h"
|
||||
#include "guile-mappings.h"
|
||||
#include "engine-helpers.h"
|
||||
#include "gnc-engine-guile.h"
|
||||
#include "QuickFill.h"
|
||||
#include <gnc-commodity.h>
|
||||
}
|
||||
|
||||
|
||||
#define DIALOG_TRANSFER_CM_CLASS "dialog-transfer"
|
||||
@ -162,6 +161,7 @@ static void gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
|
||||
Account *account,
|
||||
XferDirection direction);
|
||||
|
||||
extern "C" {
|
||||
void gnc_xfer_description_insert_cb(GtkEditable *editable,
|
||||
const gchar *insert_text,
|
||||
const gint insert_text_len,
|
||||
@ -177,6 +177,7 @@ void price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data)
|
||||
|
||||
void gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data);
|
||||
void gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data);
|
||||
}
|
||||
|
||||
/** Implementations **********************************************/
|
||||
|
||||
@ -337,10 +338,9 @@ gnc_xfer_dialog_update_price (XferDialog *xferData)
|
||||
static void
|
||||
gnc_xfer_dialog_toggle_cb(GtkToggleButton *button, gpointer data)
|
||||
{
|
||||
AccountTreeFilterInfo* info;
|
||||
GncTreeViewAccount* treeview = GNC_TREE_VIEW_ACCOUNT (data);
|
||||
|
||||
info = g_object_get_data (G_OBJECT(treeview), "filter-info");
|
||||
auto info = static_cast<AccountTreeFilterInfo*> (g_object_get_data (G_OBJECT(treeview), "filter-info"));
|
||||
if (info)
|
||||
{
|
||||
info->show_inc_exp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
|
||||
@ -355,11 +355,9 @@ gnc_xfer_dialog_key_press_cb (GtkWidget *widget,
|
||||
GdkEventKey *event,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
|
||||
if ((event->keyval == GDK_KEY_Return) || (event->keyval == GDK_KEY_KP_Enter))
|
||||
{
|
||||
toplevel = gtk_widget_get_toplevel (widget);
|
||||
auto toplevel = gtk_widget_get_toplevel (widget);
|
||||
if (gtk_widget_is_toplevel(toplevel) && GTK_IS_WINDOW(toplevel))
|
||||
{
|
||||
gtk_window_activate_default(GTK_WINDOW(toplevel));
|
||||
@ -375,15 +373,10 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
|
||||
const gnc_commodity *from_currency,
|
||||
const gnc_commodity *to_currency)
|
||||
{
|
||||
gnc_numeric from_rate;
|
||||
gnc_numeric to_rate;
|
||||
gnc_numeric price_value;
|
||||
|
||||
if (!currency_active)
|
||||
{
|
||||
GtkEntry *entry;
|
||||
gnc_xfer_dialog_set_price_edit(xferData, gnc_numeric_zero());
|
||||
entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
|
||||
auto entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
|
||||
(GNC_AMOUNT_EDIT(xferData->price_edit)));
|
||||
gtk_entry_set_text(entry, "");
|
||||
|
||||
@ -399,13 +392,13 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
|
||||
return;
|
||||
}
|
||||
|
||||
from_rate = gnc_euro_currency_get_rate (from_currency);
|
||||
to_rate = gnc_euro_currency_get_rate (to_currency);
|
||||
auto from_rate = gnc_euro_currency_get_rate (from_currency);
|
||||
auto to_rate = gnc_euro_currency_get_rate (to_currency);
|
||||
|
||||
if (gnc_numeric_zero_p (from_rate) || gnc_numeric_zero_p (to_rate))
|
||||
gnc_xfer_dialog_update_price (xferData);
|
||||
|
||||
price_value = gnc_numeric_div (to_rate, from_rate, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
|
||||
auto price_value = gnc_numeric_div (to_rate, from_rate, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
|
||||
|
||||
gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(xferData->price_edit), price_value);
|
||||
|
||||
@ -415,21 +408,17 @@ gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
|
||||
static void
|
||||
gnc_xfer_dialog_curr_acct_activate(XferDialog *xferData)
|
||||
{
|
||||
Account *to_account;
|
||||
Account *from_account;
|
||||
gboolean curr_active;
|
||||
|
||||
g_return_if_fail (xferData != NULL);
|
||||
from_account =
|
||||
auto from_account =
|
||||
gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
|
||||
|
||||
to_account =
|
||||
auto to_account =
|
||||
gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
|
||||
|
||||
curr_active = (xferData->exch_rate ||
|
||||
((from_account != NULL) && (to_account != NULL)))
|
||||
&& !gnc_commodity_equiv(xferData->from_commodity,
|
||||
xferData->to_commodity);
|
||||
gboolean curr_active =
|
||||
(xferData->exch_rate ||
|
||||
((from_account != NULL) && (to_account != NULL))) &&
|
||||
!gnc_commodity_equiv(xferData->from_commodity, xferData->to_commodity);
|
||||
|
||||
gtk_widget_set_sensitive(xferData->curr_xfer_table, curr_active);
|
||||
gtk_widget_set_sensitive(xferData->price_edit,
|
||||
@ -447,12 +436,10 @@ gnc_xfer_dialog_curr_acct_activate(XferDialog *xferData)
|
||||
|
||||
if (!curr_active)
|
||||
{
|
||||
GtkEntry *entry;
|
||||
|
||||
gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->to_amount_edit),
|
||||
gnc_numeric_zero ());
|
||||
entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
|
||||
(GNC_AMOUNT_EDIT(xferData->to_amount_edit)));
|
||||
auto entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
|
||||
(GNC_AMOUNT_EDIT(xferData->to_amount_edit)));
|
||||
gtk_entry_set_text(entry, "");
|
||||
}
|
||||
}
|
||||
@ -461,9 +448,9 @@ gnc_xfer_dialog_curr_acct_activate(XferDialog *xferData)
|
||||
void
|
||||
price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
g_return_if_fail (xferData != NULL);
|
||||
g_return_if_fail (data);
|
||||
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
gtk_widget_set_sensitive(xferData->price_edit, gtk_toggle_button_get_active
|
||||
(GTK_TOGGLE_BUTTON(xferData->price_radio)));
|
||||
gtk_widget_set_sensitive(xferData->to_amount_edit,
|
||||
@ -481,23 +468,17 @@ price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data)
|
||||
static void
|
||||
gnc_xfer_dialog_reload_quickfill( XferDialog *xferData )
|
||||
{
|
||||
GList *splitlist, *node;
|
||||
Split *split;
|
||||
Transaction *trans;
|
||||
Account *account;
|
||||
|
||||
account = gnc_transfer_dialog_get_selected_account (xferData, xferData->quickfill);
|
||||
auto account = gnc_transfer_dialog_get_selected_account (xferData, xferData->quickfill);
|
||||
|
||||
/* get a new QuickFill to use */
|
||||
gnc_quickfill_destroy( xferData->qf );
|
||||
xferData->qf = gnc_quickfill_new();
|
||||
|
||||
splitlist = xaccAccountGetSplitList( account );
|
||||
|
||||
for ( node = splitlist; node; node = node->next )
|
||||
auto splitlist = xaccAccountGetSplitList( account );
|
||||
for ( GList *node = splitlist; node; node = node->next )
|
||||
{
|
||||
split = node->data;
|
||||
trans = xaccSplitGetParent( split );
|
||||
auto split = static_cast<Split *> (node->data);
|
||||
auto trans = xaccSplitGetParent (split);
|
||||
gnc_quickfill_insert( xferData->qf,
|
||||
xaccTransGetDescription (trans), QUICKFILL_LIFO);
|
||||
}
|
||||
@ -508,22 +489,19 @@ static void
|
||||
gnc_xfer_dialog_from_tree_selection_changed_cb (GtkTreeSelection *selection,
|
||||
gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
GNCPrintAmountInfo print_info;
|
||||
gnc_commodity *commodity;
|
||||
Account *account;
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
|
||||
auto account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
|
||||
if (!account)
|
||||
return;
|
||||
|
||||
commodity = gnc_account_or_default_currency(account, NULL);
|
||||
auto commodity = gnc_account_or_default_currency(account, NULL);
|
||||
gtk_label_set_text(GTK_LABEL(xferData->from_currency_label),
|
||||
gnc_commodity_get_printname(commodity));
|
||||
|
||||
xferData->from_commodity = commodity;
|
||||
|
||||
print_info = gnc_account_print_info (account, FALSE);
|
||||
auto print_info = gnc_account_print_info (account, FALSE);
|
||||
gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (xferData->amount_edit),
|
||||
print_info);
|
||||
gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (xferData->amount_edit),
|
||||
@ -542,22 +520,19 @@ gnc_xfer_dialog_from_tree_selection_changed_cb (GtkTreeSelection *selection,
|
||||
static void
|
||||
gnc_xfer_dialog_to_tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
GNCPrintAmountInfo print_info;
|
||||
gnc_commodity *commodity;
|
||||
Account *account;
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
|
||||
auto account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
|
||||
if (!account)
|
||||
return;
|
||||
|
||||
commodity = xaccAccountGetCommodity(account);
|
||||
auto commodity = xaccAccountGetCommodity(account);
|
||||
gtk_label_set_text(GTK_LABEL(xferData->to_currency_label),
|
||||
gnc_commodity_get_printname(commodity));
|
||||
|
||||
xferData->to_commodity = commodity;
|
||||
|
||||
print_info = gnc_account_print_info (account, FALSE);
|
||||
auto print_info = gnc_account_print_info (account, FALSE);
|
||||
gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (xferData->to_amount_edit),
|
||||
print_info);
|
||||
gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (xferData->to_amount_edit),
|
||||
@ -576,10 +551,7 @@ gboolean
|
||||
gnc_xfer_dialog_inc_exp_filter_func (Account *account,
|
||||
gpointer data)
|
||||
{
|
||||
AccountTreeFilterInfo* info;
|
||||
GNCAccountType type;
|
||||
|
||||
info = (AccountTreeFilterInfo*)data;
|
||||
auto info = static_cast<AccountTreeFilterInfo *> (data);
|
||||
|
||||
if (!info->show_hidden && xaccAccountIsHidden(account))
|
||||
{
|
||||
@ -591,7 +563,7 @@ gnc_xfer_dialog_inc_exp_filter_func (Account *account,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
type = xaccAccountGetType(account);
|
||||
auto type = xaccAccountGetType(account);
|
||||
return ((type != ACCT_TYPE_INCOME) && (type != ACCT_TYPE_EXPENSE));
|
||||
}
|
||||
|
||||
@ -599,18 +571,14 @@ static void
|
||||
gnc_xfer_dialog_fill_tree_view(XferDialog *xferData,
|
||||
XferDirection direction)
|
||||
{
|
||||
GtkTreeView *tree_view;
|
||||
const char *show_inc_exp_message = _("Show the income and expense accounts");
|
||||
GtkWidget *scroll_win;
|
||||
GtkWidget *button;
|
||||
GtkTreeSelection *selection;
|
||||
gboolean use_accounting_labels;
|
||||
AccountTreeFilterInfo *info;
|
||||
GtkBuilder *builder = g_object_get_data (G_OBJECT (xferData->dialog), "builder");
|
||||
GtkWidget *scroll_win;
|
||||
auto builder = static_cast<GtkBuilder *> (g_object_get_data (G_OBJECT (xferData->dialog), "builder"));
|
||||
|
||||
g_return_if_fail (xferData != NULL);
|
||||
use_accounting_labels = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
|
||||
GNC_PREF_ACCOUNTING_LABELS);
|
||||
auto use_accounting_labels = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
|
||||
GNC_PREF_ACCOUNTING_LABELS);
|
||||
|
||||
/* In "normal" mode (non accounting terms) the account where the
|
||||
* money comes from is displayed on the left side and the account
|
||||
@ -643,12 +611,13 @@ gnc_xfer_dialog_fill_tree_view(XferDialog *xferData,
|
||||
}
|
||||
|
||||
|
||||
AccountTreeFilterInfo *info;
|
||||
if (direction == XFER_DIALOG_TO)
|
||||
info = to_info;
|
||||
else
|
||||
info = from_info;
|
||||
|
||||
tree_view = GTK_TREE_VIEW(gnc_tree_view_account_new(FALSE));
|
||||
auto tree_view = GTK_TREE_VIEW(gnc_tree_view_account_new(FALSE));
|
||||
gtk_container_add(GTK_CONTAINER(scroll_win), GTK_WIDGET(tree_view));
|
||||
info->show_inc_exp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
|
||||
info->show_hidden = FALSE;
|
||||
@ -662,7 +631,7 @@ gnc_xfer_dialog_fill_tree_view(XferDialog *xferData,
|
||||
g_signal_connect (G_OBJECT (tree_view), "key-press-event",
|
||||
G_CALLBACK (gnc_xfer_dialog_key_press_cb), NULL);
|
||||
|
||||
selection = gtk_tree_view_get_selection (tree_view);
|
||||
auto selection = gtk_tree_view_get_selection (tree_view);
|
||||
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
|
||||
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
|
||||
@ -824,8 +793,9 @@ gnc_xfer_dialog_quickfill( XferDialog *xferData )
|
||||
static gboolean
|
||||
idle_select_region(gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
g_return_val_if_fail(xferData, FALSE);
|
||||
g_return_val_if_fail(data, FALSE);
|
||||
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
gtk_editable_select_region(GTK_EDITABLE(xferData->description_entry),
|
||||
xferData->desc_start_selection,
|
||||
@ -992,8 +962,9 @@ static gboolean
|
||||
gnc_xfer_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
|
||||
gpointer data)
|
||||
{
|
||||
XferDialog * xferData = data;
|
||||
g_return_val_if_fail (xferData != NULL, FALSE);
|
||||
g_return_val_if_fail (data, FALSE);
|
||||
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL);
|
||||
|
||||
@ -1052,19 +1023,18 @@ static gboolean
|
||||
gnc_xfer_price_update_cb(GtkWidget *widget, GdkEventFocus *event,
|
||||
gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
gnc_xfer_update_to_amount (xferData);
|
||||
xferData->price_type = PRICE_TYPE_TRN;
|
||||
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnc_xfer_date_changed_cb(GtkWidget *widget, gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
if (xferData)
|
||||
gnc_xfer_dialog_update_price (xferData);
|
||||
@ -1076,11 +1046,10 @@ static gboolean
|
||||
gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
|
||||
gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
gnc_numeric price_value;
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL);
|
||||
price_value = gnc_xfer_dialog_compute_price_value(xferData);
|
||||
auto price_value = gnc_xfer_dialog_compute_price_value (xferData);
|
||||
gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
|
||||
price_value);
|
||||
xferData->price_source = PRICE_SOURCE_XFER_DLG_VAL;
|
||||
@ -1675,14 +1644,9 @@ create_price(XferDialog *xferData, time64 time)
|
||||
void
|
||||
gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
{
|
||||
XferDialog *xferData = data;
|
||||
Account *to_account;
|
||||
Account *from_account;
|
||||
gnc_numeric amount, to_amount;
|
||||
time64 time;
|
||||
GDate date;
|
||||
g_return_if_fail (data);
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
g_return_if_fail (xferData != NULL);
|
||||
ENTER(" ");
|
||||
|
||||
if (response == GTK_RESPONSE_APPLY)
|
||||
@ -1695,7 +1659,7 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
* Remove date changed handler to prevent it from triggering
|
||||
* on a focus-out event while we're already destroying the widget */
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (xferData->date_entry),
|
||||
G_CALLBACK (gnc_xfer_date_changed_cb),
|
||||
(gpointer)gnc_xfer_date_changed_cb,
|
||||
xferData);
|
||||
|
||||
if (response != GTK_RESPONSE_OK)
|
||||
@ -1705,8 +1669,8 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
return;
|
||||
}
|
||||
|
||||
from_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
|
||||
to_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
|
||||
auto from_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
|
||||
auto to_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
|
||||
|
||||
if (xferData->exch_rate == NULL &&
|
||||
!check_accounts(xferData, from_account, to_account))
|
||||
@ -1719,7 +1683,7 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
return;
|
||||
}
|
||||
|
||||
amount = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
|
||||
auto amount = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
|
||||
|
||||
if (gnc_numeric_zero_p (amount))
|
||||
{
|
||||
@ -1728,10 +1692,13 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
LEAVE("invalid from amount");
|
||||
return;
|
||||
}
|
||||
|
||||
GDate date;
|
||||
g_date_clear (&date, 1);
|
||||
gnc_date_edit_get_gdate (GNC_DATE_EDIT (xferData->date_entry), &date);
|
||||
time = gdate_to_time64 (date);
|
||||
auto time = gdate_to_time64 (date);
|
||||
|
||||
auto to_amount = amount;
|
||||
if (!gnc_commodity_equiv(xferData->from_commodity, xferData->to_commodity))
|
||||
{
|
||||
if (!check_edit(xferData))
|
||||
@ -1739,22 +1706,18 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
to_amount = gnc_amount_edit_get_amount
|
||||
(GNC_AMOUNT_EDIT(xferData->to_amount_edit));
|
||||
}
|
||||
else
|
||||
to_amount = amount;
|
||||
|
||||
gnc_suspend_gui_refresh ();
|
||||
|
||||
if (xferData->exch_rate)
|
||||
{
|
||||
gnc_numeric price_value;
|
||||
|
||||
/* If we've got the price-button set, then make sure we update the
|
||||
* to-amount before we use it.
|
||||
*/
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(xferData->price_radio)))
|
||||
gnc_xfer_update_to_amount(xferData);
|
||||
|
||||
price_value = gnc_xfer_dialog_compute_price_value(xferData);
|
||||
auto price_value = gnc_xfer_dialog_compute_price_value(xferData);
|
||||
gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
|
||||
price_value);
|
||||
*(xferData->exch_rate) = gnc_numeric_abs(price_value);
|
||||
@ -1777,14 +1740,13 @@ gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
void
|
||||
gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data)
|
||||
{
|
||||
XferDialog * xferData = data;
|
||||
GtkWidget *entry;
|
||||
auto xferData = static_cast<XferDialog *> (data);
|
||||
|
||||
/* Notify transaction callback to unregister here */
|
||||
if (xferData->transaction_cb)
|
||||
xferData->transaction_cb(NULL, xferData->transaction_user_data);
|
||||
|
||||
entry = gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(xferData->amount_edit));
|
||||
auto entry = gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(xferData->amount_edit));
|
||||
g_signal_handlers_disconnect_matched (G_OBJECT (entry), G_SIGNAL_MATCH_DATA,
|
||||
0, 0, NULL, NULL, xferData);
|
||||
|
||||
@ -1819,44 +1781,26 @@ gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data)
|
||||
void
|
||||
gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
|
||||
{
|
||||
PriceReq pr;
|
||||
SCM quotes_func;
|
||||
SCM book_scm;
|
||||
SCM scm_window;
|
||||
|
||||
g_return_if_fail (xferData);
|
||||
|
||||
ENTER(" ");
|
||||
|
||||
quotes_func = scm_c_eval_string ("gnc:book-add-quotes");
|
||||
|
||||
if (!scm_is_procedure (quotes_func))
|
||||
try
|
||||
{
|
||||
LEAVE("quote retrieval failed");
|
||||
return;
|
||||
GncQuotes quotes;
|
||||
gnc_set_busy_cursor(nullptr, TRUE);
|
||||
quotes.fetch(xferData->book);
|
||||
gnc_unset_busy_cursor(nullptr);
|
||||
}
|
||||
|
||||
book_scm = gnc_book_to_scm (xferData->book);
|
||||
if (scm_is_true (scm_not (book_scm)))
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
LEAVE("no book");
|
||||
return;
|
||||
gnc_unset_busy_cursor(nullptr);
|
||||
PERR("Price retrieval failed: %s", err.what());
|
||||
gnc_error_dialog(GTK_WINDOW(xferData->dialog), _("Price retrieval failed: %s"), err.what());
|
||||
}
|
||||
|
||||
scm_window = SWIG_NewPointerObj(xferData->dialog,
|
||||
SWIG_TypeQuery("_p_GtkWindow"), 0);
|
||||
|
||||
if (scm_is_true (scm_not (book_scm)))
|
||||
{
|
||||
LEAVE("no scm window");
|
||||
return;
|
||||
}
|
||||
|
||||
gnc_set_busy_cursor (NULL, TRUE);
|
||||
scm_call_2 (quotes_func, scm_window, book_scm);
|
||||
gnc_unset_busy_cursor (NULL);
|
||||
|
||||
/*the results should be in the price db now, but don't crash if not. */
|
||||
PriceReq pr;
|
||||
price_request_from_xferData(&pr, xferData);
|
||||
if (lookup_price(&pr, LATEST))
|
||||
{
|
||||
@ -2070,11 +2014,10 @@ gnc_xfer_dialog_create(GtkWidget *parent, XferDialog *xferData)
|
||||
static void
|
||||
close_handler (gpointer user_data)
|
||||
{
|
||||
XferDialog *xferData = user_data;
|
||||
GtkWidget *dialog;
|
||||
auto xferData = static_cast<XferDialog *> (user_data);
|
||||
|
||||
ENTER(" ");
|
||||
dialog = GTK_WIDGET (xferData->dialog);
|
||||
auto dialog = GTK_WIDGET (xferData->dialog);
|
||||
|
||||
gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW (dialog));
|
||||
gtk_widget_hide (dialog);
|
||||
@ -2238,10 +2181,10 @@ void gnc_xfer_dialog_add_user_specified_button( XferDialog *xferData,
|
||||
{
|
||||
if ( xferData && label && callback )
|
||||
{
|
||||
GtkBuilder *builder = g_object_get_data (G_OBJECT (xferData->dialog), "builder");
|
||||
GtkWidget *button = gtk_button_new_with_label( label );
|
||||
GtkWidget *box = GTK_WIDGET(gtk_builder_get_object (builder,
|
||||
"transfermain-vbox" ));
|
||||
auto builder = static_cast<GtkBuilder *> (g_object_get_data (G_OBJECT (xferData->dialog), "builder"));
|
||||
auto button = gtk_button_new_with_label( label );
|
||||
auto box = GTK_WIDGET (gtk_builder_get_object (builder,
|
||||
"transfermain-vbox" ));
|
||||
gtk_box_pack_end( GTK_BOX(box), button, FALSE, FALSE, 0 );
|
||||
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (callback), user_data);
|
||||
gtk_widget_show( button );
|
||||
@ -2292,7 +2235,7 @@ gboolean gnc_xfer_dialog_run_until_done( XferDialog *xferData )
|
||||
* that's bad mojo whole gtk_dialog_run is still in control.
|
||||
*/
|
||||
count = g_signal_handlers_disconnect_by_func(dialog,
|
||||
gnc_xfer_dialog_response_cb,
|
||||
(gpointer) gnc_xfer_dialog_response_cb,
|
||||
xferData);
|
||||
g_assert(count == 1);
|
||||
|
@ -90,7 +90,7 @@ set (gnc_gnome_SOURCES
|
||||
dialog-order.c
|
||||
dialog-payment.c
|
||||
dialog-price-editor.c
|
||||
dialog-price-edit-db.c
|
||||
dialog-price-edit-db.cpp
|
||||
dialog-print-check.c
|
||||
dialog-progress.c
|
||||
dialog-report-column-view.cpp
|
||||
@ -133,6 +133,7 @@ target_link_libraries(gnc-gnome
|
||||
gnc-register-gnome
|
||||
gnc-register-core
|
||||
gnc-gnome-utils
|
||||
gnc-app-utils
|
||||
gnc-engine
|
||||
gnc-expressions
|
||||
gnc-html
|
||||
@ -148,6 +149,7 @@ target_compile_options(gnc-gnome PRIVATE -Wno-deprecated-declarations)
|
||||
target_include_directories(gnc-gnome
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/libgnucash/app-utils
|
||||
${CMAKE_SOURCE_DIR}/libgnucash/app-utils/calculation
|
||||
${CMAKE_SOURCE_DIR}/gnucash/html
|
||||
${CMAKE_BINARY_DIR}/gnucash/gnome-utils # for gnc-warnings.h
|
||||
|
@ -27,9 +27,10 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <libguile.h>
|
||||
#include <time.h>
|
||||
#include <gnc-quotes.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include "dialog-utils.h"
|
||||
#include "gnc-accounting-period.h"
|
||||
#include "gnc-amount-edit.h"
|
||||
@ -46,10 +47,8 @@
|
||||
#include "gnc-ui.h"
|
||||
#include "gnc-ui-util.h"
|
||||
#include "gnc-warnings.h"
|
||||
#include "swig-runtime.h"
|
||||
#include "guile-mappings.h"
|
||||
#include "gnc-engine-guile.h"
|
||||
#include <gnc-glib-utils.h>
|
||||
}
|
||||
|
||||
|
||||
#define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db"
|
||||
@ -60,6 +59,7 @@
|
||||
static QofLogModule log_module = GNC_MOD_GUI;
|
||||
|
||||
|
||||
extern "C" {
|
||||
void gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data);
|
||||
void gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data);
|
||||
void gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data);
|
||||
@ -71,9 +71,10 @@ void gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data);
|
||||
static gboolean gnc_prices_dialog_key_press_cb (GtkWidget *widget,
|
||||
GdkEventKey *event,
|
||||
gpointer data);
|
||||
}
|
||||
|
||||
|
||||
typedef struct
|
||||
struct PricesDialog
|
||||
{
|
||||
GtkWidget * window;
|
||||
QofSession *session;
|
||||
@ -88,14 +89,14 @@ typedef struct
|
||||
|
||||
GtkWidget *remove_dialog;
|
||||
GtkTreeView *remove_view;
|
||||
gint remove_source;
|
||||
} PricesDialog;
|
||||
int remove_source;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
gnc_unregister_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
|
||||
@ -116,7 +117,7 @@ gnc_prices_dialog_delete_event_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
// this cb allows the window size to be saved on closing with the X
|
||||
gnc_save_window_size (GNC_PREFS_GROUP,
|
||||
GTK_WINDOW(pdb_dialog->window));
|
||||
@ -127,7 +128,7 @@ gnc_prices_dialog_delete_event_cb (GtkWidget *widget,
|
||||
void
|
||||
gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
|
||||
@ -138,7 +139,7 @@ gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data)
|
||||
void
|
||||
gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog{static_cast<PricesDialog*>(data)};
|
||||
|
||||
gnc_gnome_help (GTK_WINDOW (pdb_dialog->window), HF_HELP, HL_PRICE_DB);
|
||||
}
|
||||
@ -147,11 +148,10 @@ gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data)
|
||||
void
|
||||
gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
GList *price_list;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree);
|
||||
auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
|
||||
if (!price_list)
|
||||
{
|
||||
LEAVE("no price selected");
|
||||
@ -164,9 +164,10 @@ gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data)
|
||||
return;
|
||||
}
|
||||
|
||||
auto price = static_cast<GNCPrice *> (price_list->data);
|
||||
gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
|
||||
price_list->data, GNC_PRICE_EDIT);
|
||||
g_list_free(price_list);
|
||||
price, GNC_PRICE_EDIT);
|
||||
g_list_free (price_list);
|
||||
LEAVE(" ");
|
||||
}
|
||||
|
||||
@ -181,20 +182,18 @@ remove_helper(GNCPrice *price, GNCPriceDB *pdb)
|
||||
void
|
||||
gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
GList *price_list;
|
||||
gint length, response;
|
||||
GtkWidget *dialog;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree);
|
||||
auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
|
||||
if (!price_list)
|
||||
{
|
||||
LEAVE("no price selected");
|
||||
return;
|
||||
}
|
||||
|
||||
length = g_list_length(price_list);
|
||||
gint response;
|
||||
auto length = g_list_length(price_list);
|
||||
if (length > 0)
|
||||
{
|
||||
gchar *message;
|
||||
@ -205,11 +204,11 @@ gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
|
||||
"Are you sure you want to delete the %d selected prices?",
|
||||
length),
|
||||
length);
|
||||
dialog = gtk_message_dialog_new(GTK_WINDOW(pdb_dialog->window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_QUESTION,
|
||||
GTK_BUTTONS_NONE,
|
||||
"%s", _("Delete prices?"));
|
||||
auto dialog = gtk_message_dialog_new (GTK_WINDOW(pdb_dialog->window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_QUESTION,
|
||||
GTK_BUTTONS_NONE,
|
||||
"%s", _("Delete prices?"));
|
||||
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
|
||||
"%s", message);
|
||||
g_free(message);
|
||||
@ -242,46 +241,38 @@ enum GncPriceColumn {PRICED_FULL_NAME, PRICED_COMM, PRICED_DATE, PRICED_COUNT};
|
||||
static time64
|
||||
gnc_prices_dialog_load_view (GtkTreeView *view, GNCPriceDB *pdb)
|
||||
{
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (view);
|
||||
const gnc_commodity_table *commodity_table = gnc_get_current_commodities ();
|
||||
GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
|
||||
gnc_commodity *tmp_commodity = NULL;
|
||||
char *tmp_namespace = NULL;
|
||||
GList *commodity_list = NULL;
|
||||
GtkTreeIter iter;
|
||||
auto oldest = gnc_time (nullptr);
|
||||
auto model = gtk_tree_view_get_model (view);
|
||||
const auto commodity_table = gnc_get_current_commodities ();
|
||||
auto namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
|
||||
|
||||
time64 oldest = gnc_time (NULL);
|
||||
|
||||
namespace_list = g_list_first (namespace_list);
|
||||
while (namespace_list != NULL)
|
||||
while (namespace_list)
|
||||
{
|
||||
tmp_namespace = namespace_list->data;
|
||||
auto tmp_namespace = static_cast<char *> (namespace_list->data);
|
||||
DEBUG("Looking at namespace %s", tmp_namespace);
|
||||
commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
|
||||
commodity_list = g_list_first (commodity_list);
|
||||
while (commodity_list != NULL)
|
||||
auto commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
|
||||
while (commodity_list)
|
||||
{
|
||||
gint num = 0;
|
||||
tmp_commodity = commodity_list->data;
|
||||
num = gnc_pricedb_num_prices (pdb, tmp_commodity);
|
||||
auto tmp_commodity = static_cast<gnc_commodity *> (commodity_list->data);
|
||||
auto num = gnc_pricedb_num_prices (pdb, tmp_commodity);
|
||||
DEBUG("Looking at commodity %s, Number of prices %d", gnc_commodity_get_fullname (tmp_commodity), num);
|
||||
|
||||
if (num > 0)
|
||||
{
|
||||
PriceList *list = gnc_pricedb_get_prices (pdb, tmp_commodity, NULL);
|
||||
GList *node = g_list_last (list);
|
||||
GNCPrice *price = (GNCPrice*)node->data;
|
||||
time64 price_time = gnc_price_get_time64 (price);
|
||||
const gchar *name_str = gnc_commodity_get_printname (tmp_commodity);
|
||||
gchar *date_str, *num_str;
|
||||
auto list = gnc_pricedb_get_prices (pdb, tmp_commodity, NULL);
|
||||
auto node = g_list_last (list);
|
||||
auto price = static_cast<GNCPrice*> (node->data);
|
||||
auto price_time = gnc_price_get_time64 (price);
|
||||
auto name_str = gnc_commodity_get_printname (tmp_commodity);
|
||||
|
||||
if (oldest > price_time)
|
||||
oldest = price_time;
|
||||
|
||||
date_str = qof_print_date (price_time);
|
||||
num_str = g_strdup_printf ("%d", num);
|
||||
auto date_str = qof_print_date (price_time);
|
||||
auto num_str = g_strdup_printf ("%d", num);
|
||||
|
||||
GtkTreeIter iter;
|
||||
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
|
||||
|
||||
gtk_list_store_set (GTK_LIST_STORE(model), &iter, PRICED_FULL_NAME, name_str,
|
||||
PRICED_COMM, tmp_commodity, PRICED_DATE, date_str, PRICED_COUNT, num_str, -1);
|
||||
|
||||
@ -291,9 +282,9 @@ gnc_prices_dialog_load_view (GtkTreeView *view, GNCPriceDB *pdb)
|
||||
}
|
||||
commodity_list = g_list_next (commodity_list);
|
||||
}
|
||||
g_list_free (commodity_list);
|
||||
namespace_list = g_list_next (namespace_list);
|
||||
}
|
||||
g_list_free (commodity_list);
|
||||
g_list_free (namespace_list);
|
||||
|
||||
return oldest;
|
||||
@ -302,25 +293,24 @@ gnc_prices_dialog_load_view (GtkTreeView *view, GNCPriceDB *pdb)
|
||||
static GList *
|
||||
gnc_prices_dialog_get_commodities (GtkTreeView *view)
|
||||
{
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
|
||||
GList *list = gtk_tree_selection_get_selected_rows (selection, &model);
|
||||
GList *row;
|
||||
GList *comm_list = NULL;
|
||||
GtkTreeIter iter;
|
||||
gnc_commodity *comm;
|
||||
auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
|
||||
auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
|
||||
auto list = gtk_tree_selection_get_selected_rows (selection, &model);
|
||||
GList *comm_list = nullptr;
|
||||
|
||||
// Walk the list
|
||||
for (row = g_list_first (list); row; row = g_list_next (row))
|
||||
for (auto row = g_list_first (list); row; row = g_list_next (row))
|
||||
{
|
||||
if (gtk_tree_model_get_iter (model, &iter, row->data))
|
||||
auto path = static_cast<GtkTreePath *> (row->data);
|
||||
GtkTreeIter iter;
|
||||
if (gtk_tree_model_get_iter (model, &iter, path))
|
||||
{
|
||||
gnc_commodity *comm;
|
||||
gtk_tree_model_get (model, &iter, PRICED_COMM, &comm, -1);
|
||||
comm_list = g_list_prepend (comm_list, comm);
|
||||
}
|
||||
}
|
||||
g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
|
||||
g_list_free (list);
|
||||
g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
|
||||
|
||||
return g_list_reverse (comm_list);
|
||||
}
|
||||
@ -328,7 +318,7 @@ gnc_prices_dialog_get_commodities (GtkTreeView *view)
|
||||
static void
|
||||
change_source_flag (PriceRemoveSourceFlags source, gboolean set, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
GtkWidget *w = gtk_dialog_get_widget_for_response (GTK_DIALOG(pdb_dialog->remove_dialog), GTK_RESPONSE_OK);
|
||||
gboolean enable_button;
|
||||
|
||||
@ -347,7 +337,7 @@ change_source_flag (PriceRemoveSourceFlags source, gboolean set, gpointer data)
|
||||
static void
|
||||
check_event_fq_cb (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
|
||||
|
||||
change_source_flag (PRICE_REMOVE_SOURCE_FQ, active, pdb_dialog);
|
||||
@ -356,7 +346,7 @@ check_event_fq_cb (GtkWidget *widget, gpointer data)
|
||||
static void
|
||||
check_event_user_cb (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
|
||||
|
||||
change_source_flag (PRICE_REMOVE_SOURCE_USER, active, pdb_dialog);
|
||||
@ -365,7 +355,7 @@ check_event_user_cb (GtkWidget *widget, gpointer data)
|
||||
static void
|
||||
check_event_app_cb (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
|
||||
|
||||
change_source_flag (PRICE_REMOVE_SOURCE_APP, active, pdb_dialog);
|
||||
@ -374,14 +364,13 @@ check_event_app_cb (GtkWidget *widget, gpointer data)
|
||||
static void
|
||||
selection_changed_cb (GtkTreeSelection *selection, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->remove_view));
|
||||
GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->remove_view));
|
||||
auto rows = gtk_tree_selection_get_selected_rows (selection, &model);
|
||||
gboolean have_rows = (gnc_list_length_cmp (rows, 0));
|
||||
|
||||
change_source_flag (PRICE_REMOVE_SOURCE_COMM, have_rows, pdb_dialog);
|
||||
g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
|
||||
g_list_free (rows);
|
||||
g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
|
||||
}
|
||||
|
||||
static GDate
|
||||
@ -401,45 +390,36 @@ get_fiscal_end_date (void)
|
||||
void
|
||||
gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
GtkBuilder *builder;
|
||||
GtkTreeModel *model;
|
||||
GtkWidget *date, *label, *box;
|
||||
GtkWidget *button;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeViewColumn *tree_column;
|
||||
GtkCellRenderer *cr;
|
||||
time64 first;
|
||||
gint result;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
builder = gtk_builder_new();
|
||||
auto builder = gtk_builder_new();
|
||||
gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore4");
|
||||
gnc_builder_add_from_file (builder, "dialog-price.glade", "deletion_date_dialog");
|
||||
|
||||
pdb_dialog->remove_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "deletion_date_dialog"));
|
||||
|
||||
box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
|
||||
date = gnc_date_edit_new (time (NULL), FALSE, FALSE);
|
||||
auto box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
|
||||
auto date = gnc_date_edit_new (time (NULL), FALSE, FALSE);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (box), date, FALSE, FALSE, 0);
|
||||
gtk_widget_show (date);
|
||||
gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(date)->date_entry), TRUE);
|
||||
label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
|
||||
auto label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
|
||||
gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date), label);
|
||||
|
||||
// Setup the commodity view
|
||||
pdb_dialog->remove_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "commodty_treeview"));
|
||||
selection = gtk_tree_view_get_selection (pdb_dialog->remove_view);
|
||||
auto selection = gtk_tree_view_get_selection (pdb_dialog->remove_view);
|
||||
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
|
||||
|
||||
// Add Entries column this way as align does not seem to work from builder
|
||||
tree_column = gtk_tree_view_column_new();
|
||||
auto tree_column = gtk_tree_view_column_new();
|
||||
gtk_tree_view_column_set_title (tree_column, _("Entries"));
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW(pdb_dialog->remove_view), tree_column);
|
||||
gtk_tree_view_column_set_alignment (tree_column, 0.5);
|
||||
gtk_tree_view_column_set_expand (tree_column, TRUE);
|
||||
cr = gtk_cell_renderer_text_new();
|
||||
auto cr = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_column_pack_start (tree_column, cr, TRUE);
|
||||
// set 'xalign' property of the cell renderer
|
||||
gtk_tree_view_column_set_attributes (tree_column, cr, "text", PRICED_COUNT, NULL);
|
||||
@ -454,19 +434,19 @@ gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
|
||||
gtk_window_set_transient_for (GTK_WINDOW (pdb_dialog->remove_dialog), GTK_WINDOW (pdb_dialog->window));
|
||||
|
||||
pdb_dialog->remove_source = 9; // FQ and Commodities highlighted
|
||||
button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_fq"));
|
||||
pdb_dialog->remove_source = PRICE_REMOVE_SOURCE_FQ + PRICE_REMOVE_SOURCE_COMM; // FQ and Commodities highlighted
|
||||
auto button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_fq"));
|
||||
g_signal_connect (button, "toggled", G_CALLBACK (check_event_fq_cb), pdb_dialog);
|
||||
button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_user"));
|
||||
g_signal_connect (button, "toggled", G_CALLBACK (check_event_user_cb), pdb_dialog);
|
||||
button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_app"));
|
||||
g_signal_connect (button, "toggled", G_CALLBACK (check_event_app_cb), pdb_dialog);
|
||||
|
||||
result = gtk_dialog_run (GTK_DIALOG (pdb_dialog->remove_dialog));
|
||||
auto result = gtk_dialog_run (GTK_DIALOG (pdb_dialog->remove_dialog));
|
||||
if (result == GTK_RESPONSE_OK)
|
||||
{
|
||||
const char *fmt = _("Are you sure you want to delete these prices?");
|
||||
GList *comm_list = gnc_prices_dialog_get_commodities (pdb_dialog->remove_view);
|
||||
auto comm_list = gnc_prices_dialog_get_commodities (pdb_dialog->remove_view);
|
||||
|
||||
// Are you sure you want to delete the entries and we have commodities
|
||||
if ((g_list_length (comm_list) != 0) && (gnc_verify_dialog (GTK_WINDOW (pdb_dialog->remove_dialog), FALSE, fmt, NULL)))
|
||||
@ -477,7 +457,7 @@ gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
PriceRemoveKeepOptions keep = PRICE_REMOVE_KEEP_NONE;
|
||||
|
||||
// disconnect the model to the price treeview
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
|
||||
auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
|
||||
g_object_ref (G_OBJECT(model));
|
||||
gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), NULL);
|
||||
|
||||
@ -502,19 +482,18 @@ gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
|
||||
if (keep != PRICE_REMOVE_KEEP_SCALED)
|
||||
gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
|
||||
&fiscal_end_date,
|
||||
last, pdb_dialog->remove_source,
|
||||
&fiscal_end_date, last,
|
||||
static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
|
||||
keep);
|
||||
else
|
||||
{
|
||||
time64 tmp;
|
||||
GDate tmp_date = time64_to_gdate (last);
|
||||
auto tmp_date = time64_to_gdate (last);
|
||||
g_date_subtract_months (&tmp_date, 6);
|
||||
tmp = gdate_to_time64 (tmp_date);
|
||||
auto tmp = gdate_to_time64 (tmp_date);
|
||||
|
||||
gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
|
||||
&fiscal_end_date, tmp,
|
||||
pdb_dialog->remove_source,
|
||||
static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
|
||||
PRICE_REMOVE_KEEP_LAST_WEEKLY);
|
||||
|
||||
g_date_subtract_months (&tmp_date, 6);
|
||||
@ -522,7 +501,7 @@ gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
|
||||
gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
|
||||
&fiscal_end_date, tmp,
|
||||
pdb_dialog->remove_source,
|
||||
static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
|
||||
PRICE_REMOVE_KEEP_LAST_MONTHLY);
|
||||
}
|
||||
// reconnect the model to the price treeview
|
||||
@ -541,19 +520,17 @@ gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
void
|
||||
gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
GNCPrice *price = NULL;
|
||||
GList *price_list;
|
||||
GList *comm_list;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
GNCPrice *price = nullptr;
|
||||
gboolean unref_price = FALSE;
|
||||
|
||||
ENTER(" ");
|
||||
price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
|
||||
comm_list = gnc_tree_view_price_get_selected_commodities (pdb_dialog->price_tree);
|
||||
auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
|
||||
auto comm_list = gnc_tree_view_price_get_selected_commodities (pdb_dialog->price_tree);
|
||||
|
||||
if (price_list) // selected row is on a price
|
||||
{
|
||||
price = price_list->data;
|
||||
price = static_cast<GNCPrice *> (price_list->data);
|
||||
g_list_free (price_list);
|
||||
}
|
||||
else if (comm_list) // selection contains price parent rows
|
||||
@ -561,7 +538,8 @@ gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
|
||||
if (!gnc_list_length_cmp (comm_list, 1)) // make sure it is only one parent
|
||||
{
|
||||
price = gnc_price_create (pdb_dialog->book);
|
||||
gnc_price_set_commodity (price, comm_list->data);
|
||||
auto comm = static_cast<gnc_commodity *> (comm_list->data);
|
||||
gnc_price_set_commodity (price, comm);
|
||||
unref_price = TRUE;
|
||||
}
|
||||
g_list_free (comm_list);
|
||||
@ -578,33 +556,25 @@ gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
|
||||
void
|
||||
gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
SCM quotes_func;
|
||||
SCM book_scm;
|
||||
SCM scm_window;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
quotes_func = scm_c_eval_string ("gnc:book-add-quotes");
|
||||
if (!scm_is_procedure (quotes_func))
|
||||
{
|
||||
LEAVE(" no procedure");
|
||||
return;
|
||||
try {
|
||||
GncQuotes quotes;
|
||||
gnc_set_busy_cursor (NULL, TRUE);
|
||||
quotes.fetch (pdb_dialog->book);
|
||||
gnc_unset_busy_cursor (NULL);
|
||||
if (quotes.had_failures())
|
||||
gnc_warning_dialog(GTK_WINDOW(pdb_dialog->window), "%s",
|
||||
quotes.report_failures().c_str());
|
||||
}
|
||||
|
||||
book_scm = gnc_book_to_scm (pdb_dialog->book);
|
||||
if (scm_is_true (scm_not (book_scm)))
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
LEAVE("no book");
|
||||
return;
|
||||
gnc_unset_busy_cursor(nullptr);
|
||||
PERR("Price retrieval failed: %s", err.what());
|
||||
gnc_error_dialog(GTK_WINDOW(pdb_dialog), _("Price retrieval failed: %s"), err.what());
|
||||
}
|
||||
|
||||
scm_window = SWIG_NewPointerObj(pdb_dialog->window,
|
||||
SWIG_TypeQuery("_p_GtkWindow"), 0);
|
||||
|
||||
gnc_set_busy_cursor (NULL, TRUE);
|
||||
scm_call_2 (quotes_func, scm_window, book_scm);
|
||||
gnc_unset_busy_cursor (NULL);
|
||||
|
||||
/* Without this, the summary bar on the accounts tab
|
||||
* won't reflect the new prices (bug #522095). */
|
||||
gnc_gui_refresh_all ();
|
||||
@ -617,26 +587,21 @@ static void
|
||||
gnc_prices_dialog_selection_changed (GtkTreeSelection *treeselection,
|
||||
gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
GtkTreeModel *model;
|
||||
GList *price_list;
|
||||
GList *rows;
|
||||
gint length;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
ENTER(" ");
|
||||
price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
|
||||
length = g_list_length (price_list);
|
||||
auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
|
||||
auto length = g_list_length (price_list);
|
||||
g_list_free (price_list);
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
|
||||
rows = gtk_tree_selection_get_selected_rows (treeselection, &model);
|
||||
auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
|
||||
auto rows = gtk_tree_selection_get_selected_rows (treeselection, &model);
|
||||
|
||||
// if selected rows greater than length, parents must of been selected also
|
||||
if (g_list_length (rows) > length)
|
||||
length = 0;
|
||||
|
||||
g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
|
||||
g_list_free (rows);
|
||||
g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
|
||||
|
||||
gtk_widget_set_sensitive (pdb_dialog->edit_button,
|
||||
length == 1);
|
||||
@ -652,29 +617,23 @@ static gboolean
|
||||
gnc_price_dialog_filter_ns_func (gnc_commodity_namespace *name_space,
|
||||
gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
const gchar *name;
|
||||
static GList *cm_list;
|
||||
GList *item;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
/* Never show the template list */
|
||||
name = gnc_commodity_namespace_get_name (name_space);
|
||||
auto name = gnc_commodity_namespace_get_name (name_space);
|
||||
if (g_strcmp0 (name, GNC_COMMODITY_NS_TEMPLATE) == 0)
|
||||
return FALSE;
|
||||
|
||||
/* See if this namespace has commodities */
|
||||
cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
|
||||
for (item = cm_list; item; item = g_list_next(item))
|
||||
auto cm_list = gnc_commodity_namespace_get_commodity_list (name_space);
|
||||
for (auto item = cm_list; item; item = g_list_next (item))
|
||||
{
|
||||
|
||||
/* For each commodity, see if there are prices */
|
||||
if (gnc_pricedb_has_prices(pdb_dialog->price_db, item->data, NULL))
|
||||
{
|
||||
auto comm = static_cast<gnc_commodity *> (item->data);
|
||||
if (gnc_pricedb_has_prices (pdb_dialog->price_db, comm, nullptr))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Namespace %s not visible\n", name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -683,7 +642,7 @@ static gboolean
|
||||
gnc_price_dialog_filter_cm_func (gnc_commodity *commodity,
|
||||
gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
/* Show any commodity that has prices */
|
||||
return gnc_pricedb_has_prices(pdb_dialog->price_db, commodity, NULL);
|
||||
@ -808,11 +767,10 @@ gnc_prices_dialog_create (GtkWidget * parent, PricesDialog *pdb_dialog)
|
||||
static void
|
||||
close_handler (gpointer user_data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = user_data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (user_data);
|
||||
|
||||
ENTER(" ");
|
||||
gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window));
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (pdb_dialog->window));
|
||||
LEAVE(" ");
|
||||
}
|
||||
@ -830,7 +788,7 @@ static gboolean
|
||||
show_handler (const char *klass, gint component_id,
|
||||
gpointer user_data, gpointer iter_data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = user_data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (user_data);
|
||||
|
||||
ENTER(" ");
|
||||
if (!pdb_dialog)
|
||||
@ -849,7 +807,7 @@ gboolean
|
||||
gnc_prices_dialog_key_press_cb (GtkWidget *widget, GdkEventKey *event,
|
||||
gpointer data)
|
||||
{
|
||||
PricesDialog *pdb_dialog = data;
|
||||
auto pdb_dialog = static_cast<PricesDialog *> (data);
|
||||
|
||||
if (event->keyval == GDK_KEY_Escape)
|
||||
{
|
@ -42,6 +42,7 @@ extern "C" {
|
||||
#include <boost/nowide/args.hpp>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <gnc-quotes.hpp>
|
||||
|
||||
namespace bl = boost::locale;
|
||||
|
||||
@ -59,8 +60,9 @@ namespace Gnucash {
|
||||
private:
|
||||
void configure_program_options (void);
|
||||
|
||||
boost::optional <std::string> m_quotes_cmd;
|
||||
std::vector<std::string> m_quotes_cmd;
|
||||
boost::optional <std::string> m_namespace;
|
||||
bool m_verbose = false;
|
||||
|
||||
boost::optional <std::string> m_report_cmd;
|
||||
boost::optional <std::string> m_report_name;
|
||||
@ -93,11 +95,17 @@ Gnucash::GnucashCli::configure_program_options (void)
|
||||
{
|
||||
bpo::options_description quotes_options(_("Price Quotes Retrieval Options"));
|
||||
quotes_options.add_options()
|
||||
("quotes,Q", bpo::value (&m_quotes_cmd),
|
||||
_("Execute price quote related commands. Currently only one command is supported.\n\n"
|
||||
" get: \tFetch current quotes for all foreign currencies and stocks in the given GnuCash datafile.\n"))
|
||||
("quotes,Q", bpo::value<std::vector<std::string>> (&m_quotes_cmd)->multitoken(),
|
||||
_("Execute price quote related commands. The following commands are supported.\n\n"
|
||||
" info: \tShow Finance::Quote version and exposed quote sources.\n"
|
||||
" get: \tFetch current quotes for all foreign currencies and stocks in the given GnuCash datafile.\n"
|
||||
" dump: \tFetch current quotes for specified currencies or stocks from a specified namespace and print the results to the console.\n"
|
||||
" \tThis must be followed with a source and one or more symbols, unless the source is \"currency\" in which case it must be followed with two or more symbols, the first of which is the currency in which exchange rates for the rest will be quoted.\n"))
|
||||
("namespace", bpo::value (&m_namespace),
|
||||
_("Regular expression determining which namespace commodities will be retrieved for"));
|
||||
_("Regular expression determining which namespace commodities will be retrieved for when using the get command"))
|
||||
("verbose,V", bpo::bool_switch (&m_verbose),
|
||||
_("When using the dump command list all of the parameters Finance::Quote returns for the symbol instead of the ones that Gnucash requires."));
|
||||
|
||||
m_opt_desc_display->add (quotes_options);
|
||||
m_opt_desc_all.add (quotes_options);
|
||||
|
||||
@ -125,23 +133,44 @@ Gnucash::GnucashCli::start ([[maybe_unused]] int argc, [[maybe_unused]] char **a
|
||||
{
|
||||
Gnucash::CoreApp::start();
|
||||
|
||||
if (m_quotes_cmd)
|
||||
if (!m_quotes_cmd.empty())
|
||||
{
|
||||
if (*m_quotes_cmd != "get")
|
||||
if (m_quotes_cmd.front() == "info")
|
||||
{
|
||||
std::cerr << bl::format (bl::translate("Unknown quotes command '{1}'")) % *m_quotes_cmd << "\n\n"
|
||||
<< *m_opt_desc_display.get();
|
||||
return 1;
|
||||
return Gnucash::check_finance_quote ();
|
||||
}
|
||||
|
||||
if (!m_file_to_load || m_file_to_load->empty())
|
||||
else if (m_quotes_cmd.front() == "get")
|
||||
{
|
||||
std::cerr << bl::translate("Missing data file parameter") << "\n\n"
|
||||
<< *m_opt_desc_display.get();
|
||||
return 1;
|
||||
|
||||
if (!m_file_to_load || m_file_to_load->empty())
|
||||
{
|
||||
std::cerr << bl::translate("Missing data file parameter") << "\n\n"
|
||||
<< *m_opt_desc_display.get() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return Gnucash::add_quotes (m_file_to_load);
|
||||
}
|
||||
else if (m_quotes_cmd.front() == "dump")
|
||||
{
|
||||
if (m_quotes_cmd.size() < 3 ||
|
||||
(m_quotes_cmd[1] == "currency" &&
|
||||
m_quotes_cmd.size() < 4))
|
||||
{
|
||||
std::cerr << bl::translate("Not enough information for quotes dump") << std::endl;
|
||||
return 1;
|
||||
}
|
||||
auto source = m_quotes_cmd[1];
|
||||
m_quotes_cmd.erase(m_quotes_cmd.begin(), m_quotes_cmd.begin() + 2);
|
||||
return Gnucash::report_quotes(source.c_str(), m_quotes_cmd,
|
||||
m_verbose);
|
||||
}
|
||||
else
|
||||
return Gnucash::add_quotes (m_file_to_load);
|
||||
{
|
||||
std::cerr << bl::format (bl::translate("Unknown quotes command '{1}'")) % m_quotes_cmd.front() << "\n\n"
|
||||
<< *m_opt_desc_display.get() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_report_cmd)
|
||||
@ -151,7 +180,7 @@ Gnucash::GnucashCli::start ([[maybe_unused]] int argc, [[maybe_unused]] char **a
|
||||
if (!m_file_to_load || m_file_to_load->empty())
|
||||
{
|
||||
std::cerr << bl::translate("Missing data file parameter") << "\n\n"
|
||||
<< *m_opt_desc_display.get();
|
||||
<< *m_opt_desc_display.get() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
@ -175,7 +204,7 @@ Gnucash::GnucashCli::start ([[maybe_unused]] int argc, [[maybe_unused]] char **a
|
||||
if (!m_report_name || m_report_name->empty())
|
||||
{
|
||||
std::cerr << bl::translate("Missing --name parameter") << "\n\n"
|
||||
<< *m_opt_desc_display.get();
|
||||
<< *m_opt_desc_display.get() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
@ -183,13 +212,13 @@ Gnucash::GnucashCli::start ([[maybe_unused]] int argc, [[maybe_unused]] char **a
|
||||
else
|
||||
{
|
||||
std::cerr << bl::format (bl::translate("Unknown report command '{1}'")) % *m_report_cmd << "\n\n"
|
||||
<< *m_opt_desc_display.get();
|
||||
<< *m_opt_desc_display.get() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << bl::translate("Missing command or option") << "\n\n"
|
||||
<< *m_opt_desc_display.get();
|
||||
<< *m_opt_desc_display.get() << std::endl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ extern "C" {
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <gnc-report.h>
|
||||
#include <gnc-quotes.hpp>
|
||||
|
||||
namespace bl = boost::locale;
|
||||
|
||||
@ -53,8 +54,8 @@ static std::string empty_string{};
|
||||
/* This static indicates the debugging module that this .o belongs to. */
|
||||
static QofLogModule log_module = GNC_MOD_GUI;
|
||||
|
||||
static void
|
||||
scm_cleanup_and_exit_with_failure (QofSession *session)
|
||||
static int
|
||||
cleanup_and_exit_with_failure (QofSession *session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
@ -70,60 +71,15 @@ scm_cleanup_and_exit_with_failure (QofSession *session)
|
||||
qof_session_destroy (session);
|
||||
}
|
||||
qof_event_resume();
|
||||
gnc_shutdown (1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* scm_boot_guile doesn't expect to return, so call shutdown ourselves here */
|
||||
static void
|
||||
scm_add_quotes(void *data, [[maybe_unused]] int argc, [[maybe_unused]] char **argv)
|
||||
scm_cleanup_and_exit_with_failure (QofSession *session)
|
||||
{
|
||||
auto add_quotes_file = static_cast<const std::string*>(data);
|
||||
|
||||
scm_c_eval_string("(debug-set! stack 200000)");
|
||||
|
||||
auto mod = scm_c_resolve_module("gnucash price-quotes");
|
||||
scm_set_current_module(mod);
|
||||
|
||||
gnc_prefs_init ();
|
||||
qof_event_suspend();
|
||||
scm_c_eval_string("(gnc:price-quotes-install-sources)");
|
||||
|
||||
if (!gnc_quote_source_fq_installed())
|
||||
{
|
||||
std::cerr << bl::translate ("No quotes retrieved. Finance::Quote isn't "
|
||||
"installed properly.") << "\n";
|
||||
scm_cleanup_and_exit_with_failure (nullptr);
|
||||
}
|
||||
|
||||
auto add_quotes = scm_c_eval_string("gnc:book-add-quotes");
|
||||
auto session = gnc_get_current_session();
|
||||
if (!session)
|
||||
scm_cleanup_and_exit_with_failure (session);
|
||||
|
||||
qof_session_begin(session, add_quotes_file->c_str(), SESSION_NORMAL_OPEN);
|
||||
if (qof_session_get_error(session) != ERR_BACKEND_NO_ERR)
|
||||
scm_cleanup_and_exit_with_failure (session);
|
||||
|
||||
qof_session_load(session, NULL);
|
||||
if (qof_session_get_error(session) != ERR_BACKEND_NO_ERR)
|
||||
scm_cleanup_and_exit_with_failure (session);
|
||||
|
||||
auto scm_book = gnc_book_to_scm(qof_session_get_book(session));
|
||||
auto scm_result = scm_call_2(add_quotes, SCM_BOOL_F, scm_book);
|
||||
|
||||
qof_session_save(session, NULL);
|
||||
if (qof_session_get_error(session) != ERR_BACKEND_NO_ERR)
|
||||
scm_cleanup_and_exit_with_failure (session);
|
||||
|
||||
qof_session_destroy(session);
|
||||
if (!scm_is_true(scm_result))
|
||||
{
|
||||
PERR ("Failed to add quotes to %s.", add_quotes_file->c_str());
|
||||
scm_cleanup_and_exit_with_failure (session);
|
||||
}
|
||||
|
||||
qof_event_resume();
|
||||
gnc_shutdown(0);
|
||||
return;
|
||||
cleanup_and_exit_with_failure (session);
|
||||
gnc_shutdown (1);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -343,12 +299,84 @@ scm_report_list ([[maybe_unused]] void *data,
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
Gnucash::check_finance_quote (void)
|
||||
{
|
||||
gnc_prefs_init ();
|
||||
try
|
||||
{
|
||||
GncQuotes quotes;
|
||||
std::cout << bl::format (bl::translate ("Found Finance::Quote version {1}.")) % quotes.version() << "\n";
|
||||
std::cout << bl::translate ("Finance::Quote sources: ");
|
||||
for (auto source : quotes.sources())
|
||||
std::cout << source << " ";
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
std::cout << err.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Gnucash::add_quotes (const bo_str& uri)
|
||||
{
|
||||
if (uri && !uri->empty())
|
||||
scm_boot_guile (0, nullptr, scm_add_quotes, (void *)&(*uri));
|
||||
gnc_prefs_init ();
|
||||
qof_event_suspend();
|
||||
|
||||
auto session = gnc_get_current_session();
|
||||
if (!session)
|
||||
return 1;
|
||||
|
||||
qof_session_begin(session, uri->c_str(), SESSION_NORMAL_OPEN);
|
||||
if (qof_session_get_error(session) != ERR_BACKEND_NO_ERR)
|
||||
cleanup_and_exit_with_failure (session);
|
||||
|
||||
qof_session_load(session, NULL);
|
||||
if (qof_session_get_error(session) != ERR_BACKEND_NO_ERR)
|
||||
cleanup_and_exit_with_failure (session);
|
||||
|
||||
try
|
||||
{
|
||||
GncQuotes quotes;
|
||||
std::cout << bl::format (bl::translate ("Found Finance::Quote version {1}.")) % quotes.version() << std::endl;
|
||||
auto quote_sources = quotes.sources_as_glist();
|
||||
gnc_quote_source_set_fq_installed (quotes.version().c_str(), quote_sources);
|
||||
g_list_free_full (quote_sources, g_free);
|
||||
quotes.fetch(qof_session_get_book(session));
|
||||
if (quotes.had_failures())
|
||||
std::cerr << quotes.report_failures() << std::endl;
|
||||
}
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
std::cerr << bl::translate("Price retrieval failed: ") << err.what() << std::endl;
|
||||
}
|
||||
qof_session_save(session, NULL);
|
||||
if (qof_session_get_error(session) != ERR_BACKEND_NO_ERR)
|
||||
cleanup_and_exit_with_failure (session);
|
||||
|
||||
qof_session_destroy(session);
|
||||
qof_event_resume();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Gnucash::report_quotes (const char* source, const StrVec& commodities, bool verbose)
|
||||
{
|
||||
try
|
||||
{
|
||||
GncQuotes quotes;
|
||||
quotes.report(source, commodities, verbose);
|
||||
if (quotes.had_failures())
|
||||
std::cerr << quotes.report_failures() << std::endl;
|
||||
}
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
std::cerr << bl::translate("Price retrieval failed: ") << err.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,13 +26,19 @@
|
||||
#define GNUCASH_COMMANDS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
using bo_str = boost::optional <std::string>;
|
||||
using StrVec = std::vector<std::string>;
|
||||
|
||||
namespace Gnucash {
|
||||
|
||||
int check_finance_quote (void);
|
||||
int add_quotes (const bo_str& uri);
|
||||
int report_quotes (const char* source,
|
||||
const StrVec& commodities,
|
||||
bool verbose);
|
||||
int run_report (const bo_str& file_to_load,
|
||||
const bo_str& run_report,
|
||||
const bo_str& export_type,
|
||||
|
@ -244,7 +244,7 @@ Gnucash::CoreApp::parse_command_line (int argc, char **argv)
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << "\n\n";
|
||||
std::cerr << *m_opt_desc_display.get() << "\n";
|
||||
std::cerr << *m_opt_desc_display.get() << std::endl;
|
||||
|
||||
exit(1);
|
||||
}
|
||||
@ -275,13 +275,13 @@ Gnucash::CoreApp::parse_command_line (int argc, char **argv)
|
||||
else
|
||||
std::cout << rel_fmt % gnc_version () << "\n";
|
||||
|
||||
std::cout << bl::translate ("Build ID") << ": " << gnc_build_id () << "\n";
|
||||
std::cout << bl::translate ("Build ID") << ": " << gnc_build_id () << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (m_show_help)
|
||||
{
|
||||
std::cout << *m_opt_desc_display.get() << "\n";
|
||||
std::cout << *m_opt_desc_display.get() << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,7 @@ extern "C" {
|
||||
#include <iostream>
|
||||
#include <gnc-report.h>
|
||||
#include <gnc-locale-utils.hpp>
|
||||
#include <gnc-quotes.hpp>
|
||||
|
||||
namespace bl = boost::locale;
|
||||
|
||||
@ -173,10 +174,24 @@ scm_run_gnucash (void *data, [[maybe_unused]] int argc, [[maybe_unused]] char **
|
||||
gnc_hook_add_dangler(HOOK_UI_SHUTDOWN, (GFunc)gnc_file_quit, NULL, NULL);
|
||||
|
||||
/* Install Price Quote Sources */
|
||||
auto msg = bl::translate ("Checking Finance::Quote...").str(gnc_get_boost_locale());
|
||||
gnc_update_splash_screen (msg.c_str(), GNC_SPLASH_PERCENTAGE_UNKNOWN);
|
||||
scm_c_use_module("gnucash price-quotes");
|
||||
scm_c_eval_string("(gnc:price-quotes-install-sources)");
|
||||
|
||||
try
|
||||
{
|
||||
auto msg = bl::translate ("Checking Finance::Quote...").str(gnc_get_boost_locale());
|
||||
GncQuotes quotes;
|
||||
msg = (bl::format (bl::translate("Found Finance::Quote version {1}.")) % quotes.version()).str(gnc_get_boost_locale());
|
||||
auto quote_sources = quotes.sources_as_glist();
|
||||
gnc_quote_source_set_fq_installed (quotes.version().c_str(), quote_sources);
|
||||
g_list_free (quote_sources);
|
||||
gnc_update_splash_screen (msg.c_str(), GNC_SPLASH_PERCENTAGE_UNKNOWN);
|
||||
}
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
auto msg = bl::translate("Unable to load Finance::Quote.").str(gnc_get_boost_locale());
|
||||
PINFO ("Attempt to load Finance::Quote returned this error message:\n");
|
||||
PINFO ("%s", err.what());
|
||||
gnc_update_splash_screen (msg.c_str(), GNC_SPLASH_PERCENTAGE_UNKNOWN);
|
||||
}
|
||||
|
||||
gnc_hook_run(HOOK_STARTUP, NULL);
|
||||
|
||||
|
@ -1,544 +0,0 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; 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.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program; if not, contact:
|
||||
;;;
|
||||
;;; Free Software Foundation Voice: +1-617-542-5942
|
||||
;;; 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
;;; Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define-module (gnucash price-quotes))
|
||||
|
||||
(export gnc:book-add-quotes) ;; called from gnome/dialog-price-edit-db.c
|
||||
(export gnc:price-quotes-install-sources)
|
||||
|
||||
(use-modules (gnucash engine))
|
||||
(use-modules (gnucash utilities))
|
||||
(use-modules (gnucash core-utils))
|
||||
(use-modules (gnucash app-utils))
|
||||
(use-modules (gnucash gnome-utils))
|
||||
(use-modules (srfi srfi-11)
|
||||
(srfi srfi-1))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define gnc:*finance-quote-check*
|
||||
(string-append (gnc-path-get-bindir) "/gnc-fq-check"))
|
||||
|
||||
(define (gnc:fq-check-sources)
|
||||
(let ((program #f))
|
||||
|
||||
(define (start-program)
|
||||
(set! program
|
||||
(gnc-spawn-process-async
|
||||
(list "perl" "-w" gnc:*finance-quote-check*) #t)))
|
||||
|
||||
(define (get-sources)
|
||||
(when program
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(let ((results (read (fdes->inport (gnc-process-get-fd program 1)))))
|
||||
(gnc:debug "gnc:fq-check-sources results: " results)
|
||||
results))
|
||||
(lambda (key . args) key))))
|
||||
|
||||
(define (kill-program)
|
||||
(when program
|
||||
(gnc-detach-process program #t)
|
||||
(set! program #f)))
|
||||
|
||||
(dynamic-wind start-program get-sources kill-program)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Finance::Quote based instantaneous quotes -- used by the
|
||||
;; --add-price-quotes command line option, etc.
|
||||
;;
|
||||
;; Note From: Dave Peticolas <dave@krondo.com> Date: Sun, 01 Apr 2001
|
||||
;; Those aren't pricedb functions, those are online quote functions,
|
||||
;; i.e., low-level functions for getting online-quotes and putting
|
||||
;; them into the price db. Reports should not be using those
|
||||
;; functions, they should be using the price db. See
|
||||
;; src/engine/gnc-pricedb.h
|
||||
|
||||
(define gnc:*finance-quote-helper*
|
||||
(string-append (gnc-path-get-bindir) "/gnc-fq-helper"))
|
||||
|
||||
(define (gnc:fq-get-quotes requests)
|
||||
;; requests should be a list where each item is of the form
|
||||
;;
|
||||
;; (<fq-method> sym sym ...)
|
||||
;;
|
||||
;; i.e. (alphavantage "RHAT" "LNUX" "IBM")
|
||||
;;
|
||||
;; for currencies, we have
|
||||
;;
|
||||
;; (currency "USD" "AUD") for the conversion from USD to AUD,
|
||||
;; i.e., the price of USD in AUD.
|
||||
;;
|
||||
;; This function will return #f on catastrophic failure or a list
|
||||
;; where, for each element in requests, the output list will contain
|
||||
;; a quote-result element. This element will be #f or an error
|
||||
;; symbol if the corresponding method call fails, or a list
|
||||
;; otherwise. A quote-result list will contain the symbol
|
||||
;; representing the item being quoted, followed by an alist
|
||||
;; detailing the quote data from gnc-fq-helper.
|
||||
;;
|
||||
;; Possible error symbols and their meanings are:
|
||||
;; missing-lib One of the required perl libs is missing
|
||||
;;
|
||||
;; So for the example method call above, the resulting item in the
|
||||
;; output list might look like this:
|
||||
;;
|
||||
;; (("RHAT" (symbol . "RHAT") (gnc:time-no-zone . "...")
|
||||
;; (last . 6.59375) (currency . "USD"))
|
||||
;; ("LNUX" (symbol . "LNUX") (gnc:time-no-zone . "...")
|
||||
;; (last . 3.5) (currency . "USD"))
|
||||
;; ("IBM" (symbol . "IBM") (gnc:time-no-zone . "...")
|
||||
;; (last . 104.42) (currency . "USD")))
|
||||
;;
|
||||
;; Also note that any given value in the alist might be
|
||||
;; 'failed-conversion if the Finance::Quote result for that field
|
||||
;; was unparsable. See the gnc-fq-helper for more details
|
||||
;; about it's output.
|
||||
|
||||
(let ((quoter #f))
|
||||
|
||||
(define (start-quoter)
|
||||
(set! quoter
|
||||
(gnc-spawn-process-async (list "perl" "-w" gnc:*finance-quote-helper*) #t)))
|
||||
|
||||
(define (get-quotes)
|
||||
(when quoter
|
||||
(map
|
||||
(lambda (request)
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(gnc:debug "handling-request: " request)
|
||||
(and (member (car request) '("currency" "alphavantage" "vanguard"))
|
||||
(not (getenv "ALPHAVANTAGE_API_KEY"))
|
||||
(throw 'need-alphavantage-key))
|
||||
;; we need to display the first element (the method,
|
||||
;; so it won't be quoted) and then write the rest
|
||||
(with-output-to-port (fdes->outport (gnc-process-get-fd quoter 0))
|
||||
(lambda ()
|
||||
(display #\()
|
||||
(display (car request))
|
||||
(display " ")
|
||||
(for-each write (cdr request))
|
||||
(display #\))
|
||||
(newline)
|
||||
(force-output)))
|
||||
|
||||
(let ((results (read (fdes->inport (gnc-process-get-fd quoter 1)))))
|
||||
(gnc:debug "results: " results)
|
||||
results))
|
||||
(lambda (key . args) key)))
|
||||
requests)))
|
||||
|
||||
(define (kill-quoter)
|
||||
(when quoter
|
||||
(gnc-detach-process quoter #t)
|
||||
(set! quoter #f)))
|
||||
|
||||
(dynamic-wind start-quoter get-quotes kill-quoter)))
|
||||
|
||||
(define (gnc:book-add-quotes window book)
|
||||
|
||||
(define (book->commodity->fq-call-data book)
|
||||
;; Call helper that walks all of the defined commodities to see if
|
||||
;; any are marked for quote retrieval. This function returns a
|
||||
;; list of info needed for the relevant Finance::Quote calls, and
|
||||
;; 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:
|
||||
;;
|
||||
;; (("alphavantage" (commodity-1 currency-1 tz-1)
|
||||
;; (commodity-2 currency-2 tz-2) ...)
|
||||
;; ("fidelity_direct" (commodity-3 currency-3 tz-3)
|
||||
;; (commodity-4 currency-4 tz-4) ...)
|
||||
;; ("currency" curr-1 curr-2 tz)
|
||||
;; ("currency" curr-3 curr-4 tz) ...)
|
||||
|
||||
(let-values (((currency-list commodity-list)
|
||||
(partition (lambda (a) (string=? (car a) "currency"))
|
||||
(gnc-commodity-table-get-quotable-commodities
|
||||
(gnc-commodity-table-get-table book)))))
|
||||
|
||||
(let ((commodity-hash (make-hash-table))
|
||||
(currency-list-filtered
|
||||
(filter
|
||||
(lambda (a)
|
||||
(and (not (gnc-commodity-equiv (cadr a) (caddr a)))
|
||||
(not (string=? (gnc-commodity-get-mnemonic (cadr a)) "XXX"))))
|
||||
currency-list)))
|
||||
|
||||
;; Now collect symbols going to the same backend.
|
||||
(for-each
|
||||
(lambda (item)
|
||||
(let ((key (car item))
|
||||
(val (cdr item)))
|
||||
(hash-set! commodity-hash key
|
||||
(cons val (hash-ref commodity-hash key '())))))
|
||||
commodity-list)
|
||||
|
||||
;; Now translate to just what gnc-fq-helper expects.
|
||||
(and (or (pair? currency-list-filtered) (pair? commodity-list))
|
||||
(append
|
||||
(hash-map->list cons commodity-hash)
|
||||
(map (lambda (cmd) (cons (car cmd) (list (cdr cmd))))
|
||||
currency-list-filtered))))))
|
||||
|
||||
(define (fq-call-data->fq-calls fq-call-data)
|
||||
;; take an output element from book->commodity->fq-call-data and
|
||||
;; return a list where the gnc_commodities have been converted to
|
||||
;; their fq-suitable symbol strings. i.e. turn the former into
|
||||
;; the latter:
|
||||
;;
|
||||
;; ("alphavantage" (commodity-1 currency-1 tz-1)
|
||||
;; (commodity-2 currency-2 tz-2) ...)
|
||||
;;
|
||||
;; ("alphavantage" "IBM" "AMD" ...)
|
||||
;;
|
||||
|
||||
(if (equal? (car fq-call-data) "currency")
|
||||
(map (lambda (quote-item-info)
|
||||
(list (car fq-call-data)
|
||||
(gnc-commodity-get-mnemonic (car quote-item-info))
|
||||
(gnc-commodity-get-mnemonic (cadr quote-item-info))))
|
||||
(cdr fq-call-data))
|
||||
(list
|
||||
(cons (car fq-call-data)
|
||||
(map
|
||||
(lambda (quote-item-info)
|
||||
(gnc-commodity-get-mnemonic (car quote-item-info)))
|
||||
(cdr fq-call-data))))))
|
||||
|
||||
(define (fq-results->commod-tz-quote-triples fq-call-data fq-results)
|
||||
;; Change output of gnc:fq-get-quotes to a list of (commod
|
||||
;; timezone quote) triples using the matching commodity items from
|
||||
;; fq-call-data.
|
||||
;;
|
||||
;; This function presumes that fq-call-data is "correct" -- it
|
||||
;; contains the correct number of calls, and has the commodity
|
||||
;; pointers in all the right places. If not, then the results of
|
||||
;; this function are undefined.
|
||||
;;
|
||||
;; If there's a catatstrophic error, this function might return
|
||||
;; #f. If there's an error for any given input element, there
|
||||
;; will be a pair like this in the output (#f . <commodity>)
|
||||
;; indicating the commodity for which the quote failed.
|
||||
;;
|
||||
;; If this function doesn't return #f, it will return a list with
|
||||
;; as many elements as there were commodities in the fq-call-data.
|
||||
;;
|
||||
;; We might want more sophisticated error handling later, but this
|
||||
;; will do for now .
|
||||
(let ((result-list '()))
|
||||
|
||||
(define (process-a-quote call-data call-result)
|
||||
;; data -> (commod-1 currency-1 tz-1)
|
||||
;; result -> (commod-1-sym . result-alist) or some kind of garbage.
|
||||
(if (and (list? call-result)
|
||||
(not (null? call-result))
|
||||
(list? (cdr call-result))
|
||||
(every
|
||||
(lambda (alist-item)
|
||||
(and (pair? alist-item)
|
||||
(not (eq? 'failed-conversion (cdr alist-item)))))
|
||||
(cdr call-result)))
|
||||
;; OK, data is good (as far as we can tell).
|
||||
(set! result-list
|
||||
(cons (list (car call-data)
|
||||
(caddr call-data)
|
||||
(cdr call-result))
|
||||
result-list))
|
||||
(set! result-list
|
||||
(cons (cons #f (car call-data))
|
||||
result-list))))
|
||||
|
||||
(define (process-call-result-pair call-data call-result)
|
||||
(if (and (list? call-result)
|
||||
(= (length call-data) (+ 1 (length call-result))))
|
||||
|
||||
;; OK, continue.
|
||||
(for-each
|
||||
(lambda (call-data-item call-result-item)
|
||||
(if (and (list? call-result-item) (list? (car call-result-item)))
|
||||
(for-each
|
||||
(lambda (result-subitem)
|
||||
(gnc:debug "call-data-item: " call-data-item)
|
||||
(gnc:debug "result-subitem: " result-subitem)
|
||||
(process-a-quote call-data-item result-subitem))
|
||||
call-result-item)
|
||||
(process-a-quote call-data-item call-result-item)))
|
||||
(cdr call-data) call-result)
|
||||
|
||||
;; else badly formed result, must assume all garbage.
|
||||
(for-each
|
||||
(lambda (call-item)
|
||||
(set! result-list (cons (cons #f (car call-item)) result-list)))
|
||||
(cdr call-data))))
|
||||
|
||||
(and (list? fq-call-data)
|
||||
(list? fq-results)
|
||||
(= (length fq-call-data) (length fq-results))
|
||||
(begin
|
||||
(for-each process-call-result-pair
|
||||
fq-call-data
|
||||
fq-results)
|
||||
(reverse result-list)))))
|
||||
|
||||
(define (timestr->time64 timestr time-zone)
|
||||
;; time-zone is ignored currently
|
||||
(gnc-parse-time-to-time64 timestr "%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
(define (commodity-tz-quote-triple->price book c-tz-quote-triple)
|
||||
;; return a string like "NASDAQ:CSCO" on error, or a price on
|
||||
;; success. Presume that F::Q currencies are ISO4217 currencies.
|
||||
(let* ((commodity (first c-tz-quote-triple))
|
||||
(time-zone (second c-tz-quote-triple))
|
||||
(quote-data (third c-tz-quote-triple))
|
||||
(gnc-time (assq-ref quote-data 'gnc:time-no-zone))
|
||||
(price #f)
|
||||
(price-type #f)
|
||||
(currency-str (assq-ref quote-data 'currency))
|
||||
(commodity-table (gnc-commodity-table-get-table book))
|
||||
(currency
|
||||
(and commodity-table
|
||||
(string? currency-str)
|
||||
(gnc-commodity-table-lookup commodity-table
|
||||
"ISO4217"
|
||||
(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)))
|
||||
|
||||
(let lp ((price-syms '(last nav price))
|
||||
(price-types '("last" "nav" "unknown")))
|
||||
(unless (null? price-syms)
|
||||
(cond
|
||||
((assq-ref quote-data (car price-syms)) =>
|
||||
(lambda (p)
|
||||
;; The OpenExchange exchange rate source in Finance::Quote produces
|
||||
;; some ridiculously precise prices like #e6.95253159056541e-5 which
|
||||
;; produce a denominator greater than INT64_MAX. Use the rationalize
|
||||
;; function to bring them back to reality. The precision parameter is
|
||||
;; chosen empirically to give the best results.
|
||||
(set! price (gnc-scm-to-numeric
|
||||
(rationalize p 1/100000000000000)))
|
||||
(set! price-type (car price-types))))
|
||||
(else (lp (cdr price-syms) (cdr price-types))))))
|
||||
|
||||
(if gnc-time
|
||||
(set! gnc-time (timestr->time64 gnc-time time-zone))
|
||||
(set! gnc-time (gnc:get-today)))
|
||||
|
||||
(if (not (and commodity currency gnc-time price price-type))
|
||||
(string-append
|
||||
currency-str ":" (gnc-commodity-get-mnemonic commodity))
|
||||
(begin
|
||||
(set! saved-price (gnc-pricedb-lookup-day-t64 pricedb
|
||||
commodity currency
|
||||
gnc-time))
|
||||
(if (not (null? saved-price))
|
||||
(begin
|
||||
(if (gnc-commodity-equiv (gnc-price-get-currency saved-price)
|
||||
commodity)
|
||||
(set! price (gnc-numeric-invert price)))
|
||||
(if (>= (gnc-price-get-source saved-price) PRICE-SOURCE-FQ)
|
||||
(begin
|
||||
(gnc-price-begin-edit saved-price)
|
||||
(gnc-price-set-time64 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-time64 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)
|
||||
(when price
|
||||
(gnc-pricedb-add-price pricedb price)
|
||||
(gnc-price-unref price)))
|
||||
prices)))
|
||||
|
||||
(define (show-error msg)
|
||||
(gnc:gui-error msg (G_ msg)))
|
||||
|
||||
;; Add the alphavantage api key to the environment. This value is taken from
|
||||
;; the Online Quotes preference tab
|
||||
(let ((alphavantage-api-key
|
||||
(gnc-prefs-get-string "general.finance-quote" "alphavantage-api-key")))
|
||||
(gnc:debug "ALPHAVANTAGE_API_KEY=" alphavantage-api-key)
|
||||
(unless (string-null? alphavantage-api-key)
|
||||
(setenv "ALPHAVANTAGE_API_KEY" alphavantage-api-key)))
|
||||
|
||||
(let* ((fq-call-data (book->commodity->fq-call-data book))
|
||||
(fq-calls (and fq-call-data
|
||||
(append-map fq-call-data->fq-calls fq-call-data)))
|
||||
(fq-results (and fq-calls (gnc:fq-get-quotes fq-calls)))
|
||||
(commod-tz-quote-triples (and fq-results (list? (car fq-results))
|
||||
(fq-results->commod-tz-quote-triples
|
||||
fq-call-data fq-results)))
|
||||
;; At this point commod-tz-quote-triples will either be #f or a
|
||||
;; list of items. Each item will either be (commodity
|
||||
;; timezone quote-data) or (#f . problem-commodity)
|
||||
(problem-syms (and commod-tz-quote-triples
|
||||
(filter-map
|
||||
(lambda (cq-pair)
|
||||
(and (not (car cq-pair))
|
||||
(string-append
|
||||
(gnc-commodity-get-namespace (cdr cq-pair))
|
||||
":"
|
||||
(gnc-commodity-get-mnemonic (cdr cq-pair)))))
|
||||
commod-tz-quote-triples)))
|
||||
;; strip out the "bad" ones from above.
|
||||
(ok-syms (and commod-tz-quote-triples (filter car commod-tz-quote-triples)))
|
||||
(keep-going? #t))
|
||||
|
||||
(cond
|
||||
((not fq-call-data)
|
||||
(set! keep-going? #f)
|
||||
(show-error (N_ "No commodities marked for quote retrieval.")))
|
||||
|
||||
((not fq-results)
|
||||
(set! keep-going? #f)
|
||||
(show-error (N_ "Unable to get quotes or diagnose the problem.")))
|
||||
|
||||
((memq 'missing-lib fq-results)
|
||||
(set! keep-going? #f)
|
||||
(show-error (N_ "You are missing some needed Perl libraries.
|
||||
Run 'gnc-fq-update' as root to install them.")))
|
||||
|
||||
((memq 'need-alphavantage-key fq-results)
|
||||
(set! keep-going? #f)
|
||||
(show-error (format #f (G_ "ERROR: ALPHAVANTAGE_API_KEY must be set for currency and quotes; see ~A")
|
||||
"https://wiki.gnucash.org/wiki/Online_Quotes#Source_Alphavantage.2C_US")))
|
||||
|
||||
((memq 'system-error fq-results)
|
||||
(set! keep-going? #f)
|
||||
(show-error (N_ "There was a system error while retrieving the price quotes.")))
|
||||
|
||||
((not (list? (car fq-results)))
|
||||
(set! keep-going? #f)
|
||||
(show-error (N_ "There was an unknown error while retrieving the price quotes.")))
|
||||
|
||||
((not commod-tz-quote-triples)
|
||||
(set! keep-going? #f)
|
||||
(show-error (N_ "Unable to get quotes or diagnose the problem.")))
|
||||
|
||||
((pair? problem-syms)
|
||||
(cond
|
||||
((not (gnucash-ui-is-running))
|
||||
(gnc:warn
|
||||
(with-output-to-string
|
||||
(lambda ()
|
||||
(display "Unable to retrieve quotes for these items:\n")
|
||||
(display (string-join problem-syms "\n "))
|
||||
(newline)
|
||||
(display "Continuing with good quotes.")
|
||||
(newline)))))
|
||||
|
||||
((and ok-syms (not (null? ok-syms)))
|
||||
(set! keep-going?
|
||||
(gnc-verify-dialog
|
||||
window #t (with-output-to-string
|
||||
(lambda ()
|
||||
(display (G_ "Unable to retrieve quotes for these items:"))
|
||||
(display "\n ")
|
||||
(display (string-join problem-syms "\n "))
|
||||
(newline)
|
||||
(display (G_ "Continue using only the good quotes?")))))))
|
||||
|
||||
(else
|
||||
(set! keep-going? #f)
|
||||
(gnc-error-dialog
|
||||
window (with-output-to-string
|
||||
(lambda ()
|
||||
(display (G_ "Unable to retrieve quotes for these items:"))
|
||||
(display "\n ")
|
||||
(display (string-join problem-syms "\n ")))))))))
|
||||
|
||||
(when keep-going?
|
||||
(let ((prices (map (lambda (triple)
|
||||
(commodity-tz-quote-triple->price book triple))
|
||||
ok-syms)))
|
||||
(when (any string? prices)
|
||||
(if (gnucash-ui-is-running)
|
||||
(set! keep-going?
|
||||
(gnc-verify-dialog
|
||||
window #t
|
||||
(with-output-to-string
|
||||
(lambda ()
|
||||
(display (G_ "Unable to create prices for these items:"))
|
||||
(display "\n ")
|
||||
(display (string-join (filter string? prices) "\n "))
|
||||
(newline)
|
||||
(display (G_ "Add remaining good quotes?"))))))
|
||||
(gnc:warn
|
||||
(with-output-to-string
|
||||
(lambda ()
|
||||
(display "Unable to create prices for these items:\n ")
|
||||
(display (string-join (filter string? prices) "\n "))
|
||||
(newline)
|
||||
(display "Adding remaining good quotes.")
|
||||
(newline))))))
|
||||
|
||||
(when keep-going?
|
||||
(book-add-prices! book (filter (negate string?) prices)))))))
|
||||
|
||||
(define (gnc:price-quotes-install-sources)
|
||||
(let ((sources (gnc:fq-check-sources)))
|
||||
(cond
|
||||
((list? sources)
|
||||
;; Translators: ~A is the version string
|
||||
(format #t (G_ "Found Finance::Quote version ~A.") (car sources))
|
||||
(newline)
|
||||
(gnc:msg "Found Finance::Quote version " (car sources))
|
||||
(gnc-quote-source-set-fq-installed (car sources) (cdr sources))))))
|
@ -17,6 +17,7 @@ set (app_utils_HEADERS
|
||||
gnc-gsettings.h
|
||||
gnc-help-utils.h
|
||||
gnc-prefs-utils.h
|
||||
gnc-quotes.hpp
|
||||
gnc-state.h
|
||||
gnc-ui-util.h
|
||||
gnc-ui-balances.h
|
||||
@ -31,6 +32,7 @@ set (app_utils_SOURCES
|
||||
gnc-euro.c
|
||||
gnc-gsettings.cpp
|
||||
gnc-prefs-utils.c
|
||||
gnc-quotes.cpp
|
||||
gnc-state.c
|
||||
gnc-ui-util.c
|
||||
gnc-ui-balances.c
|
||||
@ -46,7 +48,9 @@ endif()
|
||||
set(app_utils_ALL_SOURCES ${app_utils_SOURCES} ${app_utils_HEADERS})
|
||||
set(app_utils_ALL_LIBRARIES
|
||||
gnc-engine
|
||||
${GLIB_LDFLAGS}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_PROPERTY_TREE_LIBRARY}
|
||||
${Boost_LOCALE_LIBRARY}
|
||||
${GIO_LDFLAGS}
|
||||
${LIBXML2_LDFLAGS}
|
||||
${LIBXSLT_LDFLAGS}
|
||||
|
1059
libgnucash/app-utils/gnc-quotes.cpp
Normal file
1059
libgnucash/app-utils/gnc-quotes.cpp
Normal file
File diff suppressed because it is too large
Load Diff
146
libgnucash/app-utils/gnc-quotes.hpp
Normal file
146
libgnucash/app-utils/gnc-quotes.hpp
Normal file
@ -0,0 +1,146 @@
|
||||
/********************************************************************\
|
||||
* gnc-quotes.hpp -- proxy for Finance::Quote *
|
||||
* Copyright (C) 2021 Geert Janssens <geert@kobaltwit.be> *
|
||||
* *
|
||||
* 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: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
#ifndef GNC_QUOTES_HPP
|
||||
#define GNC_QUOTES_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <gnc-commodity.hpp> // For CommVec alias
|
||||
#include <glib.h>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" {
|
||||
#include <qofbook.h>
|
||||
}
|
||||
|
||||
using StrVec = std::vector<std::string>;
|
||||
using QuoteSources = StrVec;
|
||||
|
||||
enum class GncQuoteError
|
||||
{
|
||||
SUCCESS,
|
||||
NO_RESULT,
|
||||
QUOTE_FAILED,
|
||||
NO_CURRENCY,
|
||||
UNKNOWN_CURRENCY,
|
||||
NO_PRICE,
|
||||
UNKNOWN_PRICE_TYPE,
|
||||
PRICE_PARSE_FAILURE,
|
||||
};
|
||||
|
||||
/** QuoteFailure elements are namespace, mnemonic, error code, and
|
||||
* F::Q errormsg if there is one.
|
||||
*/
|
||||
using QuoteFailure = std::tuple<std::string, std::string,
|
||||
GncQuoteError, std::string>;
|
||||
using QFVec = std::vector<QuoteFailure>;
|
||||
|
||||
struct GncQuoteException : public std::runtime_error
|
||||
{
|
||||
GncQuoteException(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class GncQuotesImpl;
|
||||
|
||||
class GncQuotes
|
||||
{
|
||||
public:
|
||||
/** Create a GncQuotes object.
|
||||
*
|
||||
* Throws a GncQuoteException if Finance::Quote is not installed or fails to initialize.
|
||||
*/
|
||||
GncQuotes ();
|
||||
~GncQuotes ();
|
||||
|
||||
/** Fetch quotes for all commodities in our db that have a quote source set
|
||||
*
|
||||
* @param book The current book.
|
||||
*/
|
||||
void fetch (QofBook *book);
|
||||
/** Fetch quotes for a vector of commodities
|
||||
*
|
||||
* @param commodities std::vector of the gnc_commodity* to get quotes for.
|
||||
* @note Commodities without a quote source will be silently ignored.
|
||||
*/
|
||||
void fetch (CommVec& commodities);
|
||||
/** Fetch quote for a single commodity
|
||||
*
|
||||
* @param comm Commodity for which to retrieve a quote
|
||||
* @note Commodity must have a quote source set or the call will silently fail.
|
||||
*/
|
||||
void fetch (gnc_commodity *comm);
|
||||
/** Report quote results from Finance::Quote to std::cout.
|
||||
*
|
||||
* @param source A valid quote source
|
||||
* @param commodities A std::vector of symbols to request quotes for.
|
||||
* @note If requesting currency rates the first symbol is the to-currency and the rest are from-currencies. For example, {"USD", "EUR", "CAD"} will print the price of 1 Euro and 1 Canadian Dollar in US Dollars.
|
||||
* @param verbose Ignored for currency queries. If false it will print the six fields GnuCash uses regardless of whether a value was returned; if true it will print all of the fields for which Finanace::Quote returned values.
|
||||
*/
|
||||
void report (const char* source, const StrVec& commodities, bool verbose = false);
|
||||
/** Get the installed Finance::Quote version
|
||||
*
|
||||
* @return the Finance::Quote version string
|
||||
*/
|
||||
const std::string& version() noexcept;
|
||||
|
||||
/** Get the available Finance::Quote sources as a std::vector
|
||||
*
|
||||
* @return The quote sources configured in Finance::Quote
|
||||
*/
|
||||
const QuoteSources& sources() noexcept;
|
||||
|
||||
/** Get the available Finance::Quote sources as a GList
|
||||
*
|
||||
* @return A double-linked list containing the names of the installed quote sources.
|
||||
* @note the list and its contents are owned by the caller and should be freed with `g_list_free_full(list, g_free)`.
|
||||
*/
|
||||
GList* sources_as_glist () ;
|
||||
|
||||
/** Report if there were quotes requested but not retrieved.
|
||||
*
|
||||
* @returns True if there were quote failures.
|
||||
*/
|
||||
bool had_failures() noexcept;
|
||||
|
||||
/** Report the commodities for which quotes were requested but not successfully retrieved.
|
||||
*
|
||||
* This does not include requested commodities that didn't have a quote source.
|
||||
*
|
||||
* @return a reference to a vector of QuoteFailure tuples.
|
||||
* @note The vector and its contents belong to the GncQuotes object and will be destroyed with it.
|
||||
*/
|
||||
const QFVec& failures() noexcept;
|
||||
|
||||
/* Report the commodities for which quotes were requested but not successfully retrieved.
|
||||
*
|
||||
* This does not include requested commodities that didn't have a quote source.
|
||||
*
|
||||
* @return A localized std::string with an intro and a list of the quote failures with a cause. The string is owned by the caller.
|
||||
*/
|
||||
const std::string report_failures() noexcept;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GncQuotesImpl> m_impl;
|
||||
};
|
||||
|
||||
#endif /* GNC_QUOTES_HPP */
|
@ -31,6 +31,24 @@ gnc_add_test_with_guile(test-sx test-sx.cpp
|
||||
APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS
|
||||
)
|
||||
|
||||
set(test_gnc_quotes_SOURCES
|
||||
gtest-gnc-quotes.cpp
|
||||
)
|
||||
|
||||
set(test_gnc_quotes_INCLUDES
|
||||
${CMAKE_BINARY_DIR}/common # for config.h
|
||||
${MODULEPATH}
|
||||
)
|
||||
|
||||
set(test_gnc_quotes_LIBS
|
||||
gnc-engine
|
||||
gtest
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_LOCALE_LIBRARY}
|
||||
${Boost_PROPERTY_TREE_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
)
|
||||
gnc_add_test(test-gnc-quotes "${test_gnc_quotes_SOURCES}" test_gnc_quotes_INCLUDES test_gnc_quotes_LIBS)
|
||||
|
||||
set(GUILE_DEPENDS
|
||||
scm-test-engine
|
||||
@ -43,6 +61,7 @@ set(GUILE_DEPENDS
|
||||
|
||||
set_dist_list(test_app_utils_DIST
|
||||
CMakeLists.txt
|
||||
gtest-gnc-quotes.cpp
|
||||
test-exp-parser.c
|
||||
test-print-parse-amount.cpp
|
||||
test-sx.cpp
|
||||
|
411
libgnucash/app-utils/test/gtest-gnc-quotes.cpp
Normal file
411
libgnucash/app-utils/test/gtest-gnc-quotes.cpp
Normal file
@ -0,0 +1,411 @@
|
||||
/********************************************************************\
|
||||
* test-gnc-price-quotes.cpp -- Unit tests for GncQuotes *
|
||||
* *
|
||||
* Copyright 2022 John Ralls <jralls@ceridwen.us> *
|
||||
* *
|
||||
* 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: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
* *
|
||||
\********************************************************************/
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <config.h>
|
||||
#include <gnc-session.h>
|
||||
#include <gnc-commodity.h>
|
||||
#include <gnc-pricedb-p.h>
|
||||
#include <qof.h>
|
||||
|
||||
/* gnc-quotes normally gets this from gnc-ui-util, but let's avoid the dependency. */
|
||||
static gnc_commodity*
|
||||
gnc_default_currency(void)
|
||||
{
|
||||
auto book{qof_session_get_book(gnc_get_current_session())};
|
||||
auto table{gnc_commodity_table_get_table(book)};
|
||||
return gnc_commodity_table_lookup(table, GNC_COMMODITY_NS_CURRENCY, "USD");
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
#include <gtest/gtest.h>
|
||||
#include "../gnc-quotes.cpp"
|
||||
|
||||
class GncMockQuoteSource final : public GncQuoteSource
|
||||
{
|
||||
const std::string m_version{"9.99"};
|
||||
const StrVec m_sources{"currency", "yahoo_json"};
|
||||
const StrVec m_quotes;
|
||||
const StrVec m_errors;
|
||||
public:
|
||||
GncMockQuoteSource(StrVec&& quotes, StrVec&& errors) :
|
||||
m_quotes{std::move(quotes)}, m_errors{std::move(errors)}{}
|
||||
~GncMockQuoteSource() override = default;
|
||||
const std::string& get_version() const noexcept override { return m_version; }
|
||||
const StrVec& get_sources() const noexcept override { return m_sources; }
|
||||
QuoteResult get_quotes(const std::string&) const override;
|
||||
};
|
||||
|
||||
class GncFailedQuoteSource final : public GncQuoteSource
|
||||
{
|
||||
|
||||
const std::string m_version{"0"};
|
||||
const StrVec m_sources;
|
||||
public:
|
||||
GncFailedQuoteSource()
|
||||
{
|
||||
std::string err{"Failed to initialize Finance::Quote: "};
|
||||
err += "missing_modules Mozilla::CA Try::Tiny";
|
||||
throw GncQuoteSourceError (err);
|
||||
}
|
||||
~GncFailedQuoteSource() override = default;
|
||||
const std::string& get_version() const noexcept override { return m_version; }
|
||||
const StrVec& get_sources() const noexcept override { return m_sources; }
|
||||
QuoteResult get_quotes(const std::string&) const override {return {0, {}, {}}; }
|
||||
};
|
||||
|
||||
QuoteResult
|
||||
GncMockQuoteSource::get_quotes(const std::string& json_string) const
|
||||
{
|
||||
if (m_errors.empty())
|
||||
return {0, m_quotes, m_errors};
|
||||
return {1, m_quotes, m_errors};
|
||||
}
|
||||
|
||||
class GncQuotesTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GncQuotesTest() : m_session{gnc_get_current_session()},
|
||||
m_book{qof_session_get_book(gnc_get_current_session())}
|
||||
{
|
||||
qof_init();
|
||||
gnc_commodity_table_register();
|
||||
gnc_pricedb_register();
|
||||
auto comm_table{gnc_commodity_table_new()};
|
||||
qof_book_set_data(m_book, GNC_COMMODITY_TABLE, comm_table);
|
||||
auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
|
||||
auto source{gnc_quote_source_lookup_by_internal("currency")};
|
||||
gnc_commodity_begin_edit(eur);
|
||||
gnc_commodity_set_quote_flag(eur, TRUE);
|
||||
gnc_commodity_set_quote_source(eur, source);
|
||||
gnc_commodity_commit_edit(eur);
|
||||
gnc_commodity_table_insert(comm_table, eur);
|
||||
auto usd = gnc_commodity_new(m_book, "United States Dollar", "CURRENCY",
|
||||
"USD", NULL, 100);
|
||||
gnc_commodity_table_insert(comm_table, usd);
|
||||
source = gnc_quote_source_lookup_by_internal("yahoo_json");
|
||||
auto aapl = gnc_commodity_new(m_book, "Apple", "NASDAQ", "AAPL", NULL, 1);
|
||||
gnc_commodity_begin_edit(aapl);
|
||||
gnc_commodity_set_quote_flag(aapl, TRUE);
|
||||
gnc_commodity_set_quote_source(aapl, source);
|
||||
gnc_commodity_commit_edit(aapl);
|
||||
gnc_commodity_table_insert(comm_table, aapl);
|
||||
auto hpe = gnc_commodity_new(m_book, "Hewlett Packard", "NYSE", "HPE",
|
||||
NULL, 1);
|
||||
gnc_commodity_begin_edit(hpe);
|
||||
gnc_commodity_set_quote_flag(hpe, TRUE);
|
||||
gnc_commodity_set_quote_source(hpe, source);
|
||||
gnc_commodity_commit_edit(hpe);
|
||||
gnc_commodity_table_insert(comm_table, hpe);
|
||||
auto fkcm = gnc_commodity_new(m_book, "Fake Company", "NASDAQ", "FKCM", NULL, 1);
|
||||
gnc_commodity_begin_edit(fkcm);
|
||||
gnc_commodity_set_quote_flag(fkcm, TRUE);
|
||||
gnc_commodity_set_quote_source(fkcm, source);
|
||||
gnc_commodity_commit_edit(fkcm);
|
||||
gnc_commodity_table_insert(comm_table, fkcm);
|
||||
gnc_quote_source_set_fq_installed("TestSuite", g_list_prepend(nullptr, (void*)"yahoo_json"));
|
||||
}
|
||||
~GncQuotesTest() {
|
||||
gnc_clear_current_session();
|
||||
}
|
||||
|
||||
QofSession* m_session;
|
||||
QofBook* m_book;
|
||||
};
|
||||
|
||||
TEST_F(GncQuotesTest, quote_sources)
|
||||
{
|
||||
auto qs_cur{gnc_quote_source_lookup_by_internal("currency")};
|
||||
auto qs_yahoo{gnc_quote_source_lookup_by_internal("yahoo_json")};
|
||||
auto qs_alpha{gnc_quote_source_lookup_by_internal("alphavantage")};
|
||||
EXPECT_TRUE(qs_cur != nullptr);
|
||||
EXPECT_TRUE(qs_yahoo != nullptr);
|
||||
EXPECT_TRUE(qs_alpha != nullptr);
|
||||
EXPECT_TRUE(gnc_quote_source_get_supported(qs_cur));
|
||||
EXPECT_TRUE(gnc_quote_source_get_supported(qs_yahoo));
|
||||
EXPECT_FALSE(gnc_quote_source_get_supported(qs_alpha));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, quotable_commodities)
|
||||
{
|
||||
auto commodities{gnc_quotes_get_quotable_commodities(gnc_commodity_table_get_table(m_book))};
|
||||
EXPECT_EQ(4u, commodities.size());
|
||||
}
|
||||
|
||||
#ifdef HAVE_F_Q
|
||||
TEST_F(GncQuotesTest, online_wiggle)
|
||||
{
|
||||
GncQuotes quotes;
|
||||
quotes.fetch(m_book);
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
auto failures{quotes.failures()};
|
||||
ASSERT_EQ(1u, failures.size());
|
||||
EXPECT_EQ(GncQuoteError::QUOTE_FAILED, std::get<2>(failures[0]));
|
||||
// EXPECT_EQ(GncQuoteError::QUOTE_FAILED, std::get<2>(failures[1]));
|
||||
EXPECT_EQ(3u, gnc_pricedb_get_num_prices(pricedb));
|
||||
}
|
||||
#else
|
||||
TEST_F(GncQuotesTest, fq_failure)
|
||||
{
|
||||
EXPECT_THROW(GncQuotes quotes;, GncQuoteException);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(GncQuotesTest, offline_wiggle)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"EUR\":{\"symbol\":\"EUR\",\"currency\":\"USD\",\"success\":\"1\",\"inverted\":0,\"last\":1.0004},"
|
||||
"\"AAPL\":{\"eps\":6.05,\"success\":1,\"year_range\":\" 129.04 - 182.94\",\"currency\":\"USD\",\"exchange\":\"Sourced from Yahoo Finance (as JSON)\",\"volume\":73539475,\"close\":157.22,\"high\":158.39,\"open\":156.64,\"div_yield\":0.5660857,\"last\":157.96,\"isodate\":\"2022-09-01\",\"method\":\"yahoo_json\",\"name\":\"AAPL (Apple Inc.)\",\"pe\":26.10909,\"low\":154.67,\"type\":\"EQUITY\",\"symbol\":\"AAPL\",\"date\":\"09/01/2022\"},"
|
||||
"\"HPE\":{\"symbol\":\"HPE\",\"date\":\"09/01/2022\",\"low\":13.13,\"type\":\"EQUITY\",\"method\":\"yahoo_json\",\"name\":\"HPE (Hewlett Packard Enterprise Comp)\",\"isodate\":\"2022-09-01\",\"pe\":4.7921147,\"last\":13.37,\"high\":13.535,\"close\":13.6,\"open\":13.5,\"div_yield\":3.5294116,\"volume\":16370483,\"exchange\":\"Sourced from Yahoo Finance (as JSON)\",\"currency\":\"USD\",\"year_range\":\" 12.4 - 17.76\",\"eps\":2.79,\"success\":1},"
|
||||
"\"FKCM\":{\"success\":0,\"symbol\":\"FKCM\",\"errormsg\":\"Error retrieving quote for FKCM - no listing for this name found. Please check symbol and the two letter extension (if any)\"}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(m_book);
|
||||
auto failures{quotes.failures()};
|
||||
ASSERT_EQ(1u, failures.size());
|
||||
EXPECT_EQ(GncQuoteError::QUOTE_FAILED, std::get<2>(failures[0]));
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
EXPECT_EQ(3u, gnc_pricedb_get_num_prices(pricedb));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, offline_report)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"AAPL\":{\"eps\":6.05,\"success\":1,\"year_range\":\" 129.04 - 182.94\",\"currency\":\"USD\",\"exchange\":\"Sourced from Yahoo Finance (as JSON)\",\"volume\":73539475,\"close\":157.22,\"high\":158.39,\"open\":156.64,\"div_yield\":0.5660857,\"last\":157.96,\"isodate\":\"2022-09-01\",\"method\":\"yahoo_json\",\"name\":\"AAPL (Apple Inc.)\",\"pe\":26.10909,\"low\":154.67,\"type\":\"EQUITY\",\"symbol\":\"AAPL\",\"date\":\"09/01/2022\"},"
|
||||
"\"HPE\":{\"symbol\":\"HPE\",\"date\":\"09/01/2022\",\"low\":13.13,\"type\":\"EQUITY\",\"method\":\"yahoo_json\",\"name\":\"HPE (Hewlett Packard Enterprise Comp)\",\"isodate\":\"2022-09-01\",\"pe\":4.7921147,\"last\":13.37,\"high\":13.535,\"close\":13.6,\"open\":13.5,\"div_yield\":3.5294116,\"volume\":16370483,\"exchange\":\"Sourced from Yahoo Finance (as JSON)\",\"currency\":\"USD\",\"year_range\":\" 12.4 - 17.76\",\"eps\":2.79,\"success\":1},"
|
||||
"\"FKCM\":{\"success\":0,\"symbol\":\"FKCM\",\"errormsg\":\"Error retrieving quote for FKCM - no listing for this name found. Please check symbol and the two letter extension (if any)\"}"
|
||||
"}"
|
||||
};
|
||||
StrVec commodities{"AAPL", "HPE", "FKCM"};
|
||||
StrVec err_vec;
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.report("yahoo_json", commodities, false);
|
||||
quotes.report("yahoo_json", commodities, true);
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, offline_currency_report)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"EUR\":{\"symbol\":\"EUR\",\"currency\":\"USD\",\"success\":\"1\",\"inverted\":0,\"last\":1.0004}"
|
||||
"}"
|
||||
};
|
||||
StrVec commodities{"USD", "EUR"};
|
||||
StrVec err_vec;
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.report("currency", commodities, false);
|
||||
quotes.report("currency", commodities, true);
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, comvec_fetch)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"AAPL\":{\"eps\":6.05,\"success\":1,\"year_range\":\" 129.04 - 182.94\",\"currency\":\"USD\",\"exchange\":\"Sourced from Yahoo Finance (as JSON)\",\"volume\":73539475,\"close\":157.22,\"high\":158.39,\"open\":156.64,\"div_yield\":0.5660857,\"last\":157.96,\"isodate\":\"2022-09-01\",\"method\":\"yahoo_json\",\"name\":\"AAPL (Apple Inc.)\",\"pe\":26.10909,\"low\":154.67,\"type\":\"EQUITY\",\"symbol\":\"AAPL\",\"date\":\"09/01/2022\"},"
|
||||
"\"HPE\":{\"symbol\":\"HPE\",\"date\":\"09/01/2022\",\"low\":13.13,\"type\":\"EQUITY\",\"method\":\"yahoo_json\",\"name\":\"HPE (Hewlett Packard Enterprise Comp)\",\"isodate\":\"2022-09-01\",\"pe\":4.7921147,\"last\":13.37,\"high\":13.535,\"close\":13.6,\"open\":13.5,\"div_yield\":3.5294116,\"volume\":16370483,\"exchange\":\"Sourced from Yahoo Finance (as JSON)\",\"currency\":\"USD\",\"year_range\":\" 12.4 - 17.76\",\"eps\":2.79,\"success\":1}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
auto commtable{gnc_commodity_table_get_table(m_book)};
|
||||
auto hpe{gnc_commodity_table_lookup(commtable, "NYSE", "HPE")};
|
||||
auto aapl{gnc_commodity_table_lookup(commtable, "NASDAQ", "AAPL")};
|
||||
CommVec comms{hpe, aapl};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(comms);
|
||||
auto failures{quotes.failures()};
|
||||
EXPECT_TRUE(failures.empty());
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
EXPECT_EQ(2u, gnc_pricedb_get_num_prices(pricedb));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, fetch_one_commodity)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"HPE\":{\"date\":\"09/01/2022\",\"last\":13.37,\"currency\":\"USD\",\"success\":1}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
auto commtable{gnc_commodity_table_get_table(m_book)};
|
||||
auto hpe{gnc_commodity_table_lookup(commtable, "NYSE", "HPE")};
|
||||
auto usd{gnc_commodity_table_lookup(commtable, "ISO4217", "USD")};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(hpe);
|
||||
auto failures{quotes.failures()};
|
||||
EXPECT_TRUE(failures.empty());
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
auto price{gnc_pricedb_lookup_latest(pricedb, hpe, usd)};
|
||||
auto datetime{static_cast<time64>(GncDateTime("20220901160000"))};
|
||||
|
||||
EXPECT_EQ(usd, gnc_price_get_currency(price));
|
||||
EXPECT_EQ(datetime, gnc_price_get_time64(price));
|
||||
EXPECT_EQ(PRICE_SOURCE_FQ, gnc_price_get_source(price));
|
||||
EXPECT_TRUE(gnc_numeric_equal(GncNumeric{1337, 100},
|
||||
gnc_price_get_value(price)));
|
||||
EXPECT_STREQ("Finance::Quote", gnc_price_get_source_string(price));
|
||||
EXPECT_STREQ("last", gnc_price_get_typestr(price));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, fetch_one_currency)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"EUR\":{\"symbol\":\"EUR\",\"currency\":\"USD\",\"success\":\"1\",\"inverted\":0,\"last\":1.0004}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
auto commtable{gnc_commodity_table_get_table(m_book)};
|
||||
auto eur{gnc_commodity_table_lookup(commtable, "ISO4217", "EUR")};
|
||||
auto usd{gnc_commodity_table_lookup(commtable, "ISO4217", "USD")};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(eur);
|
||||
auto failures{quotes.failures()};
|
||||
EXPECT_TRUE(failures.empty());
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
auto price{gnc_pricedb_lookup_latest(pricedb, eur, usd)};
|
||||
EXPECT_EQ(1u, gnc_pricedb_get_num_prices(pricedb));
|
||||
auto datetime{static_cast<time64>(GncDateTime())};
|
||||
|
||||
EXPECT_EQ(usd, gnc_price_get_currency(price));
|
||||
EXPECT_EQ(datetime, gnc_price_get_time64(price));
|
||||
EXPECT_EQ(PRICE_SOURCE_FQ, gnc_price_get_source(price));
|
||||
EXPECT_EQ(10004, gnc_price_get_value(price).num);
|
||||
EXPECT_TRUE(gnc_numeric_equal(GncNumeric{10004, 10000},
|
||||
gnc_price_get_value(price)));
|
||||
EXPECT_STREQ("Finance::Quote", gnc_price_get_source_string(price));
|
||||
EXPECT_STREQ("last", gnc_price_get_typestr(price));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, no_currency)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"HPE\":{\"date\":\"09/01/2022\",\"last\":13.37,\"success\":1}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
auto commtable{gnc_commodity_table_get_table(m_book)};
|
||||
auto hpe{gnc_commodity_table_lookup(commtable, "NYSE", "HPE")};
|
||||
auto usd{gnc_commodity_table_lookup(commtable, "ISO4217", "USD")};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(hpe);
|
||||
auto failures{quotes.failures()};
|
||||
ASSERT_EQ(1u, failures.size());
|
||||
EXPECT_EQ(GncQuoteError::NO_CURRENCY, std::get<2>(failures[0]));
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
EXPECT_EQ(0u, gnc_pricedb_get_num_prices(pricedb));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, bad_currency)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"HPE\":{\"date\":\"09/01/2022\",\"last\":13.37,\"currency\":\"BTC\",\"success\":1}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
auto commtable{gnc_commodity_table_get_table(m_book)};
|
||||
auto hpe{gnc_commodity_table_lookup(commtable, "NYSE", "HPE")};
|
||||
auto usd{gnc_commodity_table_lookup(commtable, "ISO4217", "USD")};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(hpe);
|
||||
auto failures{quotes.failures()};
|
||||
ASSERT_EQ(1u, failures.size());
|
||||
EXPECT_EQ(GncQuoteError::UNKNOWN_CURRENCY, std::get<2>(failures[0]));
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
EXPECT_EQ(0u, gnc_pricedb_get_num_prices(pricedb));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, no_date)
|
||||
{
|
||||
StrVec quote_vec{
|
||||
"{"
|
||||
"\"HPE\":{\"last\":13.37,\"currency\":\"USD\",\"success\":1}"
|
||||
"}"
|
||||
};
|
||||
StrVec err_vec;
|
||||
auto commtable{gnc_commodity_table_get_table(m_book)};
|
||||
auto hpe{gnc_commodity_table_lookup(commtable, "NYSE", "HPE")};
|
||||
auto usd{gnc_commodity_table_lookup(commtable, "ISO4217", "USD")};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
quotes.fetch(hpe);
|
||||
auto failures{quotes.failures()};
|
||||
EXPECT_TRUE(failures.empty());
|
||||
auto pricedb{gnc_pricedb_get_db(m_book)};
|
||||
auto price{gnc_pricedb_lookup_latest(pricedb, hpe, usd)};
|
||||
auto datetime{static_cast<time64>(GncDateTime())};
|
||||
|
||||
EXPECT_EQ(usd, gnc_price_get_currency(price));
|
||||
EXPECT_EQ(datetime, gnc_price_get_time64(price));
|
||||
EXPECT_EQ(PRICE_SOURCE_FQ, gnc_price_get_source(price));
|
||||
EXPECT_TRUE(gnc_numeric_equal(GncNumeric{1337, 100},
|
||||
gnc_price_get_value(price)));
|
||||
EXPECT_STREQ("Finance::Quote", gnc_price_get_source_string(price));
|
||||
EXPECT_STREQ("last", gnc_price_get_typestr(price));
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, test_version)
|
||||
{
|
||||
StrVec quote_vec, err_vec;
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
EXPECT_STREQ("9.99", quotes.version().c_str());
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, test_failure_invalid_json)
|
||||
{
|
||||
StrVec quote_vec, err_vec{"invalid_json\n"};
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncMockQuoteSource>(std::move(quote_vec), std::move(err_vec)));
|
||||
EXPECT_THROW(quotes.fetch(m_book), GncQuoteException);
|
||||
try
|
||||
{
|
||||
quotes.fetch(m_book);
|
||||
}
|
||||
catch (const GncQuoteException& err)
|
||||
{
|
||||
EXPECT_STREQ("GnuCash submitted invalid json to Finance::Quote. The details were logged.\n",
|
||||
err.what());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_F(GncQuotesTest, test_failure_missing_modules)
|
||||
{
|
||||
EXPECT_THROW(GncQuotesImpl quotes(m_book, std::make_unique<GncFailedQuoteSource>()),
|
||||
GncQuoteSourceError);
|
||||
try
|
||||
{
|
||||
GncQuotesImpl quotes(m_book, std::make_unique<GncFailedQuoteSource>());
|
||||
}
|
||||
catch (const GncQuoteSourceError& err)
|
||||
{
|
||||
EXPECT_STREQ("Failed to initialize Finance::Quote: missing_modules Mozilla::CA Try::Tiny",
|
||||
err.what());
|
||||
}
|
||||
|
||||
}
|
@ -287,46 +287,6 @@ gnc_g_list_cut(GList **list, GList *cut_point)
|
||||
cut_point->prev = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gnc_scm_log_warn(const gchar *msg)
|
||||
{
|
||||
g_log("gnc.scm", G_LOG_LEVEL_WARNING, "%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
gnc_scm_log_error(const gchar *msg)
|
||||
{
|
||||
g_log("gnc.scm", G_LOG_LEVEL_CRITICAL, "%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
gnc_scm_log_msg(const gchar *msg)
|
||||
{
|
||||
g_log("gnc.scm", G_LOG_LEVEL_MESSAGE, "%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
gnc_scm_log_debug(const gchar *msg)
|
||||
{
|
||||
g_log("gnc.scm", G_LOG_LEVEL_DEBUG, "%s", msg);
|
||||
}
|
||||
|
||||
void gnc_gpid_kill(GPid pid)
|
||||
{
|
||||
#ifdef G_OS_WIN32
|
||||
if (!TerminateProcess((HANDLE) pid, 0))
|
||||
{
|
||||
gchar *msg = g_win32_error_message(GetLastError());
|
||||
g_warning("Could not kill child process: %s", msg ? msg : "(null)");
|
||||
g_free(msg);
|
||||
}
|
||||
#else /* !G_OS_WIN32 */
|
||||
if (kill(pid, SIGKILL))
|
||||
{
|
||||
g_warning("Could not kill child process: %s", g_strerror(errno));
|
||||
}
|
||||
#endif /* G_OS_WIN32 */
|
||||
}
|
||||
|
||||
gchar *
|
||||
gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep)
|
||||
|
@ -169,20 +169,6 @@ void gnc_g_list_cut(GList **list, GList *cut_point);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name Message Logging
|
||||
@{
|
||||
*/
|
||||
void gnc_scm_log_warn(const gchar *msg);
|
||||
void gnc_scm_log_error(const gchar *msg);
|
||||
void gnc_scm_log_msg(const gchar *msg);
|
||||
void gnc_scm_log_debug(const gchar *msg);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name glib Miscellaneous Functions
|
||||
@{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return a string joining a GList whose elements are gchar*
|
||||
@ -198,7 +184,6 @@ void gnc_scm_log_debug(const gchar *msg);
|
||||
* caller.
|
||||
**/
|
||||
gchar * gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep);
|
||||
|
||||
/**
|
||||
* @brief Scans the GList elements the minimum number of iterations
|
||||
* required to test it against a specified size. Returns -1, 0 or 1
|
||||
@ -213,12 +198,6 @@ gchar * gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep);
|
||||
**/
|
||||
gint gnc_list_length_cmp (const GList *list, size_t len);
|
||||
|
||||
/** Kill a process. On UNIX send a SIGKILL, on Windows call TerminateProcess.
|
||||
*
|
||||
* @param pid The process ID. */
|
||||
void gnc_gpid_kill(GPid pid);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
@ -53,6 +53,7 @@ set (engine_HEADERS
|
||||
gnc-aqbanking-templates.h
|
||||
gnc-budget.h
|
||||
gnc-commodity.h
|
||||
gnc-commodity.hpp
|
||||
gnc-date.h
|
||||
gnc-datetime.hpp
|
||||
gnc-engine.h
|
||||
|
46
libgnucash/engine/gnc-commodity.hpp
Normal file
46
libgnucash/engine/gnc-commodity.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
/**********************************************************************
|
||||
* gnc-commodity.hpp -- API for tradable commodities (incl. currency) *
|
||||
* *
|
||||
* 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: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
* *
|
||||
*********************************************************************/
|
||||
|
||||
/** @addtogroup Engine
|
||||
@{ */
|
||||
/** @addtogroup Commodity Commodities
|
||||
|
||||
@{ */
|
||||
/** @file gnc-commodity.hpp
|
||||
* @brief Commodity handling public routines (C++ api)
|
||||
* @author Copyright (C) 2021 Geert Janssens
|
||||
*/
|
||||
|
||||
#ifndef GNC_COMMODITY_HPP
|
||||
#define GNC_COMMODITY_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
#include <gnc-commodity.h>
|
||||
}
|
||||
|
||||
using CommVec = std::vector<gnc_commodity*>;
|
||||
|
||||
#endif /* GNC_COMMODITY_HPP */
|
||||
/** @} */
|
||||
/** @} */
|
@ -118,7 +118,6 @@ static const char* source_names[(size_t)PRICE_SOURCE_INVALID + 1] =
|
||||
{
|
||||
/* 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. */
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
set(_BIN_FILES "")
|
||||
foreach(file gnc-fq-check.in gnc-fq-helper.in gnc-fq-update.in gnc-fq-dump.in)
|
||||
foreach(file gnc-fq-update.in finance-quote-wrapper.in)
|
||||
string(REPLACE ".in" "" _OUTPUT_FILE_NAME ${file})
|
||||
set(_ABS_OUTPUT_FILE ${BINDIR_BUILD}/${_OUTPUT_FILE_NAME})
|
||||
configure_file( ${file} ${_ABS_OUTPUT_FILE} @ONLY)
|
||||
@ -9,7 +9,7 @@ endforeach(file)
|
||||
|
||||
|
||||
set(_MAN_FILES "")
|
||||
foreach(file gnc-fq-dump gnc-fq-helper)
|
||||
foreach(file finance-quote-wrapper)
|
||||
set(_POD_INPUT ${BINDIR_BUILD}/${file})
|
||||
set(_MAN_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${file}.1)
|
||||
list(APPEND _MAN_FILES ${_MAN_OUTPUT})
|
||||
@ -26,4 +26,4 @@ add_custom_target(quotes-bin ALL DEPENDS ${_BIN_FILES})
|
||||
install(FILES ${_MAN_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
install(PROGRAMS ${_BIN_FILES} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
set_dist_list(quotes_DIST CMakeLists.txt gnc-fq-check.in gnc-fq-dump.in gnc-fq-helper.in gnc-fq-update.in Quote_example.pl README)
|
||||
set_dist_list(quotes_DIST CMakeLists.txt gnc-fq-update.in finance-quote-wrapper.in README)
|
||||
|
@ -1,90 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
##@file
|
||||
# @brief
|
||||
# example script showing how to use the Quote perl module.
|
||||
# gets prices for some stocks, for some mutual funds
|
||||
#
|
||||
# Note that this example uses the meta-level "fetch" command. We do
|
||||
# NOT used that in Gnucash because it's behavior is unpredictable If
|
||||
# the given method/exchange doesn't work, it'll fall back to other
|
||||
# methods, and I've seen no guarantee that all exchanges treat all
|
||||
# symbols the same. So in Gnucash, we use the backend methods
|
||||
# directly, i.e. $quoter->fidelity_direct("IBM", "LNUX");, etc. The
|
||||
# documentation page for each Finance::Quote sub-module describes how
|
||||
# to call it directly without fallbacks.
|
||||
#
|
||||
# @cond PERL
|
||||
|
||||
use Finance::Quote;
|
||||
|
||||
# Create a quote object.
|
||||
my $quoter = Finance::Quote->new();
|
||||
|
||||
# -----------------------------------
|
||||
# get quotes for two stocks ...
|
||||
%quotes = $quoter->fetch("alphavantage","IBM", "SGI");
|
||||
|
||||
# print some selected values
|
||||
print "NYSE by Alphavantage: ", $quotes {"IBM", "name"},
|
||||
" last price: ", $quotes {"IBM", "last"}, "\n";
|
||||
print "NYSE by Alphavantage: ", $quotes {"SGI", "name"},
|
||||
" last price: ", $quotes {"SGI", "last"}, "\n";
|
||||
|
||||
# loop over and print all values.
|
||||
# Notes that values are stored ion a multi-dimensional associative array
|
||||
foreach $k (sort (keys %quotes)) {
|
||||
($sym, $attr) = split ($;, $k, 2);
|
||||
$val = $quotes {$sym, $attr};
|
||||
# $val = $quotes {$k}; # this also works, if desired ...
|
||||
print "\t$sym $attr =\t $val\n";
|
||||
}
|
||||
print "\n\n";
|
||||
|
||||
# -----------------------------------
|
||||
# get quotes from Fidelity Investments
|
||||
@funds = ("FGRIX", "FNMIX", "FASGX", "FCONX");
|
||||
%quotes = $quoter->fetch("fidelity",@funds);
|
||||
|
||||
foreach $f (@funds) {
|
||||
$name = $quotes {$f, "name"};
|
||||
$nav = $quotes {$f, "nav"};
|
||||
print "Fidelity Fund $f $name \tNAV = $nav\n";
|
||||
}
|
||||
print "\n\n";
|
||||
|
||||
# -----------------------------------
|
||||
@funds = ("FGRXX");
|
||||
%quotes = $quoter->fetch("fidelity",@funds);
|
||||
|
||||
print "Not all funds have a NAV; some have Yields:\n";
|
||||
foreach $f (@funds) {
|
||||
$name = $quotes {$f, "name"};
|
||||
$yield = $quotes {$f, "yield"};
|
||||
print "\tFidelity $f $name 30-day Yield = $yield percent\n";
|
||||
}
|
||||
print "\n\n";
|
||||
|
||||
# -----------------------------------
|
||||
# demo T. Rowe Price -- same as above
|
||||
@funds = ("PRFDX", "PRIDX");
|
||||
%quotes = $quoter->fetch("troweprice",@funds);
|
||||
|
||||
foreach $f (@funds) {
|
||||
$nav = $quotes {$f, "nav"};
|
||||
$dayte = $quotes {$f, "date"};
|
||||
print "T. Rowe Price $f NAV = $nav as of $dayte\n";
|
||||
}
|
||||
print "\n\n";
|
||||
|
||||
|
||||
# -----------------------------------
|
||||
|
||||
# demo for ASX. Grab the price of Coles-Myer and Telstra
|
||||
@funds = ("CML","TLS");
|
||||
%quotes = $quoter->fetch("australia",@funds);
|
||||
foreach $f (@funds) {
|
||||
print "ASX Price of $f is ".$quotes{$f,"last"}." at ".
|
||||
$quotes{$f,"date"}."\n";
|
||||
}
|
||||
print "\n\n";
|
||||
##@endcond Perl
|
@ -2,23 +2,11 @@
|
||||
|
||||
This directory contains assorted stock quote scripts.
|
||||
|
||||
gnc-fq-check.in:
|
||||
finance-quote-wrapper.in:
|
||||
|
||||
Source file for gnc-fq-check which is a perl script that allows
|
||||
gnucash to determine if Finance::Quote is installed properly. The
|
||||
responses is a scheme form.
|
||||
|
||||
gnc-fq-dump.in:
|
||||
|
||||
Source file for gnc-fq-dump which is a perl script that retrieves
|
||||
a quote from Finance::Quote and dumps the response to the terminal.
|
||||
Its useful for determining problems with F::Q.
|
||||
|
||||
gnc-fq-helper.in:
|
||||
|
||||
Source file for gnc-fq-helper which is a perl script that
|
||||
allows gnucash to communicate with Finance::Quote over pipes from
|
||||
guile. The requests and responses are scheme forms.
|
||||
Source file for finance-quote-wrapper which is a perl script that
|
||||
allows gnucash to communicate with Finance::Quote.
|
||||
The requests and responses are in json format.
|
||||
|
||||
gnc-fq-update.in:
|
||||
|
||||
|
286
libgnucash/quotes/finance-quote-wrapper.in
Executable file
286
libgnucash/quotes/finance-quote-wrapper.in
Executable file
@ -0,0 +1,286 @@
|
||||
#!@PERL@ -w
|
||||
######################################################################
|
||||
### finance-quote-wrapper - interface file between gnucash and
|
||||
### Finanace::Quote. Only intended to be used
|
||||
### from gnucash code.
|
||||
### Based on code taken from gnc-fq-helper.
|
||||
### Copyright 2001 Rob Browning <rlb@cs.utexas.edu>
|
||||
### Copyright 2021 Geert Janssens
|
||||
###
|
||||
### 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:
|
||||
###
|
||||
### Free Software Foundation Voice: +1-617-542-5942
|
||||
### 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
### Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
######################################################################
|
||||
|
||||
use strict;
|
||||
use English;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
finance-quote-wrapper - internal interface between gnucash and Finance::Quote
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
finance-quote-wrapper
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Input: a JSON encoded hash of namespaces and commodities to query prices for.
|
||||
Currencies all go under the "currency' namespace, other commodities are
|
||||
grouped according to the quotes source they should be queried from
|
||||
There should also be a "defaultcurrency" key with the currency to be used as
|
||||
base currency for currency quotes.
|
||||
|
||||
{
|
||||
"defaultcurrency": "EUR",
|
||||
"currency": {
|
||||
"XAG": "",
|
||||
"HKD": "",
|
||||
"USD": ""
|
||||
},
|
||||
"yahoo_json": {
|
||||
"CSCO": ""
|
||||
}
|
||||
}
|
||||
|
||||
Output (on standard output):
|
||||
|
||||
The retrieved quotes in JSON format for further processing. These are
|
||||
the raw values returned by Finance::Quote. The caller is responsible for
|
||||
parsing and interpreting the results.
|
||||
|
||||
If there are program failures, an error message will be printed on standard error.
|
||||
|
||||
Exit status
|
||||
|
||||
0 - success
|
||||
non-zero - failure
|
||||
|
||||
=cut
|
||||
|
||||
sub check_modules {
|
||||
my @modules = qw(Finance::Quote JSON::Parse Getopt::Std);
|
||||
my @missing;
|
||||
|
||||
foreach my $mod (@modules) {
|
||||
if (eval "require $mod") {
|
||||
$mod->import();
|
||||
}
|
||||
else {
|
||||
push (@missing, $mod);
|
||||
}
|
||||
}
|
||||
|
||||
return unless @missing;
|
||||
|
||||
# Test for STDERR being a tty and output a detailed message if it is
|
||||
# and a short message if it isn't; in the latter case we're probably
|
||||
# being called from GnuCash and it will emit its own localized error.
|
||||
if (-t STDERR)
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "You need to install the following Perl modules:\n";
|
||||
foreach my $mod (@missing) {
|
||||
print STDERR " ".$mod."\n";
|
||||
}
|
||||
|
||||
print STDERR "\n";
|
||||
print STDERR "Please see https://wiki.gnucash.org/wiki/Online_Quotes#Finance::Quote for detailed corrective action.\n";
|
||||
|
||||
print "missing-lib\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print STDERR "missing_modules ", join(" ", @missing), "\n";
|
||||
}
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub print_version {
|
||||
my $quoter = Finance::Quote->new();
|
||||
my @sources = $quoter->sources();
|
||||
print "$Finance::Quote::VERSION\n";
|
||||
foreach my $source (@sources) {
|
||||
print "$source\n";
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub print_usage {
|
||||
if (-t STDERR)
|
||||
{
|
||||
my $message = << 'END';
|
||||
Usage:
|
||||
Check proper installation and version:
|
||||
finance-quote-wrapper -v
|
||||
Fetch quotes (input should be passed as JSON via stdin):
|
||||
finance-quote-wrapper -f
|
||||
END
|
||||
print STDERR $message;
|
||||
}
|
||||
}
|
||||
|
||||
sub sanitize_hash {
|
||||
|
||||
my (%quotehash) = @_;
|
||||
my %newhash;
|
||||
|
||||
my @oldkeys = sort keys %quotehash;
|
||||
|
||||
foreach my $singlekey (@oldkeys) {
|
||||
my ($symbol, $newkey) = split /\x1c/, $singlekey, 2;
|
||||
$newhash{$symbol}{$newkey} = $quotehash{$singlekey};
|
||||
}
|
||||
|
||||
return %newhash;
|
||||
}
|
||||
|
||||
sub parse_currencies {
|
||||
my($quoter, $currencies, $to_currency) = @_;
|
||||
|
||||
return unless $to_currency;
|
||||
|
||||
my %results;
|
||||
foreach my $from_currency (keys %$currencies) {
|
||||
|
||||
next unless $from_currency;
|
||||
|
||||
my $price = $quoter->currency($from_currency, $to_currency);
|
||||
my $inv_price = undef;
|
||||
my $inverted = 0;
|
||||
# Sometimes price quotes are available in only one direction.
|
||||
unless (defined($price)) {
|
||||
$inv_price = $quoter->currency($to_currency, $from_currency);
|
||||
if (defined($inv_price)) {
|
||||
$price = $inv_price;
|
||||
$inverted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$results{$from_currency}{"success"} = defined($price);
|
||||
$results{$from_currency}{"inverted"} = $inverted;
|
||||
$results{$from_currency}{"symbol"} = $from_currency;
|
||||
$results{$from_currency}{"currency"} = $to_currency;
|
||||
$results{$from_currency}{"last"} = $price;
|
||||
}
|
||||
return %results;
|
||||
}
|
||||
|
||||
sub parse_commodities {
|
||||
my($quoter, $quote_method_name, $commodities) = @_;
|
||||
|
||||
my %quote_data = $quoter->fetch($quote_method_name, keys %$commodities);
|
||||
my %normalized_quote_data = sanitize_hash(%quote_data);
|
||||
return %normalized_quote_data;
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Runtime.
|
||||
|
||||
# Check for and load non-standard modules
|
||||
check_modules ();
|
||||
|
||||
my %opts;
|
||||
my $status = getopts('hvf', \%opts);
|
||||
if (!$status)
|
||||
{
|
||||
print_usage();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if (exists $opts{'v'})
|
||||
{
|
||||
print_version();
|
||||
}
|
||||
elsif (exists $opts{'h'})
|
||||
{
|
||||
print_usage();
|
||||
exit 0;
|
||||
}
|
||||
elsif (!exists $opts{'f'})
|
||||
{
|
||||
print_usage();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
JSON::Parse->import(qw(valid_json parse_json));
|
||||
|
||||
my $json_input = do { local $/; <STDIN> };
|
||||
|
||||
if (!valid_json($json_input)) {
|
||||
if (-t STDERR)
|
||||
{
|
||||
print STDERR "Could not parse input as valid JSON.\n";
|
||||
print STDERR "Received input:\n$json_input\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print STDERR "invalid_json\n";
|
||||
}
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $requests = parse_json ($json_input);
|
||||
|
||||
my $defaultcurrency = $$requests{'defaultcurrency'};
|
||||
# This shouldn't be possible if we're called from GnuCash, so only warn in interactive use.
|
||||
if (!$defaultcurrency) {
|
||||
$defaultcurrency = "USD";
|
||||
if (-t STDERR)
|
||||
{
|
||||
print STDERR "Warning: no default currency was specified, assuming 'USD'\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Create a stockquote object.
|
||||
my $quoter = Finance::Quote->new();
|
||||
my $prgnam = "gnc-fq-helper";
|
||||
|
||||
# Disable default currency conversions.
|
||||
$quoter->set_currency();
|
||||
|
||||
my $key;
|
||||
my $values;
|
||||
my %results;
|
||||
while (($key, $values) = each %$requests)
|
||||
{
|
||||
next if ($key eq "defaultcurrency");
|
||||
if ($key eq "currency") {
|
||||
my %curr_results = parse_currencies ($quoter, $values, $defaultcurrency, %results);
|
||||
if (%curr_results) {
|
||||
%results = (%results, %curr_results);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my %comm_results = parse_commodities ($quoter, $key, $values, %results);
|
||||
if (%comm_results) {
|
||||
%results = (%results, %comm_results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (%results) {
|
||||
use JSON;
|
||||
my $jsonval = encode_json \%results;
|
||||
print "$jsonval\n";
|
||||
}
|
||||
|
||||
STDOUT->flush();
|
||||
|
||||
## Local Variables:
|
||||
## mode: perl
|
||||
## End:
|
@ -1,103 +0,0 @@
|
||||
#!@PERL@ -w
|
||||
######################################################################
|
||||
### gnc-fq-check - check for the presence of Finance::Quote
|
||||
### From gnc-fq-helper.
|
||||
### 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.
|
||||
###
|
||||
### You should have received a copy of the GNU General Public License
|
||||
### along with this program# if not, contact:
|
||||
###
|
||||
### Free Software Foundation Voice: +1-617-542-5942
|
||||
### 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
### Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
######################################################################
|
||||
|
||||
use strict;
|
||||
use English;
|
||||
use FileHandle;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
gnc-fq-check - check for the presence of Finance::Quote
|
||||
From gnc-fq-helper
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
gnc-fq-check
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Input: <none>
|
||||
|
||||
Output (on standard output, one output form per input line):
|
||||
|
||||
A list of quote sources supported by Finance::Quote, or the single
|
||||
term missing-lib if finance quote could not be executed.
|
||||
|
||||
Exit status
|
||||
|
||||
0 - success
|
||||
non-zero - failure
|
||||
|
||||
=cut
|
||||
|
||||
sub check_modules {
|
||||
my @modules = qw(Finance::Quote);
|
||||
my @missing;
|
||||
|
||||
foreach my $mod (@modules) {
|
||||
if (eval "require $mod") {
|
||||
$mod->import();
|
||||
}
|
||||
else {
|
||||
push (@missing, $mod);
|
||||
}
|
||||
}
|
||||
|
||||
return unless @missing;
|
||||
|
||||
print STDERR "\n";
|
||||
print STDERR "You need to install the following Perl modules:\n";
|
||||
foreach my $mod (@missing) {
|
||||
print STDERR " ".$mod."\n";
|
||||
}
|
||||
|
||||
print STDERR "\n";
|
||||
print STDERR "Use your system's package manager to install them,\n";
|
||||
print STDERR "or run 'gnc-fq-update' as root.\n";
|
||||
|
||||
print "missing-lib\n";
|
||||
|
||||
exit 1;
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Runtime.
|
||||
|
||||
# Check for and load non-standard modules
|
||||
check_modules ();
|
||||
|
||||
# Create a stockquote object.
|
||||
my $quoter = Finance::Quote->new();
|
||||
my $prgnam = "gnc-fq-check";
|
||||
|
||||
my @qsources;
|
||||
my @sources = $quoter->sources();
|
||||
foreach my $source (@sources) {
|
||||
push(@qsources, "\"$source\"");
|
||||
}
|
||||
printf "(\"%s\" %s)\n", $Finance::Quote::VERSION, join(" ", sort(@qsources));
|
||||
|
||||
## Local Variables:
|
||||
## mode: perl
|
||||
## End:
|
@ -1,242 +0,0 @@
|
||||
#!@PERL@ -w
|
||||
#
|
||||
# Copyright (C) 2003, David Hampton <hampton@employees.org>
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
|
||||
use strict;
|
||||
|
||||
sub check_modules {
|
||||
my @modules = qw(Finance::Quote);
|
||||
my @missing;
|
||||
|
||||
foreach my $mod (@modules) {
|
||||
if (eval "require $mod") {
|
||||
$mod->import();
|
||||
}
|
||||
else {
|
||||
push (@missing, $mod);
|
||||
}
|
||||
}
|
||||
|
||||
return unless @missing;
|
||||
|
||||
print STDERR "$0 cannot find all the Perl modules needed to run.\n";
|
||||
print STDERR "You need to install the following Perl modules:\n";
|
||||
foreach my $mod (@missing) {
|
||||
print STDERR " ".$mod."\n";
|
||||
}
|
||||
print STDERR "Use your system's package manager to install them,\n";
|
||||
print STDERR "or run 'gnc-fq-update' as root.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub report {
|
||||
my($itemname, $qh, $verbose) = @_;
|
||||
my ($symbol, $date, $currency, $last, $nav, $price, $timezone, $keyname);
|
||||
my($gccanuse, $gcshoulduse) = (1, 1);
|
||||
|
||||
# Sanity check returned results
|
||||
if ((keys %$qh) < 1) {
|
||||
printf("No results found for stock $itemname.\n");
|
||||
return;
|
||||
} else {
|
||||
my ($stock, $attribute, %seen, $first);
|
||||
|
||||
foreach $keyname (sort keys %$qh) {
|
||||
($stock, $attribute) = split('\034', $keyname);
|
||||
last if $stock eq $itemname;
|
||||
$first = $stock if !defined $first;
|
||||
$seen{$stock} = 1;
|
||||
}
|
||||
|
||||
if ($stock ne $itemname) {
|
||||
printf "\nNo results found for stock $itemname, but results were returned for\n";
|
||||
printf "the stock(s) %s. ", join(", ", keys(%seen));
|
||||
printf "Printing data for the first stock returned.\n\n";
|
||||
|
||||
# Print stats for the first stock returned.
|
||||
$itemname = $first;
|
||||
}
|
||||
}
|
||||
|
||||
# Parse the quote fields and put warnings where necessary.
|
||||
if (defined($$qh{$itemname, "symbol"})) {
|
||||
$symbol = $$qh{$itemname, "symbol"};
|
||||
} else {
|
||||
$symbol = "$itemname (deduced)";
|
||||
$gccanuse = 0;
|
||||
}
|
||||
if (defined($$qh{$itemname, "date"})) {
|
||||
$date = $$qh{$itemname, "date"};
|
||||
} else {
|
||||
$date = "** missing **";
|
||||
$gcshoulduse = 0;
|
||||
}
|
||||
if (defined($$qh{$itemname, "currency"})) {
|
||||
$currency = $$qh{$itemname, "currency"};
|
||||
} else {
|
||||
$currency = "** missing **";
|
||||
$gccanuse = 0;
|
||||
}
|
||||
if ((!defined($$qh{$itemname, "last"})) &&
|
||||
(!defined($$qh{$itemname, "nav" })) &&
|
||||
(!defined($$qh{$itemname, "price"}))) {
|
||||
$$qh{$itemname, "last"} = "**missing**";
|
||||
$$qh{$itemname, "nav"} = "**missing**";
|
||||
$$qh{$itemname, "price"} = "**missing**";
|
||||
$gccanuse = 0;
|
||||
}
|
||||
$last = defined($$qh{$itemname, "last"})
|
||||
? $$qh{$itemname, "last"} : "";
|
||||
$nav = defined($$qh{$itemname, "nav"})
|
||||
? $$qh{$itemname, "nav"} : "";
|
||||
$price = defined($$qh{$itemname, "price"})
|
||||
? $$qh{$itemname, "price"} : "";
|
||||
$timezone = defined($$qh{$itemname, "timezone"})
|
||||
? $$qh{$itemname, "timezone"} : "";
|
||||
|
||||
# Dump gnucash recognized fields
|
||||
printf "Finance::Quote fields Gnucash uses:\n";
|
||||
printf " symbol: %-20s <=== required\n", $symbol;
|
||||
printf " date: %-20s <=== recommended\n", $date;
|
||||
printf " currency: %-20s <=== required\n", $currency;
|
||||
printf " last: %-20s <=\\\n", $last;
|
||||
printf " nav: %-20s <=== one of these\n", $nav;
|
||||
printf " price: %-20s <=/\n", $price;
|
||||
printf " timezone: %-20s <=== optional\n", $timezone;
|
||||
|
||||
# Report failure
|
||||
if ($gccanuse == 0) {
|
||||
printf "\n** This stock quote cannot be used by GnuCash!\n\n";
|
||||
} elsif ($gcshoulduse == 0) {
|
||||
printf "\n** This quote will have today's date, which might be incorrect.\n";
|
||||
printf " GnuCash will use it, but you might prefer that it doesn't.\n\n";
|
||||
}
|
||||
# Dump all fields if requested
|
||||
if ($verbose) {
|
||||
printf "\nAll fields returned by Finance::Quote for stock $itemname\n\n";
|
||||
printf "%-10s %10s %s\n", "stock", "field", "value";
|
||||
printf "%-10s %10s %s\n", "-----", "-----", "-----";
|
||||
foreach $keyname (sort keys %$qh) {
|
||||
my ($stock, $key) = split('\034', $keyname);
|
||||
printf "%-10s %10s: %s\n", $stock, $key, $$qh{$stock, $key};
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub chk_api_key {
|
||||
my $exch = $_[0];
|
||||
my $url = " https://wiki.gnucash.org/wiki/Online_Quotes#Source_Alphavantage.2C_US\n";
|
||||
if (($exch eq "currency") || ($exch eq "alphavantage")
|
||||
|| ($exch eq "vanguard")) {
|
||||
die "ERROR: ALPHAVANTAGE_API_KEY *must* be set for currency quotes and\n" .
|
||||
"stock quotes with source 'alphavantage' or 'vanguard'; see\n" . $url
|
||||
unless (defined ($ENV{'ALPHAVANTAGE_API_KEY'}));
|
||||
}
|
||||
if (($exch eq "canada") || ($exch eq "nasdaq")
|
||||
|| ($exch eq "nyse") || ($exch eq "usa")) {
|
||||
printf("WARNING: Multiple Source '%s' will not be able to use alphavantage " .
|
||||
"unless ALPHAVANTAGE_API_KEY is set; see\n%s", $exch, $url)
|
||||
unless (defined ($ENV{'ALPHAVANTAGE_API_KEY'}));
|
||||
}
|
||||
}
|
||||
|
||||
############## end of functions - start mainline #########################
|
||||
|
||||
# Check for and load non-standard modules
|
||||
check_modules ();
|
||||
|
||||
my $q = Finance::Quote->new;
|
||||
$q->timeout(60);
|
||||
|
||||
if ($#ARGV < 1) {
|
||||
my @sources = sort $q->sources();
|
||||
printf "\nUsage: $0 [-v] <quote-source> <stock> [<stock> ...]\n\n";
|
||||
printf "-v: verbose\n";
|
||||
printf "Available sources are:\n %s\n\n", join(' ', @sources);
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $verbose = 0;
|
||||
if ($ARGV[0] eq "-v") {
|
||||
$verbose = 1;
|
||||
shift;
|
||||
}
|
||||
|
||||
my $exchange = shift;
|
||||
chk_api_key ($exchange);
|
||||
if ($exchange eq "currency") {
|
||||
my $from = shift;
|
||||
while ($#ARGV >= 0) {
|
||||
my $to = shift;
|
||||
my $result = $q->currency($from, $to);
|
||||
# Sometimes quotes are available in only one direction.
|
||||
# If we didn't get the one we wanted try the reverse quote
|
||||
unless (defined($result)) {
|
||||
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 {
|
||||
printf "1 $from = <unknown> $to\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ($#ARGV >= 0) {
|
||||
my $stock = shift;
|
||||
my %quotes = $q->fetch($exchange, $stock);
|
||||
report($stock, \%quotes, $verbose);
|
||||
if ($#ARGV >= 0) {
|
||||
printf "=====\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=head1 NAME
|
||||
|
||||
gnc-fq-dump - Print out data from the F::Q module
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Currency Exchange Rates
|
||||
gnc-fq-dump currency USD AUD
|
||||
gnc-fq-dump [-v] yahoo_json USDEUR=X
|
||||
Stock Quotes
|
||||
gnc-fq-dump [-v] alphavantage CSCO JNPR
|
||||
gnc-fq-dump [-v] alphavantage BAESY.PK
|
||||
gnc-fq-dump [-v] yahoo_json CBA.AX
|
||||
gnc-fq-dump [-v] europe 48406.PA 13000.PA
|
||||
gnc-fq-dump [-v] vwd 632034
|
||||
gnc-fq-dump [-v] ftportfolios FKYGTX
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This program obtains information from Finance::Quote about any
|
||||
specified stock, and then dumps it to the screen in annotated form.
|
||||
This will allow someone to see what is returned, and whether it
|
||||
provides all the information needed by Gnucash.
|
||||
|
||||
=cut
|
@ -1,435 +0,0 @@
|
||||
#!@PERL@ -w
|
||||
######################################################################
|
||||
### gnc-fq-helper - present a scheme interface to Finance::Quote
|
||||
### 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.
|
||||
###
|
||||
### You should have received a copy of the GNU General Public License
|
||||
### along with this program# if not, contact:
|
||||
###
|
||||
### Free Software Foundation Voice: +1-617-542-5942
|
||||
### 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
### Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
######################################################################
|
||||
|
||||
use strict;
|
||||
use English;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
gnc-fq-helper - allows gnucash to communicate with Finance::Quote
|
||||
over pipes from guile. The requests and responses
|
||||
are scheme forms.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
gnc-fq-helper
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Input: (on standard input - one entry per line and one line per
|
||||
entry, and double quotes must only be delimiters, not string
|
||||
content -- remember, we don't have a real scheme parser on the perl
|
||||
side :>).
|
||||
|
||||
(<method-name> symbol symbol symbol ...)
|
||||
|
||||
where <method-name> indicates the desired Finance::Quote method.
|
||||
One can list the many methods by running gnc-fq-check.
|
||||
|
||||
For currency quotes, the symbols alternate between the 'from'
|
||||
and 'to' currencies.
|
||||
|
||||
For example:
|
||||
|
||||
(alphavantage "IBM" "LNUX")
|
||||
(fidelity_direct "FBIOX" "FSELX")
|
||||
(currency "USD" "AUD")
|
||||
|
||||
Output (on standard output, one output form per input line):
|
||||
|
||||
Schemified version of gnc-fq's output, basically an alist of
|
||||
alists, as in the example below. Right now, only the fields that
|
||||
this script knows about (and knows how to convert to scheme) are
|
||||
returned, so the conversion function will have to be updated
|
||||
whenever Finance::Quote changes. Currently you'll get symbol,
|
||||
gnc:time-no-zone, and currency, and either last, nav, or price.
|
||||
Fields with gnc: prefixes are non-Finance::Quote fields.
|
||||
gnc:time-no-zone is returned as a string of the form "YYYY-MM-DD
|
||||
HH:MM:SS", basically the unmolested (and underspecified) output of
|
||||
the quote source. It's up to you to know what it's proper timezone
|
||||
really is. i.e. if you know the time was in America/Chicago, you'll
|
||||
need to convert it to that.
|
||||
|
||||
For example:
|
||||
|
||||
$ echo '(alphavantage "CSCO" "JDSU" "^IXIC")' | ./gnc-fq-helper
|
||||
(("CSCO" (symbol . "CSCO")
|
||||
(gnc:time-no-zone . "2001-03-13 19:27:00")
|
||||
(last . 20.375)
|
||||
(currency . "USD"))
|
||||
("JDSU" (symbol . "JDSU")
|
||||
(gnc:time-no-zone . "2001-03-13 19:27:00")
|
||||
(last . 23.5625)
|
||||
(currency . "USD"))
|
||||
("^IXIC" (symbol . ^IXIC)
|
||||
(gnc:time-no-zone . 2002-12-04 17:16:00)
|
||||
(last . 1430.35)
|
||||
(currency . failed-conversion)))
|
||||
|
||||
On error, the overall result may be #f, or on individual errors, the
|
||||
list sub-item for a given symbol may be #f, like this:
|
||||
|
||||
$ echo '(alphavantage "CSCO" "JDSU")' | ./gnc-fq-helper
|
||||
(#f
|
||||
("JDSU" (symbol . "JDSU")
|
||||
(gnc:time-no-zone . "2001-03-13 19:27:00")
|
||||
(last . 23.5625)
|
||||
(currency . "USD")))
|
||||
|
||||
further, errors may be stored with each quote as indicated in
|
||||
Finance::Quote, and whenever the conversion to scheme data fails,
|
||||
the field will have the value 'failed-conversion, and accordingly
|
||||
this symbol will never be a legitimate conversion.
|
||||
|
||||
Exit status
|
||||
|
||||
0 - success
|
||||
non-zero - failure
|
||||
|
||||
=cut
|
||||
|
||||
# The methods we know about. For now we assume they all have the same
|
||||
# signature so this works OK.
|
||||
|
||||
sub check_modules {
|
||||
|
||||
# Date::Manip provides ParseDate, ParseDateString, and UnixTime.
|
||||
|
||||
my @modules = qw(FileHandle Finance::Quote Date::Manip);
|
||||
my @missing;
|
||||
|
||||
foreach my $mod (@modules) {
|
||||
if (eval "require $mod") {
|
||||
$mod->import();
|
||||
}
|
||||
else {
|
||||
push (@missing, $mod);
|
||||
}
|
||||
}
|
||||
|
||||
return unless @missing;
|
||||
|
||||
print STDERR "\n";
|
||||
print STDERR "You need to install the following Perl modules:\n";
|
||||
foreach my $mod (@missing) {
|
||||
print STDERR " ".$mod."\n";
|
||||
}
|
||||
|
||||
print STDERR "\n";
|
||||
print STDERR "Use your system's package manager to install them,\n";
|
||||
print STDERR "or run 'gnc-fq-update' as root.\n";
|
||||
|
||||
print "missing-lib";
|
||||
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Check for and load non-standard modules
|
||||
check_modules ();
|
||||
|
||||
# Set a base date with the current time in the current TZ:
|
||||
my $base_date = new Date::Manip::Date;
|
||||
$base_date->parse("now");
|
||||
|
||||
sub schemify_string {
|
||||
my($str) = @_;
|
||||
|
||||
if(!$str) { return "failed-conversion"; }
|
||||
|
||||
# FIXME: Is this safe? Can we just double all backslashes and backslash
|
||||
# escape all double quotes and get the right answer?
|
||||
|
||||
# double all backslashes.
|
||||
my $bs = "\\";
|
||||
$str =~ s/$bs$bs/$bs$bs/gmo;
|
||||
|
||||
# escape all double quotes.
|
||||
# Have to do this because the perl-mode parser freaks out otherwise.
|
||||
my $dq = '"';
|
||||
$str =~ s/$dq/$bs$dq/gmo;
|
||||
return '"' . $str . '"';
|
||||
}
|
||||
|
||||
sub schemify_boolean {
|
||||
my($bool) = @_;
|
||||
|
||||
if($bool) {
|
||||
return "#t";
|
||||
} else {
|
||||
return "#f";
|
||||
}
|
||||
}
|
||||
|
||||
sub schemify_num {
|
||||
my($numstr) = @_;
|
||||
# This is for normal numbers, not the funny ones like "2.346B".
|
||||
# For now we don't need to do anything.
|
||||
|
||||
if(!$numstr) { return "failed-conversion"; }
|
||||
|
||||
if($numstr =~ /^\s*(\d+(\.\d+)?([eE][+-]?\d+)?)$/o) {
|
||||
return "#e" . $1;
|
||||
} else {
|
||||
return "failed-conversion";
|
||||
}
|
||||
}
|
||||
|
||||
# sub schemify_range {
|
||||
# #convert range in form ``num1 - num2'' to ``(num1 num2)''.
|
||||
# }
|
||||
|
||||
sub get_quote_time {
|
||||
# return the date.
|
||||
my ($item, $quotehash) = @_;
|
||||
|
||||
my $datestr = $$quotehash{$item, 'date'};
|
||||
my $timestr = $$quotehash{$item, 'time'};
|
||||
my $format = "%Y-%m-%d %H:%M:%S";
|
||||
my $result;
|
||||
|
||||
if ($datestr) {
|
||||
my $parsestr = $datestr . " " . ($timestr ? $timestr : "12:00:00");
|
||||
my $date = $base_date->new();
|
||||
my $err = $date->parse($parsestr);
|
||||
if ($err) {
|
||||
print $date->err(), " $parsestr\n";
|
||||
$result = $base_date->printf($format);
|
||||
}
|
||||
else {
|
||||
$result = $date->printf($format);
|
||||
}
|
||||
} else {
|
||||
$result = $base_date->printf($format);
|
||||
}
|
||||
return("\"$result\"");
|
||||
}
|
||||
|
||||
sub schemify_quote {
|
||||
my($itemname, $quotehash, $indentlevel) = @_;
|
||||
my $scmname = schemify_string($itemname);
|
||||
my $quotedata = "";
|
||||
my $field;
|
||||
my $data;
|
||||
|
||||
if (!$$quotehash{$itemname, "success"}) {
|
||||
return schemify_boolean(0);
|
||||
}
|
||||
|
||||
$field = 'symbol';
|
||||
if (($$quotehash{$itemname, $field})) {
|
||||
$data = schemify_string($$quotehash{$itemname, $field});
|
||||
} else {
|
||||
# VWD and a few others don't set the symbol field
|
||||
$data = schemify_string($itemname);
|
||||
}
|
||||
$quotedata .= "($field . $data)";
|
||||
|
||||
$field = 'gnc:time-no-zone';
|
||||
$data = get_quote_time($itemname, $quotehash);
|
||||
$quotedata .= " ($field . $data)" if $data;
|
||||
|
||||
$field = 'last';
|
||||
if (!($$quotehash{$itemname, $field})) {
|
||||
$field = 'nav';
|
||||
}
|
||||
if (!($$quotehash{$itemname, $field})) {
|
||||
$field = 'price';
|
||||
}
|
||||
|
||||
$data = schemify_num($$quotehash{$itemname, $field});
|
||||
$quotedata .= " ($field . $data)";
|
||||
|
||||
$field = 'currency';
|
||||
$data = schemify_string($$quotehash{$itemname, $field});
|
||||
$quotedata .= " ($field . $data)";
|
||||
|
||||
return "($scmname $quotedata)";
|
||||
}
|
||||
|
||||
sub schemify_quotes {
|
||||
my($symbols, $quotehash) = @_;
|
||||
my $resultstr = "";
|
||||
my $sym;
|
||||
my $separator = "";
|
||||
|
||||
# we have to pass in @$items because Finance::Quote just uses the
|
||||
# mangled "$name$field string as the key, so there's no way (I know
|
||||
# of) to find out which stocks are in a given quotehash, just given
|
||||
# the quotehash.
|
||||
|
||||
foreach $sym (@$symbols) {
|
||||
$resultstr .= $separator . schemify_quote($sym, $quotehash, 2);
|
||||
if(!$separator) { $separator = "\n "; }
|
||||
}
|
||||
return "($resultstr)\n";
|
||||
}
|
||||
|
||||
sub parse_input_line {
|
||||
|
||||
# FIXME: we need to rewrite parsing to handle commands modularly.
|
||||
# Right now all we do is hard-code "fetch".
|
||||
|
||||
my($input) = @_;
|
||||
# Have to do this because the perl-mode parser freaks out otherwise.
|
||||
my $dq = '"';
|
||||
my @symbols;
|
||||
|
||||
# Make sure we have an opening ( preceded only by whitespace.
|
||||
# and followed by a one word method name composed of [a-z_]+.
|
||||
# Also allow the '.' and '^' characters for stock indices.
|
||||
# Kill off the whitespace if we do and grab the command.
|
||||
if($input !~ s/^\s*\(\s*([\.\^a-z_]+)\s+//o) { return 0; }
|
||||
|
||||
my $quote_method_name = $1;
|
||||
|
||||
# Make sure we have an ending ) followed only by whitespace
|
||||
# and kill it off if we do...
|
||||
if($input !~ s/\s*\)\s*$//o) { return 0; }
|
||||
|
||||
while($input) {
|
||||
# Items should look like "RHAT"
|
||||
# Grab RHAT and delete "RHAT"\s*
|
||||
if($input !~ s/^$dq([^$dq]+)$dq\s*//o) { return 0; }
|
||||
my $symbol = $1;
|
||||
push @symbols, $symbol;
|
||||
}
|
||||
|
||||
my @result = ($quote_method_name, \@symbols);
|
||||
return \@result;
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Runtime.
|
||||
|
||||
# Create a stockquote object.
|
||||
my $quoter = Finance::Quote->new();
|
||||
my $prgnam = "gnc-fq-helper";
|
||||
|
||||
# Disable default currency conversions.
|
||||
$quoter->set_currency();
|
||||
|
||||
while(<>) {
|
||||
|
||||
my $result = parse_input_line($_);
|
||||
|
||||
if(!$result) {
|
||||
print STDERR "$prgnam: bad input line ($_)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my($quote_method_name, $symbols) = @$result;
|
||||
my %quote_data;
|
||||
|
||||
if($quote_method_name =~ m/^currency$/) {
|
||||
my ($from_currency, $to_currency) = @$symbols;
|
||||
|
||||
last unless $from_currency;
|
||||
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.
|
||||
unless (defined($price)) {
|
||||
$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;
|
||||
$quote_data{$from_currency, "currency"} = $to_currency;
|
||||
$quote_data{$from_currency, "last"} = $price;
|
||||
|
||||
my @new_symbols = ($from_currency);
|
||||
$symbols = \@new_symbols;
|
||||
} else {
|
||||
%quote_data = $quoter->fetch($quote_method_name, @$symbols);
|
||||
}
|
||||
|
||||
if (%quote_data) {
|
||||
print schemify_quotes($symbols, \%quote_data);
|
||||
} else {
|
||||
print "#f\n";
|
||||
}
|
||||
|
||||
STDOUT->flush();
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
__END__
|
||||
|
||||
# Keep this around in case we need to go back to complex per-symbol args.
|
||||
#
|
||||
# while($input) {
|
||||
# # Items should look like "RHAT" "EST")
|
||||
# # Grab RHAT and delete ("RHAT"\s*
|
||||
# if($input !~ s/^\(\s*$dq([^$dq]+)$dq\s*//o) { return 0; }
|
||||
# my $symbol = $1;
|
||||
# my $timezone;
|
||||
# # Now grab EST or #f and delete \s*"EST") or #f)
|
||||
# if($input =~ s/^\s*$dq([^$dq]+)$dq\)\s*//o) {
|
||||
# $timezone = $1;
|
||||
# } else {
|
||||
# if($input =~ s/^\s*(\#f)\)\s*//o) {
|
||||
# $timezone = 0;
|
||||
# } else {
|
||||
# return 0;
|
||||
# }
|
||||
# }
|
||||
|
||||
# sub get_quote_utc {
|
||||
# # return the date in utc epoch seconds, using $timezone if specified.
|
||||
# my ($item, $timezone, $quotehash) = @_;
|
||||
|
||||
# if(!defined($timezone)) { return "failed-conversion"; }
|
||||
|
||||
# my $datestr = $$quotehash{$item, 'date'};
|
||||
# my $timestr = $$quotehash{$item, 'time'};
|
||||
|
||||
# if(!$datestr) {
|
||||
# return "failed-conversion";
|
||||
# }
|
||||
# my $parsestr = $datestr;
|
||||
# if($timestr) {
|
||||
# $parsestr .= " $timestr";
|
||||
# }
|
||||
|
||||
# if($timezone) {
|
||||
# # Perform a conversion.
|
||||
# $parsestr = Date_ConvTZ(ParseDate($parsestr), $timezone, 'UTC');
|
||||
# }
|
||||
# my $result = UnixDate($parsestr, "%s");
|
||||
# if($result !~ /^(\+|-)?\d+$/) {
|
||||
# $result = "failed-conversion";
|
||||
# }
|
||||
# return $result;
|
||||
# }
|
||||
|
||||
## Local Variables:
|
||||
## mode: perl
|
||||
## End:
|
@ -37,7 +37,6 @@ if ($( != 0) {
|
||||
}
|
||||
|
||||
CPAN::Shell->install('Test2'); #Required by an F::Q dependency but cpan doesn't notice.
|
||||
CPAN::Shell->install('Date::Manip'); #Required by gnc-fq-helper
|
||||
CPAN::Shell->install('Finance::Quote');
|
||||
|
||||
## Local Variables:
|
||||
|
@ -78,7 +78,7 @@ gnucash/gnome/dialog-lot-viewer.c
|
||||
gnucash/gnome/dialog-new-user.c
|
||||
gnucash/gnome/dialog-order.c
|
||||
gnucash/gnome/dialog-payment.c
|
||||
gnucash/gnome/dialog-price-edit-db.c
|
||||
gnucash/gnome/dialog-price-edit-db.cpp
|
||||
gnucash/gnome/dialog-price-editor.c
|
||||
gnucash/gnome/dialog-print-check.c
|
||||
gnucash/gnome/dialog-progress.c
|
||||
@ -141,7 +141,7 @@ gnucash/gnome-utils/dialog-query-view.c
|
||||
gnucash/gnome-utils/dialog-reset-warnings.c
|
||||
gnucash/gnome-utils/dialog-tax-table.c
|
||||
gnucash/gnome-utils/dialog-totd.c
|
||||
gnucash/gnome-utils/dialog-transfer.c
|
||||
gnucash/gnome-utils/dialog-transfer.cpp
|
||||
gnucash/gnome-utils/dialog-userpass.c
|
||||
gnucash/gnome-utils/dialog-utils.c
|
||||
gnucash/gnome-utils/gnc-account-sel.c
|
||||
@ -374,7 +374,6 @@ gnucash/import-export/qif-imp/qif-parse.scm
|
||||
gnucash/import-export/qif-imp/qif-to-gnc.scm
|
||||
gnucash/import-export/qif-imp/qif-utils.scm
|
||||
gnucash/import-export/qif-imp/string.scm
|
||||
gnucash/price-quotes.scm
|
||||
gnucash/python/gncmod-python.c
|
||||
gnucash/python/init.py
|
||||
gnucash/python/pycons/console.py
|
||||
@ -516,6 +515,7 @@ libgnucash/app-utils/gnc-exp-parser.c
|
||||
libgnucash/app-utils/gnc-gsettings.cpp
|
||||
libgnucash/app-utils/gnc-help-utils.c
|
||||
libgnucash/app-utils/gnc-prefs-utils.c
|
||||
libgnucash/app-utils/gnc-quotes.cpp
|
||||
libgnucash/app-utils/gnc-state.c
|
||||
libgnucash/app-utils/gnc-sx-instance-model.c
|
||||
libgnucash/app-utils/gnc-ui-balances.c
|
||||
|
Loading…
Reference in New Issue
Block a user