mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Merge branch 'prices-in' of https://github.com/Bob-IT/gnucash into unstable
This commit is contained in:
commit
3f44552e36
@ -10,15 +10,20 @@ SET(csv_import_remote_SOURCES
|
||||
SET(csv_import_SOURCES
|
||||
gncmod-csv-import.c
|
||||
assistant-csv-account-import.c
|
||||
assistant-csv-price-import.cpp
|
||||
assistant-csv-trans-import.cpp
|
||||
gnc-plugin-csv-import.c
|
||||
csv-account-import.c
|
||||
gnc-csv-account-map.c
|
||||
gnc-csv-gnumeric-popup.c
|
||||
gnc-csv-tokenizer.cpp
|
||||
gnc-csv-trans-settings.cpp
|
||||
gnc-csv-import-settings.cpp
|
||||
gnc-csv-price-import-settings.cpp
|
||||
gnc-csv-trans-import-settings.cpp
|
||||
gnc-dummy-tokenizer.cpp
|
||||
gnc-fw-tokenizer.cpp
|
||||
gnc-price-import.cpp
|
||||
gnc-price-props.cpp
|
||||
gnc-tokenizer.cpp
|
||||
gnc-trans-props.cpp
|
||||
gnc-tx-import.cpp
|
||||
@ -35,15 +40,20 @@ SET(csv_import_remote_HEADERS
|
||||
|
||||
SET(csv_import_noinst_HEADERS
|
||||
assistant-csv-account-import.h
|
||||
assistant-csv-price-import.h
|
||||
assistant-csv-trans-import.h
|
||||
gnc-plugin-csv-import.h
|
||||
csv-account-import.h
|
||||
gnc-csv-account-map.h
|
||||
gnc-csv-gnumeric-popup.h
|
||||
gnc-csv-tokenizer.hpp
|
||||
gnc-csv-trans-settings.hpp
|
||||
gnc-csv-import-settings.hpp
|
||||
gnc-csv-price-import-settings.hpp
|
||||
gnc-csv-trans-import-settings.hpp
|
||||
gnc-dummy-tokenizer.hpp
|
||||
gnc-fw-tokenizer.hpp
|
||||
gnc-price-import.hpp
|
||||
gnc-price-props.hpp
|
||||
gnc-tokenizer.hpp
|
||||
gnc-trans-props.hpp
|
||||
gnc-tx-import.hpp
|
||||
@ -84,7 +94,7 @@ INSTALL(TARGETS gncmod-csv-import
|
||||
# No headers to install
|
||||
|
||||
SET(csv_import_GLADE assistant-csv-account-import.glade
|
||||
assistant-csv-trans-import.glade)
|
||||
assistant-csv-price-import.glade assistant-csv-trans-import.glade)
|
||||
|
||||
INSTALL(FILES ${csv_import_GLADE} DESTINATION ${CMAKE_INSTALL_DATADIR}/gnucash/gtkbuilder)
|
||||
|
||||
|
@ -5,6 +5,7 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la
|
||||
libgncmod_csv_import_la_SOURCES = \
|
||||
gncmod-csv-import.c \
|
||||
assistant-csv-account-import.c \
|
||||
assistant-csv-price-import.cpp \
|
||||
assistant-csv-trans-import.cpp \
|
||||
gnc-plugin-csv-import.c \
|
||||
csv-account-import.c \
|
||||
@ -13,13 +14,18 @@ libgncmod_csv_import_la_SOURCES = \
|
||||
gnc-csv-gnumeric-popup.c \
|
||||
gnc-dummy-tokenizer.cpp \
|
||||
gnc-fw-tokenizer.cpp \
|
||||
gnc-price-import.cpp \
|
||||
gnc-price-props.cpp \
|
||||
gnc-tokenizer.cpp \
|
||||
gnc-tx-import.cpp \
|
||||
gnc-trans-props.cpp \
|
||||
gnc-csv-trans-settings.cpp
|
||||
gnc-csv-import-settings.cpp \
|
||||
gnc-csv-price-import-settings.cpp \
|
||||
gnc-csv-trans-import-settings.cpp
|
||||
|
||||
noinst_HEADERS = \
|
||||
assistant-csv-account-import.h \
|
||||
assistant-csv-price-import.h \
|
||||
assistant-csv-trans-import.h \
|
||||
gnc-plugin-csv-import.h \
|
||||
csv-account-import.h \
|
||||
@ -28,10 +34,14 @@ noinst_HEADERS = \
|
||||
gnc-csv-gnumeric-popup.h \
|
||||
gnc-dummy-tokenizer.hpp \
|
||||
gnc-fw-tokenizer.hpp \
|
||||
gnc-price-import.hpp \
|
||||
gnc-price-props.hpp \
|
||||
gnc-tokenizer.hpp \
|
||||
gnc-tx-import.hpp \
|
||||
gnc-trans-props.hpp \
|
||||
gnc-csv-trans-settings.hpp
|
||||
gnc-csv-import-settings.hpp \
|
||||
gnc-csv-price-import-settings.hpp \
|
||||
gnc-csv-trans-import-settings.hpp
|
||||
|
||||
libgncmod_csv_import_la_LDFLAGS = -avoid-version
|
||||
|
||||
@ -75,6 +85,7 @@ ui_DATA = \
|
||||
gtkbuilderdir = ${GNC_GTKBUILDER_DIR}
|
||||
gtkbuilder_DATA = \
|
||||
assistant-csv-account-import.glade \
|
||||
assistant-csv-price-import.glade \
|
||||
assistant-csv-trans-import.glade
|
||||
|
||||
EXTRA_DIST = $(ui_DATA) $(gtkbuilder_DATA) CMakeLists.txt
|
||||
|
1835
gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
Normal file
1835
gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1122
gnucash/import-export/csv-imp/assistant-csv-price-import.glade
Normal file
1122
gnucash/import-export/csv-imp/assistant-csv-price-import.glade
Normal file
File diff suppressed because it is too large
Load Diff
36
gnucash/import-export/csv-imp/assistant-csv-price-import.h
Normal file
36
gnucash/import-export/csv-imp/assistant-csv-price-import.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*******************************************************************\
|
||||
* assistant-csv-price-import.h -- An assistant for importing *
|
||||
* Prices from a file. *
|
||||
* *
|
||||
* Copyright (C) 2017 Robert Fewell *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
/** @file assistant-csv-price-import.h
|
||||
@brief CSV Import Assistant
|
||||
@author Copyright (c) 2017 Robert Fewell
|
||||
*/
|
||||
#ifndef GNC_ASSISTANT_CSV_IMPORT_PRICE_H
|
||||
#define GNC_ASSISTANT_CSV_IMPORT_PRICE_H
|
||||
|
||||
|
||||
/** The gnc_file_csv_price_import() will let the user import the
|
||||
* commodity prices from a file.
|
||||
*/
|
||||
void gnc_file_csv_price_import (void);
|
||||
#endif
|
@ -58,7 +58,7 @@ extern "C"
|
||||
#include "go-charmap-sel.h"
|
||||
}
|
||||
|
||||
#include "gnc-csv-trans-settings.hpp"
|
||||
#include "gnc-csv-trans-import-settings.hpp"
|
||||
#include "gnc-tx-import.hpp"
|
||||
#include "gnc-fw-tokenizer.hpp"
|
||||
#include "gnc-csv-tokenizer.hpp"
|
||||
@ -676,8 +676,7 @@ void CsvImpTransAssist::preview_populate_settings_combo()
|
||||
gtk_list_store_clear (GTK_LIST_STORE(model));
|
||||
|
||||
// Append the default entry
|
||||
|
||||
auto presets = get_trans_presets ();
|
||||
auto presets = get_import_presets_trans ();
|
||||
for (auto preset : presets)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
@ -705,11 +704,11 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
|
||||
/* Handle sensitivity of the delete and save button */
|
||||
if (gtk_combo_box_get_active_iter (combo, &iter))
|
||||
{
|
||||
CsvTransSettings *preset;
|
||||
CsvTransImpSettings *preset;
|
||||
GtkTreeModel *model = gtk_combo_box_get_model (combo);
|
||||
gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
|
||||
|
||||
if (preset && !trans_preset_is_reserved_name (preset->m_name))
|
||||
if (preset && !preset_is_reserved_name (preset->m_name))
|
||||
{
|
||||
/* Current preset is not read_only, so buttons can be enabled */
|
||||
can_delete = true;
|
||||
@ -717,7 +716,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
|
||||
}
|
||||
}
|
||||
else if (entry_text && (strlen (entry_text) > 0) &&
|
||||
!trans_preset_is_reserved_name (std::string(entry_text)))
|
||||
!preset_is_reserved_name (std::string(entry_text)))
|
||||
can_save = true;
|
||||
|
||||
gtk_widget_set_sensitive (save_button, can_save);
|
||||
@ -734,7 +733,7 @@ CsvImpTransAssist::preview_settings_name (GtkEntry* entry)
|
||||
|
||||
auto box = gtk_widget_get_parent (GTK_WIDGET(entry));
|
||||
auto combo = gtk_widget_get_parent (GTK_WIDGET(box));
|
||||
|
||||
|
||||
preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
|
||||
}
|
||||
|
||||
@ -750,7 +749,7 @@ CsvImpTransAssist::preview_settings_load ()
|
||||
if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
|
||||
return;
|
||||
|
||||
CsvTransSettings *preset = nullptr;
|
||||
CsvTransImpSettings *preset = nullptr;
|
||||
auto model = gtk_combo_box_get_model (settings_combo);
|
||||
gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
|
||||
|
||||
@ -777,7 +776,7 @@ CsvImpTransAssist::preview_settings_delete ()
|
||||
if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
|
||||
return;
|
||||
|
||||
CsvTransSettings *preset = nullptr;
|
||||
CsvTransImpSettings *preset = nullptr;
|
||||
auto model = gtk_combo_box_get_model (settings_combo);
|
||||
gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
|
||||
|
||||
@ -810,7 +809,7 @@ CsvImpTransAssist::preview_settings_save ()
|
||||
while (valid)
|
||||
{
|
||||
// Walk through the list, reading each row
|
||||
CsvTransSettings *preset;
|
||||
CsvTransImpSettings *preset;
|
||||
gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
|
||||
|
||||
if (preset && (preset->m_name == std::string(new_name)))
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************\
|
||||
* gnc-csv-trans-settings.c -- Save and Load CSV Import Settings *
|
||||
* gnc-csv-import-settings.cpp -- Save and Load CSV Import Settings *
|
||||
* *
|
||||
* Copyright (C) 2014 Robert Fewell *
|
||||
* *
|
||||
@ -20,13 +20,13 @@
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
/** @file gnc-csv-trans-settings.c
|
||||
/** @file gnc-csv-import-settings.cpp
|
||||
@brief CSV Import Settings
|
||||
@author Copyright (c) 2014 Robert Fewell
|
||||
@author Copyright (c) 2016 Geert Janssens
|
||||
*/
|
||||
|
||||
#include "gnc-csv-trans-settings.hpp"
|
||||
#include "gnc-csv-import-settings.hpp"
|
||||
#include <sstream>
|
||||
|
||||
extern "C"
|
||||
@ -41,15 +41,15 @@ extern "C"
|
||||
#include "gnc-ui-util.h"
|
||||
}
|
||||
|
||||
const std::string csv_group_prefix{"CSV - "};
|
||||
const std::string csv_group_prefix{"CSV-"};
|
||||
const std::string no_settings{N_("No Settings")};
|
||||
const std::string gnc_exp{N_("GnuCash Export Format")};
|
||||
|
||||
#define CSV_NAME "Name"
|
||||
#define CSV_FORMAT "CsvFormat"
|
||||
#define CSV_SKIP_ALT "SkipAltLines"
|
||||
#define CSV_SKIP_START "SkipStartLines"
|
||||
#define CSV_SKIP_END "SkipEndLines"
|
||||
#define CSV_MULTI_SPLIT "MultiSplit"
|
||||
|
||||
#define CSV_SEP "Separators"
|
||||
|
||||
@ -60,115 +60,10 @@ const std::string gnc_exp{N_("GnuCash Export Format")};
|
||||
#define CSV_CURRENCY "CurrencyFormat"
|
||||
|
||||
#define CSV_ENCODING "Encoding"
|
||||
#define CSV_COL_TYPES "ColumnTypes"
|
||||
#define CSV_COL_WIDTHS "ColumnWidths"
|
||||
#define CSV_ACCOUNT "BaseAccount"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
|
||||
|
||||
preset_vec presets;
|
||||
|
||||
static std::shared_ptr<CsvTransSettings> create_int_no_preset(void)
|
||||
{
|
||||
auto preset = std::make_shared<CsvTransSettings>();
|
||||
preset->m_name = no_settings;
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
|
||||
{
|
||||
auto preset = std::make_shared<CsvTransSettings>();
|
||||
preset->m_name = gnc_exp;
|
||||
preset->m_skip_start_lines = 1;
|
||||
preset->m_multi_split = true;
|
||||
|
||||
/* FIXME date and currency format should still be aligned with export format!
|
||||
* That's currently hard to do, because the export uses whatever the user
|
||||
* had set as global preference.
|
||||
preset->date_active = 0;
|
||||
preset->currency_active = 0;
|
||||
*/
|
||||
preset->m_column_types = {
|
||||
GncTransPropType::DATE,
|
||||
GncTransPropType::UNIQUE_ID,
|
||||
GncTransPropType::NUM,
|
||||
GncTransPropType::DESCRIPTION,
|
||||
GncTransPropType::NOTES,
|
||||
GncTransPropType::COMMODITY,
|
||||
GncTransPropType::VOID_REASON,
|
||||
GncTransPropType::ACTION,
|
||||
GncTransPropType::MEMO,
|
||||
GncTransPropType::ACCOUNT,
|
||||
GncTransPropType::NONE,
|
||||
GncTransPropType::NONE,
|
||||
GncTransPropType::DEPOSIT,
|
||||
GncTransPropType::REC_STATE,
|
||||
GncTransPropType::REC_DATE,
|
||||
GncTransPropType::PRICE
|
||||
};
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* find
|
||||
*
|
||||
* find all settings entries in the state key file
|
||||
**************************************************/
|
||||
const preset_vec& get_trans_presets (void)
|
||||
{
|
||||
|
||||
// Search all Groups in the state key file for ones starting with prefix
|
||||
auto preset_names = std::vector<std::string>();
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
gsize grouplength;
|
||||
gchar **groups = g_key_file_get_groups (keyfile, &grouplength);
|
||||
|
||||
/* Start by building a sorted list of candidate presets as found in the state file */
|
||||
for (gsize i=0; i < grouplength; i++)
|
||||
{
|
||||
auto group = std::string(groups[i]);
|
||||
auto pos = group.find(csv_group_prefix);
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
preset_names.push_back(group.substr(csv_group_prefix.size()));
|
||||
}
|
||||
// string array from the state file is no longer needed now.
|
||||
g_strfreev (groups);
|
||||
|
||||
/* We want our settings to appear sorted alphabetically to the user */
|
||||
std::sort(preset_names.begin(), preset_names.end());
|
||||
|
||||
/* Now add each preset to our global list */
|
||||
presets.clear();
|
||||
|
||||
/* Start with the internally generated ones */
|
||||
presets.push_back(create_int_no_preset());
|
||||
presets.push_back(create_int_gnc_exp_preset());
|
||||
|
||||
/* Then add all the ones we found in the state file */
|
||||
for (auto preset_name : preset_names)
|
||||
{
|
||||
auto preset = std::make_shared<CsvTransSettings>();
|
||||
preset->m_name = preset_name;
|
||||
preset->load();
|
||||
presets.push_back(preset);
|
||||
}
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
bool trans_preset_is_reserved_name (const std::string& name)
|
||||
{
|
||||
return ((name == no_settings) ||
|
||||
(name == _(no_settings.c_str())) ||
|
||||
(name == gnc_exp) ||
|
||||
(name == _(gnc_exp.c_str())));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************
|
||||
* handle_load_error
|
||||
*
|
||||
@ -176,7 +71,7 @@ bool trans_preset_is_reserved_name (const std::string& name)
|
||||
* ignore key-not-found errors though. We'll just
|
||||
* use a default value and go on.
|
||||
**************************************************/
|
||||
static bool
|
||||
bool
|
||||
handle_load_error (GError **key_error, const std::string& group)
|
||||
{
|
||||
if (!*key_error)
|
||||
@ -193,20 +88,40 @@ handle_load_error (GError **key_error, const std::string& group)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool preset_is_reserved_name (const std::string& name)
|
||||
{
|
||||
return ((name == no_settings) ||
|
||||
(name == _(no_settings.c_str())) ||
|
||||
(name == gnc_exp) ||
|
||||
(name == _(gnc_exp.c_str())));
|
||||
}
|
||||
|
||||
std::string get_no_settings (void)
|
||||
{
|
||||
return no_settings;
|
||||
}
|
||||
|
||||
std::string get_gnc_exp (void)
|
||||
{
|
||||
return gnc_exp;
|
||||
}
|
||||
|
||||
std::string get_prefix (void)
|
||||
{
|
||||
return csv_group_prefix;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* load
|
||||
* load_common
|
||||
*
|
||||
* load the settings from a state key file
|
||||
**************************************************/
|
||||
bool
|
||||
CsvTransSettings::load (void)
|
||||
CsvImportSettings::load_common (void)
|
||||
{
|
||||
if (trans_preset_is_reserved_name (m_name))
|
||||
return true;
|
||||
|
||||
GError *key_error = nullptr;
|
||||
m_load_error = false;
|
||||
auto group = csv_group_prefix + m_name;
|
||||
auto group = csv_group_prefix + m_settings_type + " - " + m_name;
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
|
||||
m_skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
|
||||
@ -218,9 +133,6 @@ CsvTransSettings::load (void)
|
||||
m_skip_alt_lines = g_key_file_get_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, &key_error);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
|
||||
m_multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
|
||||
auto csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
|
||||
if (key_error) csv_format = true; // default to true, but above command will return false in case of error
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
@ -251,40 +163,8 @@ CsvTransSettings::load (void)
|
||||
if (key_char)
|
||||
g_free (key_char);
|
||||
|
||||
key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
|
||||
if (key_char && *key_char != '\0')
|
||||
m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
if (key_char)
|
||||
g_free (key_char);
|
||||
|
||||
m_column_types.clear();
|
||||
// Widths
|
||||
gsize list_len;
|
||||
gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
|
||||
&list_len, &key_error);
|
||||
for (uint32_t i = 0; i < list_len; i++)
|
||||
{
|
||||
auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(),
|
||||
gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i]));
|
||||
if (col_types_it != gnc_csv_col_type_strs.end())
|
||||
{
|
||||
/* Found a valid column type. Now check whether it is allowed
|
||||
* in the selected mode (two-split vs multi-split) */
|
||||
auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split);
|
||||
m_column_types.push_back(prop);
|
||||
if (prop != col_types_it->first)
|
||||
PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. "
|
||||
"Inserting column type 'NONE' instead'.",
|
||||
col_types_it->second, m_multi_split ? "enabled" : "disabled");
|
||||
}
|
||||
else
|
||||
PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
|
||||
col_types_str[i]);
|
||||
|
||||
}
|
||||
if (col_types_str)
|
||||
g_strfreev (col_types_str);
|
||||
|
||||
m_column_widths.clear();
|
||||
gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
|
||||
&list_len, &key_error);
|
||||
@ -300,36 +180,20 @@ CsvTransSettings::load (void)
|
||||
return m_load_error;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************
|
||||
* save
|
||||
* save_common
|
||||
*
|
||||
* save settings to a key file
|
||||
**************************************************/
|
||||
bool
|
||||
CsvTransSettings::save (void)
|
||||
CsvImportSettings::save_common (void)
|
||||
{
|
||||
if (trans_preset_is_reserved_name (m_name))
|
||||
{
|
||||
PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((m_name.find('[') != std::string::npos))
|
||||
{
|
||||
PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
auto group = csv_group_prefix + m_name;
|
||||
auto group = csv_group_prefix + m_settings_type + " - " + m_name;
|
||||
|
||||
// Drop previous saved settings with this name
|
||||
g_key_file_remove_group (keyfile, group.c_str(), nullptr);
|
||||
|
||||
// Start Saving the settings
|
||||
// Start Saving the Common settings
|
||||
g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, m_name.c_str());
|
||||
g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
|
||||
|
||||
g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, m_skip_start_lines);
|
||||
g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, m_skip_end_lines);
|
||||
g_key_file_set_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, m_skip_alt_lines);
|
||||
@ -345,22 +209,10 @@ CsvTransSettings::save (void)
|
||||
[&cmt_ss, &fmt_num](const GncDateFormat& fmt)
|
||||
{ cmt_ss << fmt_num++ << ": '" << fmt.m_fmt << "', "; });
|
||||
auto cmt = cmt_ss.str().substr(0, static_cast<long>(cmt_ss.tellp()) - 2);
|
||||
g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE,
|
||||
cmt.c_str(), nullptr);
|
||||
g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE, cmt.c_str(), nullptr);
|
||||
g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, m_currency_format);
|
||||
g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, m_encoding.c_str());
|
||||
|
||||
if (m_base_account)
|
||||
g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
|
||||
|
||||
std::vector<const char*> col_types_str;
|
||||
for (auto col_type : m_column_types)
|
||||
col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
|
||||
|
||||
if (!col_types_str.empty())
|
||||
g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
|
||||
col_types_str.data(), col_types_str.size());
|
||||
|
||||
if (!m_column_widths.empty())
|
||||
g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
|
||||
(gint*)(m_column_widths.data()), m_column_widths.size());
|
||||
@ -377,33 +229,20 @@ CsvTransSettings::save (void)
|
||||
{
|
||||
if (key_error)
|
||||
{
|
||||
g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_COL_TYPES, key_error->message);
|
||||
g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_ENCODING, key_error->message);
|
||||
g_error_free (key_error);
|
||||
}
|
||||
else
|
||||
g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, enc_str.c_str(), group.c_str());
|
||||
g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_ENCODING, enc_str.c_str(), group.c_str());
|
||||
error = true;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
CsvTransSettings::remove (void)
|
||||
CsvImportSettings::remove_common (void)
|
||||
{
|
||||
if (trans_preset_is_reserved_name (m_name))
|
||||
return;
|
||||
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
auto group = csv_group_prefix + m_name;
|
||||
auto group = csv_group_prefix + m_settings_type + " - " + m_name;
|
||||
g_key_file_remove_group (keyfile, group.c_str(), nullptr);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CsvTransSettings::read_only (void)
|
||||
{
|
||||
return ((m_name == no_settings) ||
|
||||
(m_name == _(no_settings.c_str())) ||
|
||||
(m_name == gnc_exp) ||
|
||||
(m_name == _(gnc_exp.c_str())));
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************\
|
||||
* gnc-csv-trans-settings.h -- Save and Load CSV Import Settings *
|
||||
* gnc-csv-import-settings.hpp -- Save and Load CSV Import Settings *
|
||||
* *
|
||||
* Copyright (C) 2014 Robert Fewell *
|
||||
* *
|
||||
@ -20,22 +20,24 @@
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
/** @file gnc-csv-trans-settings.h
|
||||
/** @file gnc-csv-import-settings.hpp
|
||||
@brief CSV Import Settings
|
||||
@author Copyright (c) 2014 Robert Fewell
|
||||
@author Copyright (c) 2016 Geert Janssens
|
||||
*/
|
||||
#ifndef GNC_CSV_TRANS_SETTINGS_H
|
||||
#define GNC_CSV_TRANS_SETTINGS_H
|
||||
#ifndef GNC_CSV_IMPORT_SETTINGS_H
|
||||
#define GNC_CSV_IMPORT_SETTINGS_H
|
||||
|
||||
extern "C" {
|
||||
#include <config.h>
|
||||
#include "Account.h"
|
||||
#include "gnc-commodity.h"
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "gnc-trans-props.hpp"
|
||||
#include <boost/optional.hpp>
|
||||
#include <gnc-datetime.hpp>
|
||||
#include "gnc-tokenizer.hpp"
|
||||
|
||||
/** Enumeration for separator checkbutton types. These are the
|
||||
@ -47,70 +49,63 @@ enum SEP_BUTTON_TYPES {SEP_SPACE, SEP_TAB, SEP_COMMA, SEP_COLON, SEP_SEMICOLON,
|
||||
/** Enumeration for the settings combo's */
|
||||
enum SETTINGS_COL {SET_GROUP, SET_NAME};
|
||||
|
||||
struct CsvTransSettings
|
||||
struct CsvImportSettings
|
||||
{
|
||||
CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"},
|
||||
m_multi_split (false), m_date_format {0}, m_currency_format {0},
|
||||
CsvImportSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"},
|
||||
m_date_format {0}, m_currency_format {0},
|
||||
m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false),
|
||||
m_separators {","}, m_base_account {nullptr},
|
||||
m_load_error {false} { }
|
||||
m_separators {","}, m_load_error {false} { }
|
||||
|
||||
/** Save the gathered widget properties to a key File.
|
||||
*
|
||||
* @return true if there was a problem in saving.
|
||||
*/
|
||||
bool save (void);
|
||||
bool save_common (void);
|
||||
|
||||
/** Load the widget properties from a key File.
|
||||
*
|
||||
* @return true if there was a problem.
|
||||
*/
|
||||
bool load (void);
|
||||
bool load_common (void);
|
||||
|
||||
/** Remove the preset from the state file.
|
||||
*/
|
||||
void remove (void);
|
||||
|
||||
/** Check whether the user is allowed to save (over) or delete this preset or not.
|
||||
* The internally generated presets are read-only. The others
|
||||
* can be saved to the state file or deleted.
|
||||
*
|
||||
* @return true if there was a problem.
|
||||
*/
|
||||
bool read_only (void);
|
||||
void remove_common (void);
|
||||
|
||||
std::string m_settings_type; // Settings Type, TRANS, PRICE etc.
|
||||
|
||||
// Common Settings
|
||||
std::string m_name; // Name given to this preset by the user
|
||||
GncImpFileFormat m_file_format; // CSV import Format
|
||||
std::string m_encoding; // File encoding
|
||||
bool m_multi_split; // Assume multiple lines per transaction
|
||||
int m_date_format; // Date Active id
|
||||
int m_currency_format; // Currency Active id
|
||||
uint32_t m_skip_start_lines; // Number of header rows to skip
|
||||
uint32_t m_skip_end_lines; // Number of footer rows to skip
|
||||
bool m_skip_alt_lines; // Skip alternate rows
|
||||
std::string m_separators; // Separators for csv format
|
||||
|
||||
Account *m_base_account; // Base account
|
||||
std::vector<GncTransPropType> m_column_types; // The Column types in order
|
||||
std::vector<uint32_t> m_column_widths; // The Column widths
|
||||
|
||||
bool m_load_error; // Was there an error while parsing the state file ?
|
||||
std::vector<uint32_t> m_column_widths; // The Column widths
|
||||
};
|
||||
|
||||
using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
|
||||
/** Creates a vector of CsvTransSettings which combines
|
||||
* - one or more internally defined presets
|
||||
* - all preset found in the state key file.
|
||||
*
|
||||
* @return a reference to the populated vector.
|
||||
*/
|
||||
const preset_vec& get_trans_presets (void);
|
||||
std::string get_no_settings (void);
|
||||
std::string get_gnc_exp (void);
|
||||
std::string get_prefix (void);
|
||||
|
||||
/** Check whether name can be used as a preset name.
|
||||
* The names of the internal presets are considered reserved.
|
||||
* A preset with such a name should not be saved or deleted.
|
||||
*/
|
||||
bool trans_preset_is_reserved_name (const std::string& name);
|
||||
bool preset_is_reserved_name (const std::string& name);
|
||||
|
||||
/**************************************************
|
||||
* handle_load_error
|
||||
*
|
||||
* record possible errors in the log file
|
||||
* ignore key-not-found errors though. We'll just
|
||||
* use a default value and go on.
|
||||
**************************************************/
|
||||
bool
|
||||
handle_load_error (GError **key_error, const std::string& group);
|
||||
|
||||
#endif
|
258
gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp
Normal file
258
gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/*******************************************************************\
|
||||
* gnc-csv-price-import-settings.cpp -- Price CSV Import Settings *
|
||||
* *
|
||||
* Copyright (C) 2017 Robert Fewell *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
/** @file gnc-csv-price-import-settings.cpp
|
||||
@brief CSV Import Settings
|
||||
@author Copyright (c) 2014 Robert Fewell
|
||||
@author Copyright (c) 2016 Geert Janssens
|
||||
*/
|
||||
|
||||
#include "gnc-csv-import-settings.hpp"
|
||||
#include "gnc-csv-price-import-settings.hpp"
|
||||
#include <sstream>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "Account.h"
|
||||
#include "gnc-state.h"
|
||||
#include "gnc-ui-util.h"
|
||||
}
|
||||
|
||||
const std::string settings_type{"PRICE"};
|
||||
|
||||
#define CSV_COL_TYPES "ColumnTypes"
|
||||
|
||||
#define CSV_TO_CURR "PriceToCurrency"
|
||||
#define CSV_FROM_COMM "PriceFromCommodity"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
|
||||
|
||||
preset_vec_price presets_price;
|
||||
|
||||
static std::shared_ptr<CsvPriceImpSettings> create_int_no_preset(void)
|
||||
{
|
||||
auto preset = std::make_shared<CsvPriceImpSettings>();
|
||||
preset->m_name = get_no_settings();
|
||||
preset->m_settings_type = settings_type;
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
static std::shared_ptr<CsvPriceImpSettings> create_int_gnc_exp_preset(void)
|
||||
{
|
||||
auto preset = std::make_shared<CsvPriceImpSettings>();
|
||||
preset->m_name = get_gnc_exp();
|
||||
preset->m_skip_start_lines = 1;
|
||||
|
||||
/* FIXME date and currency format should still be aligned with export format!
|
||||
* That's currently hard to do, because the export uses whatever the user
|
||||
* had set as global preference.
|
||||
preset->date_active = 0;
|
||||
preset->currency_active = 0;
|
||||
*/
|
||||
preset->m_column_types_price = {
|
||||
GncPricePropType::DATE,
|
||||
GncPricePropType::AMOUNT,
|
||||
GncPricePropType::FROM_COMMODITY,
|
||||
GncPricePropType::TO_CURRENCY
|
||||
};
|
||||
return preset;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* find
|
||||
*
|
||||
* find all settings entries in the state key file
|
||||
* based on settings type.
|
||||
**************************************************/
|
||||
const preset_vec_price& get_import_presets_price (void)
|
||||
{
|
||||
// Search all Groups in the state key file for ones starting with prefix
|
||||
auto preset_names = std::vector<std::string>();
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
gsize grouplength;
|
||||
gchar **groups = g_key_file_get_groups (keyfile, &grouplength);
|
||||
|
||||
/* Start by building a sorted list of candidate presets as found in the state file */
|
||||
for (gsize i=0; i < grouplength; i++)
|
||||
{
|
||||
auto group = std::string(groups[i]);
|
||||
auto gp = get_prefix() + settings_type + " - ";
|
||||
auto pos = group.find(gp);
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
preset_names.push_back(group.substr(gp.size()));
|
||||
}
|
||||
// string array from the state file is no longer needed now.
|
||||
g_strfreev (groups);
|
||||
|
||||
/* We want our settings to appear sorted alphabetically to the user */
|
||||
std::sort(preset_names.begin(), preset_names.end());
|
||||
|
||||
/* Now add each preset to our global list */
|
||||
presets_price.clear();
|
||||
|
||||
/* Start with the internally generated ones */
|
||||
presets_price.push_back(create_int_no_preset());
|
||||
//presets_price.push_back(create_int_gnc_exp_preset()); // Not Required
|
||||
|
||||
/* Then add all the ones we found in the state file */
|
||||
for (auto preset_name : preset_names)
|
||||
{
|
||||
auto preset = std::make_shared<CsvPriceImpSettings>();
|
||||
preset->m_settings_type = settings_type;
|
||||
preset->m_name = preset_name;
|
||||
preset->load();
|
||||
presets_price.push_back(preset);
|
||||
}
|
||||
return presets_price;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* load
|
||||
*
|
||||
* load the settings from a state key file
|
||||
**************************************************/
|
||||
bool
|
||||
CsvPriceImpSettings::load (void)
|
||||
{
|
||||
if (preset_is_reserved_name (m_name))
|
||||
return true;
|
||||
|
||||
GError *key_error = nullptr;
|
||||
m_load_error = false;
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
auto group = get_prefix() + m_settings_type + " - " + m_name;
|
||||
|
||||
// Start Loading the settings
|
||||
m_load_error = load_common(); // load the common settings
|
||||
|
||||
gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
|
||||
if (key_char && *key_char != '\0')
|
||||
m_to_currency = parse_commodity_price_comm (key_char);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
if (key_char)
|
||||
g_free (key_char);
|
||||
|
||||
key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_FROM_COMM, &key_error);
|
||||
if (key_char && *key_char != '\0')
|
||||
m_from_commodity = parse_commodity_price_comm (key_char);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
if (key_char)
|
||||
g_free (key_char);
|
||||
|
||||
gsize list_len;
|
||||
m_column_types_price.clear();
|
||||
gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
|
||||
&list_len, &key_error);
|
||||
for (uint32_t i = 0; i < list_len; i++)
|
||||
{
|
||||
auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(),
|
||||
gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i]));
|
||||
if (col_types_it != gnc_price_col_type_strs.end())
|
||||
{
|
||||
// Found a valid column type
|
||||
m_column_types_price.push_back(col_types_it->first);
|
||||
}
|
||||
else
|
||||
PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
|
||||
col_types_str_price[i]);
|
||||
}
|
||||
if (col_types_str_price)
|
||||
g_strfreev (col_types_str_price);
|
||||
|
||||
return m_load_error;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* save
|
||||
*
|
||||
* save settings to a key file
|
||||
**************************************************/
|
||||
bool
|
||||
CsvPriceImpSettings::save (void)
|
||||
{
|
||||
if (preset_is_reserved_name (m_name))
|
||||
{
|
||||
PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((m_name.find('[') != std::string::npos))
|
||||
{
|
||||
PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
auto group = get_prefix() + m_settings_type + " - " + m_name;
|
||||
|
||||
// Drop previous saved settings with this name
|
||||
g_key_file_remove_group (keyfile, group.c_str(), nullptr);
|
||||
|
||||
// Start Saving the settings
|
||||
bool error = save_common(); // save the common settings
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (m_to_currency)
|
||||
{
|
||||
auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::",
|
||||
gnc_commodity_get_mnemonic (m_to_currency), nullptr);
|
||||
g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name);
|
||||
g_free (unique_name);
|
||||
}
|
||||
|
||||
if (m_from_commodity)
|
||||
{
|
||||
auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::",
|
||||
gnc_commodity_get_mnemonic (m_from_commodity), nullptr);
|
||||
g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name);
|
||||
g_free (unique_name);
|
||||
}
|
||||
|
||||
std::vector<const char*> col_types_str_price;
|
||||
for (auto col_type : m_column_types_price)
|
||||
col_types_str_price.push_back(gnc_price_col_type_strs[col_type]);
|
||||
|
||||
if (!col_types_str_price.empty())
|
||||
g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
|
||||
col_types_str_price.data(), col_types_str_price.size());
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
CsvPriceImpSettings::remove (void)
|
||||
{
|
||||
if (preset_is_reserved_name (m_name))
|
||||
return;
|
||||
|
||||
remove_common();
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*******************************************************************\
|
||||
* gnc-csv-price-import-settings.hpp -- Price CSV Import Settings *
|
||||
* *
|
||||
* Copyright (C) 2017 Robert Fewell *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
/** @file gnc-csv-price-import-settings.hpp
|
||||
@brief CSV Import Settings
|
||||
@author Copyright (c) 2014 Robert Fewell
|
||||
@author Copyright (c) 2016 Geert Janssens
|
||||
*/
|
||||
#ifndef GNC_CSV_PRICE_IMPORT_SETTINGS_H
|
||||
#define GNC_CSV_PRICE_IMPORT_SETTINGS_H
|
||||
|
||||
extern "C" {
|
||||
#include <config.h>
|
||||
#include "Account.h"
|
||||
#include "gnc-commodity.h"
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "gnc-price-props.hpp"
|
||||
#include "gnc-tokenizer.hpp"
|
||||
#include "gnc-csv-import-settings.hpp"
|
||||
|
||||
struct CsvPriceImpSettings : public CsvImportSettings
|
||||
{
|
||||
CsvPriceImpSettings() : m_from_commodity {nullptr}, m_to_currency {nullptr} { }
|
||||
|
||||
/** Save the gathered widget properties to a key File.
|
||||
*
|
||||
* @return true if there was a problem in saving.
|
||||
*/
|
||||
bool save (void);
|
||||
|
||||
/** Load the widget properties from a key File.
|
||||
*
|
||||
* @return true if there was a problem.
|
||||
*/
|
||||
bool load (void);
|
||||
|
||||
/** Remove the preset from the state file.
|
||||
*/
|
||||
void remove (void);
|
||||
|
||||
// Price Settings
|
||||
gnc_commodity *m_from_commodity; // Price From Commodity
|
||||
gnc_commodity *m_to_currency; // Price To Currency
|
||||
std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
|
||||
};
|
||||
|
||||
using preset_vec_price = std::vector<std::shared_ptr<CsvPriceImpSettings>>;
|
||||
|
||||
/** Creates a vector of CsvPriceImpSettings which combines
|
||||
* - one or more internally defined presets
|
||||
* - all preset found in the state key file.
|
||||
*
|
||||
* @return a reference to the populated vector.
|
||||
*/
|
||||
const preset_vec_price& get_import_presets_price (void);
|
||||
|
||||
#endif
|
262
gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp
Normal file
262
gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
/*******************************************************************\
|
||||
* gnc-csv-trans-import-settings.cpp -- Trans CSV Import Settings *
|
||||
* *
|
||||
* Copyright (C) 2014 Robert Fewell *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
/** @file gnc-csv-trans-import-settings.cpp
|
||||
@brief CSV Import Settings
|
||||
@author Copyright (c) 2014 Robert Fewell
|
||||
@author Copyright (c) 2016 Geert Janssens
|
||||
*/
|
||||
|
||||
#include "gnc-csv-import-settings.hpp"
|
||||
#include "gnc-csv-trans-import-settings.hpp"
|
||||
#include <sstream>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <config.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "Account.h"
|
||||
#include "gnc-state.h"
|
||||
#include "gnc-ui-util.h"
|
||||
}
|
||||
|
||||
const std::string settings_type{"TRANS"};
|
||||
|
||||
#define CSV_COL_TYPES "ColumnTypes"
|
||||
|
||||
#define CSV_ACCOUNT "BaseAccount"
|
||||
#define CSV_MULTI_SPLIT "MultiSplit"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
|
||||
|
||||
preset_vec_trans presets_trans;
|
||||
|
||||
static std::shared_ptr<CsvTransImpSettings> create_int_no_preset(void)
|
||||
{
|
||||
auto preset = std::make_shared<CsvTransImpSettings>();
|
||||
preset->m_name = get_no_settings();
|
||||
preset->m_settings_type = settings_type;
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
static std::shared_ptr<CsvTransImpSettings> create_int_gnc_exp_preset(void)
|
||||
{
|
||||
auto preset = std::make_shared<CsvTransImpSettings>();
|
||||
preset->m_name = get_gnc_exp();
|
||||
preset->m_skip_start_lines = 1;
|
||||
preset->m_multi_split = true;
|
||||
|
||||
/* FIXME date and currency format should still be aligned with export format!
|
||||
* That's currently hard to do, because the export uses whatever the user
|
||||
* had set as global preference.
|
||||
preset->date_active = 0;
|
||||
preset->currency_active = 0;
|
||||
*/
|
||||
preset->m_column_types = {
|
||||
GncTransPropType::DATE,
|
||||
GncTransPropType::UNIQUE_ID,
|
||||
GncTransPropType::NUM,
|
||||
GncTransPropType::DESCRIPTION,
|
||||
GncTransPropType::NOTES,
|
||||
GncTransPropType::COMMODITY,
|
||||
GncTransPropType::VOID_REASON,
|
||||
GncTransPropType::ACTION,
|
||||
GncTransPropType::MEMO,
|
||||
GncTransPropType::ACCOUNT,
|
||||
GncTransPropType::NONE,
|
||||
GncTransPropType::NONE,
|
||||
GncTransPropType::DEPOSIT,
|
||||
GncTransPropType::REC_STATE,
|
||||
GncTransPropType::REC_DATE,
|
||||
GncTransPropType::PRICE
|
||||
};
|
||||
return preset;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* find
|
||||
*
|
||||
* find all settings entries in the state key file
|
||||
* based on settings type.
|
||||
**************************************************/
|
||||
const preset_vec_trans& get_import_presets_trans (void)
|
||||
{
|
||||
// Search all Groups in the state key file for ones starting with prefix
|
||||
auto preset_names = std::vector<std::string>();
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
gsize grouplength;
|
||||
gchar **groups = g_key_file_get_groups (keyfile, &grouplength);
|
||||
|
||||
/* Start by building a sorted list of candidate presets as found in the state file */
|
||||
for (gsize i=0; i < grouplength; i++)
|
||||
{
|
||||
auto group = std::string(groups[i]);
|
||||
auto gp = get_prefix() + settings_type + " - ";
|
||||
auto pos = group.find(gp);
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
preset_names.push_back(group.substr(gp.size()));
|
||||
}
|
||||
// string array from the state file is no longer needed now.
|
||||
g_strfreev (groups);
|
||||
|
||||
/* We want our settings to appear sorted alphabetically to the user */
|
||||
std::sort(preset_names.begin(), preset_names.end());
|
||||
|
||||
/* Now add each preset to our global list */
|
||||
presets_trans.clear();
|
||||
|
||||
/* Start with the internally generated ones */
|
||||
presets_trans.push_back(create_int_no_preset());
|
||||
presets_trans.push_back(create_int_gnc_exp_preset());
|
||||
|
||||
/* Then add all the ones we found in the state file */
|
||||
for (auto preset_name : preset_names)
|
||||
{
|
||||
auto preset = std::make_shared<CsvTransImpSettings>();
|
||||
preset->m_settings_type = settings_type;
|
||||
preset->m_name = preset_name;
|
||||
preset->load();
|
||||
presets_trans.push_back(preset);
|
||||
}
|
||||
return presets_trans;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* load
|
||||
*
|
||||
* load the settings from a state key file
|
||||
**************************************************/
|
||||
bool
|
||||
CsvTransImpSettings::load (void)
|
||||
{
|
||||
if (preset_is_reserved_name (m_name))
|
||||
return true;
|
||||
|
||||
GError *key_error = nullptr;
|
||||
m_load_error = false;
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
auto group = get_prefix() + m_settings_type + " - " + m_name;
|
||||
|
||||
// Start Loading the settings
|
||||
m_load_error = load_common(); // load the common settings
|
||||
|
||||
m_multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
|
||||
gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
|
||||
if (key_char && *key_char != '\0')
|
||||
m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
|
||||
m_load_error |= handle_load_error (&key_error, group);
|
||||
if (key_char)
|
||||
g_free (key_char);
|
||||
|
||||
gsize list_len;
|
||||
m_column_types.clear();
|
||||
gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
|
||||
&list_len, &key_error);
|
||||
for (uint32_t i = 0; i < list_len; i++)
|
||||
{
|
||||
auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(),
|
||||
gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i]));
|
||||
if (col_types_it != gnc_csv_col_type_strs.end())
|
||||
{
|
||||
/* Found a valid column type. Now check whether it is allowed
|
||||
* in the selected mode (two-split vs multi-split) */
|
||||
auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split);
|
||||
m_column_types.push_back(prop);
|
||||
if (prop != col_types_it->first)
|
||||
PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. "
|
||||
"Inserting column type 'NONE' instead'.",
|
||||
col_types_it->second, m_multi_split ? "enabled" : "disabled");
|
||||
}
|
||||
else
|
||||
PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
|
||||
col_types_str[i]);
|
||||
}
|
||||
if (col_types_str)
|
||||
g_strfreev (col_types_str);
|
||||
|
||||
return m_load_error;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* save
|
||||
*
|
||||
* save settings to a key file
|
||||
**************************************************/
|
||||
bool
|
||||
CsvTransImpSettings::save (void)
|
||||
{
|
||||
if (preset_is_reserved_name (m_name))
|
||||
{
|
||||
PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((m_name.find('[') != std::string::npos))
|
||||
{
|
||||
PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto keyfile = gnc_state_get_current ();
|
||||
auto group = get_prefix() + m_settings_type + " - " + m_name;
|
||||
|
||||
// Drop previous saved settings with this name
|
||||
g_key_file_remove_group (keyfile, group.c_str(), nullptr);
|
||||
|
||||
// Start Saving the settings
|
||||
bool error = save_common(); // save the common settings
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
|
||||
|
||||
if (m_base_account)
|
||||
g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
|
||||
|
||||
std::vector<const char*> col_types_str;
|
||||
for (auto col_type : m_column_types)
|
||||
col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
|
||||
|
||||
if (!col_types_str.empty())
|
||||
g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
|
||||
col_types_str.data(), col_types_str.size());
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
CsvTransImpSettings::remove (void)
|
||||
{
|
||||
if (preset_is_reserved_name (m_name))
|
||||
return;
|
||||
|
||||
remove_common();
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*******************************************************************\
|
||||
* gnc-csv-trans-import-settings.hpp -- Trans CSV Import Settings *
|
||||
* *
|
||||
* Copyright (C) 2017 Robert Fewell *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
/** @file gnc-csv-trans-import-settings.hpp
|
||||
@brief CSV Import Settings
|
||||
@author Copyright (c) 2014 Robert Fewell
|
||||
@author Copyright (c) 2016 Geert Janssens
|
||||
*/
|
||||
#ifndef GNC_CSV_TRANS_IMPORT_SETTINGS_H
|
||||
#define GNC_CSV_TRANS_IMPORT_SETTINGS_H
|
||||
|
||||
extern "C" {
|
||||
#include <config.h>
|
||||
#include "Account.h"
|
||||
#include "gnc-commodity.h"
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "gnc-trans-props.hpp"
|
||||
#include "gnc-tokenizer.hpp"
|
||||
#include "gnc-csv-import-settings.hpp"
|
||||
|
||||
struct CsvTransImpSettings : public CsvImportSettings
|
||||
{
|
||||
CsvTransImpSettings() : m_base_account {nullptr}, m_multi_split (false) { }
|
||||
|
||||
/** Save the gathered widget properties to a key File.
|
||||
*
|
||||
* @return true if there was a problem in saving.
|
||||
*/
|
||||
bool save (void);
|
||||
|
||||
/** Load the widget properties from a key File.
|
||||
*
|
||||
* @return true if there was a problem.
|
||||
*/
|
||||
bool load (void);
|
||||
|
||||
/** Remove the preset from the state file.
|
||||
*/
|
||||
void remove (void);
|
||||
|
||||
// Transaction Settings
|
||||
Account *m_base_account; // Base account
|
||||
bool m_multi_split; // Assume multiple lines per transaction
|
||||
std::vector<GncTransPropType> m_column_types; // The Column types in order
|
||||
};
|
||||
|
||||
using preset_vec_trans = std::vector<std::shared_ptr<CsvTransImpSettings>>;
|
||||
|
||||
/** Creates a vector of CsvTransImpSettings which combines
|
||||
* - one or more internally defined presets
|
||||
* - all preset found in the state key file.
|
||||
*
|
||||
* @return a reference to the populated vector.
|
||||
*/
|
||||
const preset_vec_trans& get_import_presets_trans (void);
|
||||
|
||||
#endif
|
@ -5,6 +5,7 @@
|
||||
<placeholder name="FileImportPlaceholder">
|
||||
<menuitem name="FileCsvImportAccounts" action="CsvImportAccountAction"/>
|
||||
<menuitem name="FileCsvImportTrans" action="CsvImportTransAction"/>
|
||||
<menuitem name="FileCsvImportPrice" action="CsvImportPriceAction"/>
|
||||
</placeholder>
|
||||
</menu>
|
||||
</menu>
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "assistant-csv-account-import.h"
|
||||
#include "assistant-csv-trans-import.h"
|
||||
#include "assistant-csv-price-import.h"
|
||||
|
||||
static void gnc_plugin_csv_import_class_init (GncPluginCsvImportClass *klass);
|
||||
static void gnc_plugin_csv_import_init (GncPluginCsvImport *plugin);
|
||||
@ -38,6 +39,7 @@ static void gnc_plugin_csv_import_finalize (GObject *object);
|
||||
/* Command callbacks */
|
||||
static void gnc_plugin_csv_import_tree_cmd (GtkAction *action, GncMainWindowActionData *data);
|
||||
static void gnc_plugin_csv_import_trans_cmd (GtkAction *action, GncMainWindowActionData *data);
|
||||
static void gnc_plugin_csv_import_price_cmd (GtkAction *action, GncMainWindowActionData *data);
|
||||
|
||||
#define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-import-actions"
|
||||
#define PLUGIN_UI_FILENAME "gnc-plugin-csv-import-ui.xml"
|
||||
@ -54,6 +56,11 @@ static GtkActionEntry gnc_plugin_actions [] =
|
||||
N_("Import Transactions from a CSV file"),
|
||||
G_CALLBACK (gnc_plugin_csv_import_trans_cmd)
|
||||
},
|
||||
{
|
||||
"CsvImportPriceAction", "go-previous", N_("Import _Prices from a CSV file..."), NULL,
|
||||
N_("Import Prices from a CSV file"),
|
||||
G_CALLBACK (gnc_plugin_csv_import_price_cmd)
|
||||
},
|
||||
};
|
||||
static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
|
||||
|
||||
@ -157,6 +164,13 @@ gnc_plugin_csv_import_trans_cmd (GtkAction *action,
|
||||
gnc_file_csv_trans_import ();
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_plugin_csv_import_price_cmd (GtkAction *action,
|
||||
GncMainWindowActionData *data)
|
||||
{
|
||||
gnc_file_csv_price_import ();
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Plugin Bootstrapping *
|
||||
************************************************************/
|
||||
|
757
gnucash/import-export/csv-imp/gnc-price-import.cpp
Normal file
757
gnucash/import-export/csv-imp/gnc-price-import.cpp
Normal file
@ -0,0 +1,757 @@
|
||||
/********************************************************************\
|
||||
* gnc-price-import.cpp - import prices from csv files *
|
||||
* *
|
||||
* 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 *
|
||||
* *
|
||||
\********************************************************************/
|
||||
|
||||
#include <guid.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <platform.h>
|
||||
#if PLATFORM(WINDOWS)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "gnc-ui-util.h" //get book
|
||||
#include "gnc-commodity.h"
|
||||
#include "gnc-pricedb.h"
|
||||
}
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/regex/icu.hpp>
|
||||
|
||||
#include "gnc-price-import.hpp"
|
||||
#include "gnc-price-props.hpp"
|
||||
#include "gnc-csv-tokenizer.hpp"
|
||||
#include "gnc-fw-tokenizer.hpp"
|
||||
#include "gnc-csv-price-import-settings.hpp"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
|
||||
|
||||
const int num_currency_formats_price = 3;
|
||||
const gchar* currency_format_user_price[] = {N_("Locale"),
|
||||
N_("Period: 123,456.78"),
|
||||
N_("Comma: 123.456,78")
|
||||
};
|
||||
|
||||
|
||||
/** Constructor for GncPriceImport.
|
||||
*/
|
||||
GncPriceImport::GncPriceImport(GncImpFileFormat format)
|
||||
{
|
||||
/* All of the data pointers are initially NULL. This is so that, if
|
||||
* gnc_csv_parse_data_free is called before all of the data is
|
||||
* initialized, only the data that needs to be freed is freed. */
|
||||
m_skip_errors = false;
|
||||
file_format(m_settings.m_file_format = format);
|
||||
}
|
||||
|
||||
/** Destructor for GncPriceImport.
|
||||
*/
|
||||
GncPriceImport::~GncPriceImport()
|
||||
{
|
||||
}
|
||||
|
||||
/** Sets the file format for the file to import, which
|
||||
* may cause the file to be reloaded as well if the
|
||||
* previously set file format was different and a
|
||||
* filename was already set.
|
||||
* @param format the new format to set
|
||||
* @exception std::ifstream::failure if file reloading fails
|
||||
*/
|
||||
void GncPriceImport::file_format(GncImpFileFormat format)
|
||||
{
|
||||
if (m_tokenizer && m_settings.m_file_format == format)
|
||||
return;
|
||||
|
||||
auto new_encoding = std::string("UTF-8");
|
||||
auto new_imp_file = std::string();
|
||||
|
||||
// Recover common settings from old tokenizer
|
||||
if (m_tokenizer)
|
||||
{
|
||||
new_encoding = m_tokenizer->encoding();
|
||||
new_imp_file = m_tokenizer->current_file();
|
||||
if (file_format() == GncImpFileFormat::FIXED_WIDTH)
|
||||
{
|
||||
auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
|
||||
if (!fwtok->get_columns().empty())
|
||||
m_settings.m_column_widths = fwtok->get_columns();
|
||||
}
|
||||
}
|
||||
|
||||
m_settings.m_file_format = format;
|
||||
m_tokenizer = gnc_tokenizer_factory(m_settings.m_file_format);
|
||||
|
||||
// Set up new tokenizer with common settings
|
||||
// recovered from old tokenizer
|
||||
m_tokenizer->encoding(new_encoding);
|
||||
load_file(new_imp_file);
|
||||
|
||||
// Restore potentially previously set separators or column_widths
|
||||
if ((file_format() == GncImpFileFormat::CSV)
|
||||
&& !m_settings.m_separators.empty())
|
||||
separators (m_settings.m_separators);
|
||||
else if ((file_format() == GncImpFileFormat::FIXED_WIDTH)
|
||||
&& !m_settings.m_column_widths.empty())
|
||||
{
|
||||
auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
|
||||
fwtok->columns (m_settings.m_column_widths);
|
||||
}
|
||||
}
|
||||
|
||||
GncImpFileFormat GncPriceImport::file_format()
|
||||
{
|
||||
return m_settings.m_file_format;
|
||||
}
|
||||
|
||||
void GncPriceImport::over_write (bool over)
|
||||
{
|
||||
m_over_write = over;
|
||||
}
|
||||
bool GncPriceImport::over_write () { return m_over_write; }
|
||||
|
||||
/** Sets a from commodity. This is the commodity all import data relates to.
|
||||
* When a from commodity is set, there can't be any from columns selected
|
||||
* in the import data.
|
||||
* @param from_commodity pointer to a commodity or NULL.
|
||||
*/
|
||||
void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
|
||||
{
|
||||
m_settings.m_from_commodity = from_commodity;
|
||||
if (m_settings.m_from_commodity)
|
||||
{
|
||||
auto col_type_comm = std::find (m_settings.m_column_types_price.begin(),
|
||||
m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY);
|
||||
|
||||
if (col_type_comm != m_settings.m_column_types_price.end())
|
||||
set_column_type_price (col_type_comm -m_settings.m_column_types_price.begin(),
|
||||
GncPricePropType::NONE);
|
||||
|
||||
// force a refresh of the to_currency if the from_commodity is changed
|
||||
std::vector<GncPricePropType> commodities = { GncPricePropType::TO_CURRENCY };
|
||||
reset_formatted_column (commodities);
|
||||
}
|
||||
}
|
||||
gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; }
|
||||
|
||||
/** Sets a to currency. This is the to currency all import data relates to.
|
||||
* When a to currency is set, there can't be any to currency columns selected
|
||||
* in the import data.
|
||||
* @param to_currency pointer to a commodity or NULL.
|
||||
*/
|
||||
void GncPriceImport::to_currency (gnc_commodity* to_currency)
|
||||
{
|
||||
m_settings.m_to_currency = to_currency;
|
||||
if (m_settings.m_to_currency)
|
||||
{
|
||||
auto col_type_currency = std::find (m_settings.m_column_types_price.begin(),
|
||||
m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY);
|
||||
|
||||
if (col_type_currency != m_settings.m_column_types_price.end())
|
||||
set_column_type_price (col_type_currency -m_settings.m_column_types_price.begin(),
|
||||
GncPricePropType::NONE);
|
||||
|
||||
// force a refresh of the from_commodity if the to_currency is changed
|
||||
std::vector<GncPricePropType> commodities = { GncPricePropType::FROM_COMMODITY };
|
||||
reset_formatted_column (commodities);
|
||||
}
|
||||
}
|
||||
gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; }
|
||||
|
||||
void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
|
||||
{
|
||||
for (auto col_type: col_types)
|
||||
{
|
||||
auto col = std::find (m_settings.m_column_types_price.begin(),
|
||||
m_settings.m_column_types_price.end(), col_type);
|
||||
if (col != m_settings.m_column_types_price.end())
|
||||
set_column_type_price (col - m_settings.m_column_types_price.begin(), col_type, true);
|
||||
}
|
||||
}
|
||||
|
||||
void GncPriceImport::currency_format (int currency_format)
|
||||
{
|
||||
m_settings.m_currency_format = currency_format;
|
||||
|
||||
/* Reparse all currency related columns */
|
||||
std::vector<GncPricePropType> commodities = { GncPricePropType::AMOUNT };
|
||||
reset_formatted_column (commodities);
|
||||
}
|
||||
int GncPriceImport::currency_format () { return m_settings.m_currency_format; }
|
||||
|
||||
void GncPriceImport::date_format (int date_format)
|
||||
{
|
||||
m_settings.m_date_format = date_format;
|
||||
|
||||
/* Reparse all date related columns */
|
||||
std::vector<GncPricePropType> dates = { GncPricePropType::DATE };
|
||||
reset_formatted_column (dates);
|
||||
}
|
||||
int GncPriceImport::date_format () { return m_settings.m_date_format; }
|
||||
|
||||
/** Converts raw file data using a new encoding. This function must be
|
||||
* called after load_file only if load_file guessed
|
||||
* the wrong encoding.
|
||||
* @param encoding Encoding that data should be translated using
|
||||
*/
|
||||
void GncPriceImport::encoding (const std::string& encoding)
|
||||
{
|
||||
// TODO investigate if we can catch conversion errors and report them
|
||||
if (m_tokenizer)
|
||||
{
|
||||
m_tokenizer->encoding(encoding); // May throw
|
||||
try
|
||||
{
|
||||
tokenize(false);
|
||||
}
|
||||
catch (...)
|
||||
{ };
|
||||
}
|
||||
|
||||
m_settings.m_encoding = encoding;
|
||||
}
|
||||
|
||||
std::string GncPriceImport::encoding () { return m_settings.m_encoding; }
|
||||
|
||||
void GncPriceImport::update_skipped_lines(boost::optional<uint32_t> start, boost::optional<uint32_t> end,
|
||||
boost::optional<bool> alt, boost::optional<bool> errors)
|
||||
{
|
||||
if (start)
|
||||
m_settings.m_skip_start_lines = *start;
|
||||
if (end)
|
||||
m_settings.m_skip_end_lines = *end;
|
||||
if (alt)
|
||||
m_settings.m_skip_alt_lines = *alt;
|
||||
if (errors)
|
||||
m_skip_errors = *errors;
|
||||
|
||||
for (uint32_t i = 0; i < m_parsed_lines.size(); i++)
|
||||
{
|
||||
std::get<PL_SKIP>(m_parsed_lines[i]) =
|
||||
((i < skip_start_lines()) || // start rows to skip
|
||||
(i >= m_parsed_lines.size() - skip_end_lines()) || // end rows to skip
|
||||
(((i - skip_start_lines()) % 2 == 1) && // skip every second row...
|
||||
skip_alt_lines()) || // ...if requested
|
||||
(m_skip_errors && !std::get<PL_ERROR>(m_parsed_lines[i]).empty())); // skip lines with errors
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GncPriceImport::skip_start_lines () { return m_settings.m_skip_start_lines; }
|
||||
uint32_t GncPriceImport::skip_end_lines () { return m_settings.m_skip_end_lines; }
|
||||
bool GncPriceImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; }
|
||||
bool GncPriceImport::skip_err_lines () { return m_skip_errors; }
|
||||
|
||||
void GncPriceImport::separators (std::string separators)
|
||||
{
|
||||
if (file_format() != GncImpFileFormat::CSV)
|
||||
return;
|
||||
|
||||
m_settings.m_separators = separators;
|
||||
auto csvtok = dynamic_cast<GncCsvTokenizer*>(m_tokenizer.get());
|
||||
csvtok->set_separators (separators);
|
||||
|
||||
}
|
||||
std::string GncPriceImport::separators () { return m_settings.m_separators; }
|
||||
|
||||
void GncPriceImport::settings (const CsvPriceImpSettings& settings)
|
||||
{
|
||||
/* First apply file format as this may recreate the tokenizer */
|
||||
file_format (settings.m_file_format);
|
||||
/* Only then apply the other settings */
|
||||
m_settings = settings;
|
||||
from_commodity (m_settings.m_from_commodity);
|
||||
to_currency (m_settings.m_to_currency);
|
||||
encoding (m_settings.m_encoding);
|
||||
|
||||
if (file_format() == GncImpFileFormat::CSV)
|
||||
separators (m_settings.m_separators);
|
||||
else if (file_format() == GncImpFileFormat::FIXED_WIDTH)
|
||||
{
|
||||
auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
|
||||
fwtok->columns (m_settings.m_column_widths);
|
||||
}
|
||||
try
|
||||
{
|
||||
tokenize(false);
|
||||
}
|
||||
catch (...)
|
||||
{ };
|
||||
|
||||
/* Tokenizing will clear column types, reset them here
|
||||
* based on the loaded settings.
|
||||
*/
|
||||
std::copy_n (settings.m_column_types_price.begin(),
|
||||
std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()),
|
||||
m_settings.m_column_types_price.begin());
|
||||
}
|
||||
|
||||
bool GncPriceImport::save_settings ()
|
||||
{
|
||||
if (preset_is_reserved_name (m_settings.m_name))
|
||||
return true;
|
||||
|
||||
/* separators are already copied to m_settings in the separators
|
||||
* function above. However this is not the case for the column
|
||||
* widths in fw mode, so do this now.
|
||||
*/
|
||||
if (file_format() == GncImpFileFormat::FIXED_WIDTH)
|
||||
{
|
||||
auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
|
||||
m_settings.m_column_widths = fwtok->get_columns();
|
||||
}
|
||||
return m_settings.save();
|
||||
}
|
||||
|
||||
void GncPriceImport::settings_name (std::string name) { m_settings.m_name = name; }
|
||||
std::string GncPriceImport::settings_name () { return m_settings.m_name; }
|
||||
|
||||
/** Loads a file into a GncPriceImport. This is the first function
|
||||
* that must be called after creating a new GncPriceImport. As long as
|
||||
* this function didn't run successfully, the importer can't proceed.
|
||||
* @param filename Name of the file that should be opened
|
||||
* @exception may throw std::ifstream::failure on any io error
|
||||
*/
|
||||
void GncPriceImport::load_file (const std::string& filename)
|
||||
{
|
||||
/* Get the raw data first and handle an error if one occurs. */
|
||||
try
|
||||
{
|
||||
m_tokenizer->load_file (filename);
|
||||
return;
|
||||
}
|
||||
catch (std::ifstream::failure& ios_err)
|
||||
{
|
||||
// Just log the error and pass it on the call stack for proper handling
|
||||
PWARN ("Error: %s", ios_err.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/** Splits a file into cells. This requires having an encoding that
|
||||
* works (see GncPriceImport::convert_encoding). Tokenizing related options
|
||||
* should be set to the user's selections before calling this
|
||||
* function.
|
||||
* Notes: - this function must be called with guessColTypes set to true once
|
||||
* before calling it with guessColTypes set to false.
|
||||
* - if guessColTypes is true, all the column types will be set
|
||||
* GncPricePropType::NONE right now as real guessing isn't implemented yet
|
||||
* @param guessColTypes true to guess what the types of columns are based on the cell contents
|
||||
* @exception std::range_error if tokenizing failed
|
||||
*/
|
||||
void GncPriceImport::tokenize (bool guessColTypes)
|
||||
{
|
||||
if (!m_tokenizer)
|
||||
return;
|
||||
|
||||
uint32_t max_cols = 0;
|
||||
m_tokenizer->tokenize();
|
||||
m_parsed_lines.clear();
|
||||
for (auto tokenized_line : m_tokenizer->get_tokens())
|
||||
{
|
||||
m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
|
||||
std::make_shared<GncImportPrice>(date_format(), currency_format()),
|
||||
false));
|
||||
auto length = tokenized_line.size();
|
||||
if (length > max_cols)
|
||||
max_cols = length;
|
||||
}
|
||||
|
||||
/* If it failed, generate an error. */
|
||||
if (m_parsed_lines.size() == 0)
|
||||
{
|
||||
throw (std::range_error ("Tokenizing failed."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.m_column_types_price.resize(max_cols, GncPricePropType::NONE);
|
||||
|
||||
/* Force reinterpretation of already set columns and/or base_account */
|
||||
for (uint32_t i = 0; i < m_settings.m_column_types_price.size(); i++)
|
||||
set_column_type_price (i, m_settings.m_column_types_price[i], true);
|
||||
|
||||
if (guessColTypes)
|
||||
{
|
||||
/* Guess column_types based
|
||||
* on the contents of each column. */
|
||||
/* TODO Make it actually guess. */
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorListPrice
|
||||
{
|
||||
public:
|
||||
void add_error (std::string msg);
|
||||
std::string str();
|
||||
bool empty() { return m_error.empty(); }
|
||||
private:
|
||||
std::string m_error;
|
||||
};
|
||||
|
||||
void ErrorListPrice::add_error (std::string msg)
|
||||
{
|
||||
m_error += "- " + msg + "\n";
|
||||
}
|
||||
|
||||
std::string ErrorListPrice::str()
|
||||
{
|
||||
return m_error.substr(0, m_error.size() - 1);
|
||||
}
|
||||
|
||||
/* Test for the required minimum number of columns selected and
|
||||
* the selection is consistent.
|
||||
* @param An ErrorListPrice object to which all found issues are added.
|
||||
*/
|
||||
void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
|
||||
{
|
||||
/* Verify if a date column is selected and it's parsable.
|
||||
*/
|
||||
if (!check_for_column_type(GncPricePropType::DATE))
|
||||
error_msg.add_error( _("Please select a date column."));
|
||||
|
||||
/* Verify an amount column is selected.
|
||||
*/
|
||||
if (!check_for_column_type(GncPricePropType::AMOUNT))
|
||||
error_msg.add_error( _("Please select an amount column."));
|
||||
|
||||
/* Verify a Currency to column is selected.
|
||||
*/
|
||||
if (!check_for_column_type(GncPricePropType::TO_CURRENCY))
|
||||
{
|
||||
if (!m_settings.m_to_currency)
|
||||
error_msg.add_error( _("Please select a 'Currency to' column or set a Currency in the 'Currency To' field."));
|
||||
}
|
||||
|
||||
/* Verify a Commodity from column is selected.
|
||||
*/
|
||||
if (!check_for_column_type(GncPricePropType::FROM_COMMODITY))
|
||||
{
|
||||
if (!m_settings.m_from_commodity)
|
||||
error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field."));
|
||||
}
|
||||
|
||||
/* Verify a 'Commodity from' does not equal 'Currency to'.
|
||||
*/
|
||||
if ((m_settings.m_to_currency) && (m_settings.m_from_commodity))
|
||||
{
|
||||
if (gnc_commodity_equal (m_settings.m_to_currency, m_settings.m_from_commodity))
|
||||
error_msg.add_error( _("'Commodity From' can not be the same as 'Currency To'."));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether the chosen settings can successfully parse
|
||||
* the import data. This will check:
|
||||
* - there's at least one line selected for import
|
||||
* - the minimum number of columns is selected
|
||||
* - the values in the selected columns can be parsed meaningfully.
|
||||
* @return An empty string if all checks passed or the reason
|
||||
* verification failed otherwise.
|
||||
*/
|
||||
std::string GncPriceImport::verify ()
|
||||
{
|
||||
auto newline = std::string();
|
||||
auto error_msg = ErrorListPrice();
|
||||
|
||||
/* Check if the import file did actually contain any information */
|
||||
if (m_parsed_lines.size() == 0)
|
||||
{
|
||||
error_msg.add_error(_("No valid data found in the selected file. It may be empty or the selected encoding is wrong."));
|
||||
return error_msg.str();
|
||||
}
|
||||
|
||||
/* Check if at least one line is selected for importing */
|
||||
auto skip_alt_offset = m_settings.m_skip_alt_lines ? 1 : 0;
|
||||
if (m_settings.m_skip_start_lines + m_settings.m_skip_end_lines + skip_alt_offset >= m_parsed_lines.size())
|
||||
{
|
||||
error_msg.add_error(_("No lines are selected for importing. Please reduce the number of lines to skip."));
|
||||
return error_msg.str();
|
||||
}
|
||||
|
||||
verify_column_selections (error_msg);
|
||||
|
||||
update_skipped_lines (boost::none, boost::none, boost::none, boost::none);
|
||||
|
||||
auto have_line_errors = false;
|
||||
for (auto line : m_parsed_lines)
|
||||
{
|
||||
if (!std::get<PL_SKIP>(line) && !std::get<PL_ERROR>(line).empty())
|
||||
{
|
||||
have_line_errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_line_errors)
|
||||
error_msg.add_error( _("Not all fields could be parsed. Please correct the issues reported for each line or adjust the lines to skip."));
|
||||
|
||||
return error_msg.str();
|
||||
}
|
||||
|
||||
/** Checks whether the parsed line contains all essential properties.
|
||||
* @param parsed_line The line we are checking
|
||||
* @exception std::invalid_argument in an essential property is missing
|
||||
*/
|
||||
static void price_properties_verify_essentials (std::vector<parse_line_t>::iterator& parsed_line)
|
||||
{
|
||||
std::string error_message;
|
||||
std::shared_ptr<GncImportPrice> price_props;
|
||||
std::tie(std::ignore, error_message, price_props, std::ignore) = *parsed_line;
|
||||
|
||||
auto price_error = price_props->verify_essentials();
|
||||
|
||||
error_message.clear();
|
||||
if (!price_error.empty())
|
||||
{
|
||||
error_message += price_error;
|
||||
error_message += "\n";
|
||||
}
|
||||
|
||||
if (!error_message.empty())
|
||||
throw std::invalid_argument(error_message);
|
||||
}
|
||||
|
||||
void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_line)
|
||||
{
|
||||
StrVec line;
|
||||
std::string error_message;
|
||||
std::shared_ptr<GncImportPrice> price_props = nullptr;
|
||||
bool skip_line = false;
|
||||
std::tie(line, error_message, price_props, skip_line) = *parsed_line;
|
||||
|
||||
if (skip_line)
|
||||
return;
|
||||
|
||||
error_message.clear();
|
||||
|
||||
// Add a TO_CURRENCY property with the selected 'currency to' if no 'currency to' column was set by the user
|
||||
auto line_to_currency = price_props->get_to_currency();
|
||||
if (!line_to_currency)
|
||||
{
|
||||
if (m_settings.m_to_currency)
|
||||
price_props->set_to_currency(m_settings.m_to_currency);
|
||||
else
|
||||
{
|
||||
// Oops - the user didn't select a 'currency to' column *and* we didn't get a selected value either!
|
||||
// Note if you get here this suggests a bug in the code!
|
||||
error_message = _("No 'Currency to' column selected and no selected Currency specified either.\n"
|
||||
"This should never happen. Please report this as a bug.");
|
||||
PINFO("User warning: %s", error_message.c_str());
|
||||
throw std::invalid_argument(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a FROM_COMMODITY property with the selected 'commodity from' if no 'commodity from' column was set by the user
|
||||
auto line_from_commodity = price_props->get_from_commodity();
|
||||
if (!line_from_commodity)
|
||||
{
|
||||
if (m_settings.m_from_commodity)
|
||||
price_props->set_from_commodity(m_settings.m_from_commodity);
|
||||
else
|
||||
{
|
||||
// Oops - the user didn't select a 'commodity from' column *and* we didn't get a selected value either!
|
||||
// Note if you get here this suggests a bug in the code!
|
||||
error_message = _("No 'Commodity from' column selected and no selected Commodity specified either.\n"
|
||||
"This should never happen. Please report this as a bug.");
|
||||
PINFO("User warning: %s", error_message.c_str());
|
||||
throw std::invalid_argument(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
/* If column parsing was successful, convert price properties into a price. */
|
||||
try
|
||||
{
|
||||
price_properties_verify_essentials (parsed_line);
|
||||
|
||||
QofBook* book = gnc_get_current_book();
|
||||
GNCPriceDB *pdb = gnc_pricedb_get_db (book);
|
||||
|
||||
/* If all went well, add this price to the list. */
|
||||
auto price_created = price_props->create_price (book, pdb, m_over_write);
|
||||
if (price_created == ADDED)
|
||||
m_prices_added++;
|
||||
else if (price_created == DUPLICATED)
|
||||
m_prices_duplicated++;
|
||||
else if (price_created == REPLACED)
|
||||
m_prices_replaced++;
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error_message = e.what();
|
||||
PINFO("User warning: %s", error_message.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a list of prices from parsed data. The parsed data
|
||||
* will first be validated. If any errors are found in lines that are marked
|
||||
* for processing (ie not marked to skip) this function will
|
||||
* throw an error.
|
||||
* @param skip_errors true skip over lines with errors
|
||||
* @exception throws std::invalid_argument if data validation or processing fails.
|
||||
*/
|
||||
void GncPriceImport::create_prices ()
|
||||
{
|
||||
/* Start with verifying the current data. */
|
||||
auto verify_result = verify();
|
||||
if (!verify_result.empty())
|
||||
throw std::invalid_argument (verify_result);
|
||||
|
||||
m_prices_added = 0;
|
||||
m_prices_duplicated = 0;
|
||||
m_prices_replaced = 0;
|
||||
|
||||
/* Iterate over all parsed lines */
|
||||
for (auto parsed_lines_it = m_parsed_lines.begin();
|
||||
parsed_lines_it != m_parsed_lines.end();
|
||||
++parsed_lines_it)
|
||||
{
|
||||
/* Skip current line if the user specified so */
|
||||
if ((std::get<PL_SKIP>(*parsed_lines_it)))
|
||||
continue;
|
||||
|
||||
/* Should not throw anymore, otherwise verify needs revision */
|
||||
create_price (parsed_lines_it);
|
||||
}
|
||||
PINFO("Number of lines is %d, added %d, duplicated %d, replaced %d",
|
||||
(int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated, m_prices_replaced);
|
||||
}
|
||||
|
||||
bool
|
||||
GncPriceImport::check_for_column_type (GncPricePropType type)
|
||||
{
|
||||
return (std::find (m_settings.m_column_types_price.begin(),
|
||||
m_settings.m_column_types_price.end(), type)
|
||||
!= m_settings.m_column_types_price.end());
|
||||
}
|
||||
|
||||
/* A helper function intended to be called only from set_column_type_price */
|
||||
void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type)
|
||||
{
|
||||
if (prop_type == GncPricePropType::NONE)
|
||||
return; /* Only deal with price related properties. */
|
||||
|
||||
auto price_props = std::make_shared<GncImportPrice> (*(std::get<PL_PREPRICE>(m_parsed_lines[row])).get());
|
||||
|
||||
if (col >= std::get<PL_INPUT>(m_parsed_lines[row]).size())
|
||||
price_props->reset (prop_type); //reset errors
|
||||
else
|
||||
{
|
||||
auto value = std::get<PL_INPUT>(m_parsed_lines[row]).at(col);
|
||||
bool enable_test_empty = true;
|
||||
try
|
||||
{
|
||||
// set the from_commodity based on combo so we can test for same.
|
||||
if (prop_type == GncPricePropType::TO_CURRENCY)
|
||||
{
|
||||
if (m_settings.m_from_commodity)
|
||||
price_props->set_from_commodity (m_settings.m_from_commodity);
|
||||
|
||||
if (m_settings.m_to_currency)
|
||||
enable_test_empty = false;
|
||||
}
|
||||
// set the to_currency based on combo so we can test for same.
|
||||
if (prop_type == GncPricePropType::FROM_COMMODITY)
|
||||
{
|
||||
if (m_settings.m_to_currency)
|
||||
price_props->set_to_currency (m_settings.m_to_currency);
|
||||
|
||||
if (m_settings.m_from_commodity)
|
||||
enable_test_empty = false;
|
||||
}
|
||||
price_props->set(prop_type, value, enable_test_empty);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
/* Do nothing, just prevent the exception from escalating up
|
||||
* However log the error if it happens on a row that's not skipped
|
||||
*/
|
||||
if (!std::get<PL_SKIP>(m_parsed_lines[row]))
|
||||
PINFO("User warning: %s", e.what());
|
||||
}
|
||||
}
|
||||
/* Store the result */
|
||||
std::get<PL_PREPRICE>(m_parsed_lines[row]) = price_props;
|
||||
}
|
||||
|
||||
void
|
||||
GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, bool force)
|
||||
{
|
||||
if (position >= m_settings.m_column_types_price.size())
|
||||
return;
|
||||
|
||||
auto old_type = m_settings.m_column_types_price[position];
|
||||
if ((type == old_type) && !force)
|
||||
return; /* Nothing to do */
|
||||
|
||||
// Column types should be unique, so remove any previous occurrence of the new type
|
||||
std::replace(m_settings.m_column_types_price.begin(), m_settings.m_column_types_price.end(),
|
||||
type, GncPricePropType::NONE);
|
||||
|
||||
m_settings.m_column_types_price.at (position) = type;
|
||||
|
||||
// If the user has set a 'commodity from' column, we can't have a commodity from selected
|
||||
if (type == GncPricePropType::FROM_COMMODITY)
|
||||
from_commodity (nullptr);
|
||||
|
||||
// If the user has set a 'currency to' column, we can't have a currency to selected
|
||||
if (type == GncPricePropType::TO_CURRENCY)
|
||||
to_currency (nullptr);
|
||||
|
||||
/* Update the preparsed data */
|
||||
for (auto parsed_lines_it = m_parsed_lines.begin();
|
||||
parsed_lines_it != m_parsed_lines.end();
|
||||
++parsed_lines_it)
|
||||
{
|
||||
/* Reset date and currency formats for each price props object
|
||||
* to ensure column updates use the most recent one
|
||||
*/
|
||||
std::get<PL_PREPRICE>(*parsed_lines_it)->set_date_format (m_settings.m_date_format);
|
||||
std::get<PL_PREPRICE>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format);
|
||||
|
||||
uint32_t row = parsed_lines_it - m_parsed_lines.begin();
|
||||
|
||||
/* If the column type actually changed, first reset the property
|
||||
* represented by the old column type
|
||||
*/
|
||||
if (old_type != type)
|
||||
{
|
||||
auto old_col = std::get<PL_INPUT>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset!
|
||||
if ((old_type > GncPricePropType::NONE)
|
||||
&& (old_type <= GncPricePropType::PRICE_PROPS))
|
||||
update_price_props (row, old_col, old_type);
|
||||
}
|
||||
/* Then set the property represented by the new column type */
|
||||
if ((type > GncPricePropType::NONE)
|
||||
&& (type <= GncPricePropType::PRICE_PROPS))
|
||||
update_price_props (row, position, type);
|
||||
|
||||
/* Report errors if there are any */
|
||||
auto price_errors = std::get<PL_PREPRICE>(*parsed_lines_it)->errors();
|
||||
std::get<PL_ERROR>(*parsed_lines_it) =
|
||||
price_errors +
|
||||
(price_errors.empty() ? std::string() : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<GncPricePropType> GncPriceImport::column_types_price ()
|
||||
{
|
||||
return m_settings.m_column_types_price;
|
||||
}
|
||||
|
173
gnucash/import-export/csv-imp/gnc-price-import.hpp
Normal file
173
gnucash/import-export/csv-imp/gnc-price-import.hpp
Normal file
@ -0,0 +1,173 @@
|
||||
/********************************************************************\
|
||||
* gnc-price-import.hpp - import prices from csv files *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
|
||||
/** @file
|
||||
@brief Class to import prices from CSV or fixed width files
|
||||
*
|
||||
gnc-price-import.hpp
|
||||
@author Copyright (c) 2015 Geert Janssens <geert@kobaltwit.be>
|
||||
@author Copyright (c) 2017 Robert Fewell
|
||||
*/
|
||||
|
||||
#ifndef GNC_PRICE_IMPORT_HPP
|
||||
#define GNC_PRICE_IMPORT_HPP
|
||||
|
||||
extern "C" {
|
||||
#include "config.h"
|
||||
#include "gnc-commodity.h"
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "gnc-tokenizer.hpp"
|
||||
#include "gnc-price-props.hpp"
|
||||
#include "gnc-csv-price-import-settings.hpp"
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
/* A set of currency formats that the user sees. */
|
||||
extern const int num_currency_formats_price;
|
||||
extern const gchar* currency_format_user_price[];
|
||||
|
||||
/** An enum describing the columns found in a parse_line_t. Currently these are:
|
||||
* - a tokenized line of input
|
||||
* - an optional error string
|
||||
* - a struct to hold user selected properties for a price
|
||||
* - a boolean to mark the line as skipped by error and/or user or not */
|
||||
enum parse_line_cols {
|
||||
PL_INPUT,
|
||||
PL_ERROR,
|
||||
PL_PREPRICE,
|
||||
PL_SKIP
|
||||
};
|
||||
|
||||
/** Tuple to hold
|
||||
* - a tokenized line of input
|
||||
* - an optional error string
|
||||
* - a struct to hold user selected properties for a price */
|
||||
using parse_line_t = std::tuple<StrVec,
|
||||
std::string,
|
||||
std::shared_ptr<GncImportPrice>,
|
||||
bool>;
|
||||
struct ErrorListPrice;
|
||||
|
||||
/** The actual PriceImport class
|
||||
* It's intended to use in the following sequence of actions:
|
||||
* - set a file format
|
||||
* - load a file
|
||||
* - optionally convert it's encoding
|
||||
* - parse the file into lines, which in turn are split up in to columns
|
||||
* the result of this step can be queried from tokenizer
|
||||
* - the user should now map the columns to types, which is stored in column_types
|
||||
* - last step is convert the mapped columns into a list of prices to add */
|
||||
class GncPriceImport
|
||||
{
|
||||
public:
|
||||
// Constructor - Destructor
|
||||
GncPriceImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
|
||||
~GncPriceImport();
|
||||
|
||||
void file_format(GncImpFileFormat format);
|
||||
GncImpFileFormat file_format();
|
||||
|
||||
void over_write (bool over);
|
||||
bool over_write ();
|
||||
|
||||
void from_commodity (gnc_commodity *from_commodity);
|
||||
gnc_commodity *from_commodity ();
|
||||
|
||||
void to_currency (gnc_commodity *to_currency);
|
||||
gnc_commodity *to_currency ();
|
||||
|
||||
void currency_format (int currency_format);
|
||||
int currency_format ();
|
||||
|
||||
void date_format (int date_format);
|
||||
int date_format ();
|
||||
|
||||
void encoding (const std::string& encoding);
|
||||
std::string encoding ();
|
||||
|
||||
void update_skipped_lines (boost::optional<uint32_t> start, boost::optional<uint32_t> end,
|
||||
boost::optional<bool> alt, boost::optional<bool> errors);
|
||||
uint32_t skip_start_lines ();
|
||||
uint32_t skip_end_lines ();
|
||||
bool skip_alt_lines ();
|
||||
bool skip_err_lines ();
|
||||
|
||||
void separators (std::string separators);
|
||||
std::string separators ();
|
||||
|
||||
void settings (const CsvPriceImpSettings& settings);
|
||||
bool save_settings ();
|
||||
|
||||
void settings_name (std::string name);
|
||||
std::string settings_name ();
|
||||
|
||||
|
||||
void load_file (const std::string& filename);
|
||||
|
||||
void tokenize (bool guessColTypes);
|
||||
|
||||
std::string verify();
|
||||
|
||||
/** This function will attempt to convert all tokenized lines into
|
||||
* prices using the column types the user has set.
|
||||
*/
|
||||
void create_prices ();
|
||||
bool check_for_column_type (GncPricePropType type);
|
||||
void set_column_type_price (uint32_t position, GncPricePropType type, bool force = false);
|
||||
std::vector<GncPricePropType> column_types_price ();
|
||||
|
||||
std::unique_ptr<GncTokenizer> m_tokenizer; /**< Will handle file loading/encoding conversion/splitting into fields */
|
||||
std::vector<parse_line_t> m_parsed_lines; /**< source file parsed into a two-dimensional array of strings.
|
||||
Per line also holds possible error messages and objects with extracted
|
||||
price properties. */
|
||||
int m_prices_added;
|
||||
int m_prices_duplicated;
|
||||
int m_prices_replaced;
|
||||
|
||||
private:
|
||||
/** A helper function used by create_prices. It will attempt
|
||||
* to convert a single tokenized line into a price using
|
||||
* the column types the user has set.
|
||||
*/
|
||||
void create_price (std::vector<parse_line_t>::iterator& parsed_line);
|
||||
|
||||
void verify_column_selections (ErrorListPrice& error_msg);
|
||||
|
||||
/* Internal helper function to force reparsing of columns subject to format changes */
|
||||
void reset_formatted_column (std::vector<GncPricePropType>& col_types);
|
||||
|
||||
/* Two internal helper functions that should only be called from within
|
||||
* set_column_type_price for consistency (otherwise error messages may not be (re)set)
|
||||
*/
|
||||
void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
|
||||
|
||||
CsvPriceImpSettings m_settings;
|
||||
bool m_skip_errors;
|
||||
bool m_over_write;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
349
gnucash/import-export/csv-imp/gnc-price-props.cpp
Normal file
349
gnucash/import-export/csv-imp/gnc-price-props.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
/********************************************************************\
|
||||
* gnc-price-props.cpp - encapsulate price properties for use *
|
||||
* in the csv importer *
|
||||
* *
|
||||
* 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 <platform.h>
|
||||
#if PLATFORM(WINDOWS)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "engine-helpers.h"
|
||||
#include "gnc-ui-util.h"
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/regex/icu.hpp>
|
||||
#include "gnc-price-props.hpp"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
|
||||
|
||||
/* This map contains a set of strings representing the different column types. */
|
||||
std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
|
||||
{ GncPricePropType::NONE, N_("None") },
|
||||
{ GncPricePropType::DATE, N_("Date") },
|
||||
{ GncPricePropType::AMOUNT, N_("Amount") },
|
||||
{ GncPricePropType::FROM_COMMODITY, N_("Commodity From") },
|
||||
{ GncPricePropType::TO_CURRENCY, N_("Currency To") },
|
||||
};
|
||||
|
||||
/** Convert str into a GncNumeric using the user-specified (import) currency format.
|
||||
* @param str The string to be parsed
|
||||
* @param currency_format The currency format to use.
|
||||
* @return a GncNumeric
|
||||
* @exception May throw std::invalid argument if string can't be parsed properly
|
||||
*/
|
||||
GncNumeric parse_amount_price (const std::string &str, int currency_format)
|
||||
{
|
||||
/* If a cell is empty or just spaces return invalid amount */
|
||||
if(!boost::regex_search(str, boost::regex("[0-9]")))
|
||||
throw std::invalid_argument (_("Value doesn't appear to contain a valid number."));
|
||||
|
||||
auto expr = boost::make_u32regex("[[:Sc:]]");
|
||||
std::string str_no_symbols = boost::u32regex_replace(str, expr, "");
|
||||
|
||||
/* Convert based on user chosen currency format */
|
||||
gnc_numeric val;
|
||||
char *endptr;
|
||||
switch (currency_format)
|
||||
{
|
||||
case 0:
|
||||
/* Currency locale */
|
||||
if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr)))
|
||||
throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format."));
|
||||
break;
|
||||
case 1:
|
||||
/* Currency decimal period */
|
||||
if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
|
||||
throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format."));
|
||||
break;
|
||||
case 2:
|
||||
/* Currency decimal comma */
|
||||
if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
|
||||
throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format."));
|
||||
break;
|
||||
}
|
||||
|
||||
return GncNumeric(val);
|
||||
}
|
||||
|
||||
/** Convert comm_str into a gnc_commodity.
|
||||
* @param comm_str The string to be parsed
|
||||
* @return a gnc_commodity
|
||||
* @exception May throw std::invalid argument if string can't be parsed properly
|
||||
*/
|
||||
gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
|
||||
{
|
||||
if (comm_str.empty())
|
||||
return nullptr;
|
||||
|
||||
auto table = gnc_commodity_table_get_table (gnc_get_current_book());
|
||||
gnc_commodity* comm = nullptr;
|
||||
|
||||
/* First try commodity as a unique name. */
|
||||
if (comm_str.find("::"))
|
||||
comm = gnc_commodity_table_lookup_unique (table, comm_str.c_str());
|
||||
|
||||
/* Then try mnemonic in the currency namespace */
|
||||
if (!comm)
|
||||
comm = gnc_commodity_table_lookup (table,
|
||||
GNC_COMMODITY_NS_CURRENCY, comm_str.c_str());
|
||||
|
||||
if (!comm)
|
||||
{
|
||||
/* If that fails try mnemonic in all other namespaces */
|
||||
auto namespaces = gnc_commodity_table_get_namespaces(table);
|
||||
for (auto ns = namespaces; ns; ns = ns->next)
|
||||
{
|
||||
gchar* ns_str = (gchar*)ns->data;
|
||||
if (g_utf8_collate(ns_str, GNC_COMMODITY_NS_CURRENCY) == 0)
|
||||
continue;
|
||||
|
||||
comm = gnc_commodity_table_lookup (table,
|
||||
ns_str, comm_str.c_str());
|
||||
if (comm)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!comm)
|
||||
throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
|
||||
else
|
||||
return comm;
|
||||
}
|
||||
|
||||
void GncImportPrice::set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Drop any existing error for the prop_type we're about to set
|
||||
m_errors.erase(prop_type);
|
||||
|
||||
// conditional test for empty values
|
||||
if (value.empty() && enable_test_empty)
|
||||
throw std::invalid_argument (_("Column value can not be empty."));
|
||||
|
||||
gnc_commodity *comm = nullptr;
|
||||
switch (prop_type)
|
||||
{
|
||||
case GncPricePropType::DATE:
|
||||
m_date = boost::none;
|
||||
m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails
|
||||
break;
|
||||
|
||||
case GncPricePropType::AMOUNT:
|
||||
m_amount = boost::none;
|
||||
m_amount = parse_amount_price (value, m_currency_format); // Throws if parsing fails
|
||||
break;
|
||||
|
||||
case GncPricePropType::FROM_COMMODITY:
|
||||
m_from_commodity = boost::none;
|
||||
comm = parse_commodity_price_comm (value); // Throws if parsing fails
|
||||
if (comm)
|
||||
{
|
||||
if (m_to_currency == comm)
|
||||
throw std::invalid_argument (_("'Commodity From' can not be the same as 'Currency To' column type."));
|
||||
m_from_commodity = comm;
|
||||
}
|
||||
break;
|
||||
|
||||
case GncPricePropType::TO_CURRENCY:
|
||||
m_to_currency = boost::none;
|
||||
comm = parse_commodity_price_comm (value); // Throws if parsing fails
|
||||
if (comm)
|
||||
{
|
||||
if (m_from_commodity == comm)
|
||||
throw std::invalid_argument (_("'Currency To' can not be the same as 'Commodity From' column type."));
|
||||
if (gnc_commodity_is_currency (comm) != true)
|
||||
throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type."));
|
||||
m_to_currency = comm;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Issue a warning for all other prop_types. */
|
||||
PWARN ("%d is an invalid property for a Price", static_cast<int>(prop_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) +
|
||||
std::string(_(" could not be understood.\n")) +
|
||||
e.what();
|
||||
m_errors.emplace(prop_type, err_str);
|
||||
throw std::invalid_argument (err_str);
|
||||
}
|
||||
catch (const std::out_of_range& e)
|
||||
{
|
||||
auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) +
|
||||
std::string(_(" could not be understood.\n")) +
|
||||
e.what();
|
||||
m_errors.emplace(prop_type, err_str);
|
||||
throw std::invalid_argument (err_str);
|
||||
}
|
||||
}
|
||||
|
||||
void GncImportPrice::reset (GncPricePropType prop_type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// set enable_test_empty to false to allow empty values
|
||||
set (prop_type, std::string(), false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Set with an empty string will effectively clear the property
|
||||
// but can also set an error for the property. Clear that error here.
|
||||
m_errors.erase(prop_type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GncImportPrice::verify_essentials (void)
|
||||
{
|
||||
/* Make sure this price has the minimum required set of properties defined */
|
||||
if (m_date == boost::none)
|
||||
return _("No date column.");
|
||||
else if (m_amount == boost::none)
|
||||
return _("No amount column.");
|
||||
else if (m_to_currency == boost::none)
|
||||
return _("No 'Currency to' column.");
|
||||
else if (m_from_commodity == boost::none)
|
||||
return _("No 'Commodity from' column.");
|
||||
else if (gnc_commodity_equal (*m_from_commodity, *m_to_currency))
|
||||
return _("'Commodity from' can not be the same as 'Currency to'.");
|
||||
else
|
||||
return std::string();
|
||||
}
|
||||
|
||||
Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
|
||||
{
|
||||
/* Gently refuse to create the price if the basics are not set correctly
|
||||
* This should have been tested before calling this function though!
|
||||
*/
|
||||
auto check = verify_essentials();
|
||||
if (!check.empty())
|
||||
{
|
||||
PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str());
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
Timespec date;
|
||||
timespecFromTime64 (&date, static_cast<time64>(GncDateTime(*m_date, DayPart::neutral)));
|
||||
date.tv_nsec = 0;
|
||||
|
||||
bool rev = false;
|
||||
auto amount = *m_amount;
|
||||
Result ret_val = ADDED;
|
||||
|
||||
GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date);
|
||||
|
||||
// Should old price be over writen
|
||||
if ((old_price != nullptr) && (over == true))
|
||||
{
|
||||
DEBUG("Over write");
|
||||
gnc_pricedb_remove_price (pdb, old_price);
|
||||
gnc_price_unref (old_price);
|
||||
old_price = nullptr;
|
||||
ret_val = REPLACED;
|
||||
}
|
||||
|
||||
if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import
|
||||
{
|
||||
// Check for currency in reverse direction.
|
||||
if (old_price != nullptr)
|
||||
{
|
||||
// Check for price in reverse direction.
|
||||
if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity))
|
||||
rev = true;
|
||||
}
|
||||
DEBUG("Commodity from is a Currency");
|
||||
|
||||
// Check for price less than 1, reverse if so.
|
||||
if (*m_amount < GncNumeric(1,1))
|
||||
rev = true;
|
||||
|
||||
}
|
||||
DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date),
|
||||
rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency),
|
||||
amount.to_string().c_str());
|
||||
|
||||
// Create the new price
|
||||
if (old_price == nullptr)
|
||||
{
|
||||
DEBUG("Create");
|
||||
GNCPrice *price = gnc_price_create (book);
|
||||
gnc_price_begin_edit (price);
|
||||
if (rev)
|
||||
{
|
||||
amount = amount.inv(); //invert the amount
|
||||
gnc_price_set_commodity (price, *m_to_currency);
|
||||
gnc_price_set_currency (price, *m_from_commodity);
|
||||
}
|
||||
else
|
||||
{
|
||||
gnc_price_set_commodity (price, *m_from_commodity);
|
||||
gnc_price_set_currency (price, *m_to_currency);
|
||||
}
|
||||
auto amount_conv = amount.convert<RoundType::half_up>(CURRENCY_DENOM);
|
||||
gnc_price_set_value (price, static_cast<gnc_numeric>(amount_conv));
|
||||
|
||||
gnc_price_set_time (price, date);
|
||||
gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE);
|
||||
//FIXME Not sure which one gnc_price_set_source (price, PRICE_SOURCE_FQ);
|
||||
gnc_price_set_typestr (price, PRICE_TYPE_LAST);
|
||||
gnc_price_commit_edit (price);
|
||||
|
||||
bool perr = gnc_pricedb_add_price (pdb, price);
|
||||
|
||||
gnc_price_unref (price);
|
||||
|
||||
if (perr == false)
|
||||
throw std::invalid_argument (_("Failed to create price from selected columns."));
|
||||
}
|
||||
else
|
||||
{
|
||||
gnc_price_unref (old_price);
|
||||
ret_val = DUPLICATED;
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static std::string gen_err_str (std::map<GncPricePropType, std::string>& errors)
|
||||
{
|
||||
auto full_error = std::string();
|
||||
for (auto error : errors)
|
||||
{
|
||||
full_error += (full_error.empty() ? "" : "\n") + error.second;
|
||||
}
|
||||
return full_error;
|
||||
}
|
||||
|
||||
std::string GncImportPrice::errors ()
|
||||
{
|
||||
return gen_err_str (m_errors);
|
||||
}
|
||||
|
116
gnucash/import-export/csv-imp/gnc-price-props.hpp
Normal file
116
gnucash/import-export/csv-imp/gnc-price-props.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/********************************************************************\
|
||||
* gnc-price-props.hpp - encapsulate price properties for use *
|
||||
* in the csv importer *
|
||||
* *
|
||||
* 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_PRICE_PROPS_HPP
|
||||
#define GNC_PRICE_PROPS_HPP
|
||||
|
||||
extern "C" {
|
||||
#include <platform.h>
|
||||
#if PLATFORM(WINDOWS)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include "gnc-pricedb.h"
|
||||
#include "gnc-commodity.h"
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include <gnc-datetime.hpp>
|
||||
#include <gnc-numeric.hpp>
|
||||
|
||||
/** Enumeration for column types. These are the different types of
|
||||
* columns that can exist in a CSV/Fixed-Width file. There should be
|
||||
* no two columns with the same type except for the GncPricePropType::NONE
|
||||
* type. */
|
||||
enum class GncPricePropType {
|
||||
NONE,
|
||||
DATE,
|
||||
AMOUNT,
|
||||
FROM_COMMODITY,
|
||||
TO_CURRENCY,
|
||||
PRICE_PROPS = TO_CURRENCY
|
||||
};
|
||||
|
||||
enum Result { FAILED, ADDED, DUPLICATED, REPLACED };
|
||||
|
||||
/** Maps all column types to a string representation.
|
||||
* The actual definition is in gnc-price-props.cpp.
|
||||
* Attention: that definition should be adjusted for any
|
||||
* changes to enum class GncPricePropType ! */
|
||||
extern std::map<GncPricePropType, const char*> gnc_price_col_type_strs;
|
||||
|
||||
/** Functor to check if the above map has an element of which
|
||||
* the value equals name. To be used with std::find_if.
|
||||
*/
|
||||
struct test_price_prop_type_str
|
||||
{
|
||||
test_price_prop_type_str( const char* name ) : m_name(name) {}
|
||||
bool operator()( const std::pair<GncPricePropType, const char*>& v ) const
|
||||
{
|
||||
return !g_strcmp0(v.second, m_name);
|
||||
}
|
||||
private:
|
||||
const char *m_name;
|
||||
};
|
||||
|
||||
gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
|
||||
GncNumeric parse_amount_price (const std::string &str, int currency_format);
|
||||
|
||||
struct GncImportPrice
|
||||
{
|
||||
public:
|
||||
GncImportPrice (int date_format, int currency_format) : m_date_format{date_format},
|
||||
m_currency_format{currency_format}{};
|
||||
|
||||
void set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty);
|
||||
void set_date_format (int date_format) { m_date_format = date_format ;}
|
||||
void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
|
||||
void reset (GncPricePropType prop_type);
|
||||
std::string verify_essentials (void);
|
||||
Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
|
||||
|
||||
gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }
|
||||
void set_from_commodity (gnc_commodity* comm) { if (comm) m_from_commodity = comm; else m_from_commodity = boost::none; }
|
||||
|
||||
gnc_commodity* get_to_currency () { if (m_to_currency) return *m_to_currency; else return nullptr; }
|
||||
void set_to_currency (gnc_commodity* curr) { if (curr) m_to_currency = curr; else m_to_currency = boost::none; }
|
||||
|
||||
std::string errors();
|
||||
|
||||
private:
|
||||
int m_date_format;
|
||||
int m_currency_format;
|
||||
boost::optional<GncDate> m_date;
|
||||
boost::optional<GncNumeric> m_amount;
|
||||
boost::optional<gnc_commodity*> m_from_commodity;
|
||||
boost::optional<gnc_commodity*> m_to_currency;
|
||||
bool created = false;
|
||||
|
||||
std::map<GncPricePropType, std::string> m_errors;
|
||||
};
|
||||
|
||||
#endif
|
@ -39,7 +39,7 @@ extern "C" {
|
||||
#include "gnc-trans-props.hpp"
|
||||
#include "gnc-csv-tokenizer.hpp"
|
||||
#include "gnc-fw-tokenizer.hpp"
|
||||
#include "gnc-csv-trans-settings.hpp"
|
||||
#include "gnc-csv-trans-import-settings.hpp"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
|
||||
|
||||
@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators)
|
||||
}
|
||||
std::string GncTxImport::separators () { return m_settings.m_separators; }
|
||||
|
||||
void GncTxImport::settings (const CsvTransSettings& settings)
|
||||
void GncTxImport::settings (const CsvTransImpSettings& settings)
|
||||
{
|
||||
/* First apply file format as this may recreate the tokenizer */
|
||||
file_format (settings.m_file_format);
|
||||
@ -329,7 +329,7 @@ void GncTxImport::settings (const CsvTransSettings& settings)
|
||||
bool GncTxImport::save_settings ()
|
||||
{
|
||||
|
||||
if (trans_preset_is_reserved_name (m_settings.m_name))
|
||||
if (preset_is_reserved_name (m_settings.m_name))
|
||||
return true;
|
||||
|
||||
/* separators are already copied to m_settings in the separators
|
||||
|
@ -43,7 +43,7 @@ extern "C" {
|
||||
|
||||
#include "gnc-tokenizer.hpp"
|
||||
#include "gnc-trans-props.hpp"
|
||||
#include "gnc-csv-trans-settings.hpp"
|
||||
#include "gnc-csv-trans-import-settings.hpp"
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ public:
|
||||
void separators (std::string separators);
|
||||
std::string separators ();
|
||||
|
||||
void settings (const CsvTransSettings& settings);
|
||||
void settings (const CsvTransImpSettings& settings);
|
||||
bool save_settings ();
|
||||
|
||||
void settings_name (std::string name);
|
||||
@ -189,8 +189,8 @@ private:
|
||||
void update_pre_trans_props (uint32_t row, uint32_t col, GncTransPropType prop_type);
|
||||
void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type);
|
||||
|
||||
struct CsvTranSettings;
|
||||
CsvTransSettings m_settings;
|
||||
struct CsvTranImpSettings; //FIXME do we need this line
|
||||
CsvTransImpSettings m_settings;
|
||||
bool m_skip_errors;
|
||||
bool m_req_mapped_accts;
|
||||
|
||||
|
@ -287,17 +287,21 @@ gnucash/import-export/csv-exp/gnc-plugin-csv-export.c
|
||||
[type: gettext/gsettings]gnucash/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in
|
||||
gnucash/import-export/csv-imp/assistant-csv-account-import.c
|
||||
gnucash/import-export/csv-imp/assistant-csv-account-import.glade
|
||||
gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
|
||||
gnucash/import-export/csv-imp/assistant-csv-price-import.glade
|
||||
gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
|
||||
gnucash/import-export/csv-imp/assistant-csv-trans-import.glade
|
||||
gnucash/import-export/csv-imp/csv-account-import.c
|
||||
gnucash/import-export/csv-imp/gnc-csv-account-map.c
|
||||
gnucash/import-export/csv-imp/gnc-csv-gnumeric-popup.c
|
||||
gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp
|
||||
gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
|
||||
gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
|
||||
gnucash/import-export/csv-imp/gnc-dummy-tokenizer.cpp
|
||||
gnucash/import-export/csv-imp/gnc-fw-tokenizer.cpp
|
||||
gnucash/import-export/csv-imp/gncmod-csv-import.c
|
||||
gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
|
||||
gnucash/import-export/csv-imp/gnc-price-import.cpp
|
||||
gnucash/import-export/csv-imp/gnc-price-props.cpp
|
||||
gnucash/import-export/csv-imp/gnc-tokenizer.cpp
|
||||
gnucash/import-export/csv-imp/gnc-trans-props.cpp
|
||||
gnucash/import-export/csv-imp/gnc-tx-import.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user