25 #include <glib/gi18n.h> 32 #include "engine-helpers.h" 39 #include <boost/locale.hpp> 40 #include <boost/regex.hpp> 41 #include <boost/regex/icu.hpp> 42 #include <gnc-locale-utils.hpp> 43 #include "gnc-imp-props-price.hpp" 46 namespace bl = boost::locale;
48 G_GNUC_UNUSED
static QofLogModule log_module = GNC_MOD_IMPORT;
51 std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
52 { GncPricePropType::NONE, N_(
"None") },
53 { GncPricePropType::DATE, N_(
"Date") },
54 { GncPricePropType::AMOUNT, N_(
"Amount") },
55 { GncPricePropType::FROM_SYMBOL, N_(
"From Symbol") },
56 { GncPricePropType::FROM_NAMESPACE, N_(
"From Namespace") },
57 { GncPricePropType::TO_CURRENCY, N_(
"Currency To") },
66 GncNumeric parse_amount_price (
const std::string &str,
int currency_format)
69 static constexpr ctll::fixed_string digit_re{
"[0-9]"};
70 if(!ctre::search<digit_re>(str))
71 throw std::invalid_argument (_(
"Value doesn't appear to contain a valid number."));
73 static const auto expr = boost::make_u32regex(
"[[:Sc:]]");
74 std::string str_no_symbols;
75 boost::u32regex_replace(icu::UnicodeString::fromUTF8(str), expr,
"").toUTF8String(str_no_symbols);
78 gnc_numeric val = gnc_numeric_zero();
80 switch (currency_format)
85 throw std::invalid_argument (_(
"Value can't be parsed into a number using the selected currency format."));
90 throw std::invalid_argument (_(
"Value can't be parsed into a number using the selected currency format."));
95 throw std::invalid_argument (_(
"Value can't be parsed into a number using the selected currency format."));
108 gnc_commodity* parse_commodity_price_comm (
const std::string& symbol_str,
const std::string& namespace_str)
110 if (symbol_str.empty())
114 gnc_commodity* comm =
nullptr;
117 comm = gnc_commodity_table_lookup_unique (
table, symbol_str.c_str());
122 comm = gnc_commodity_table_lookup (
table,
123 namespace_str.c_str(), symbol_str.c_str());
127 throw std::invalid_argument (_(
"Value can't be parsed into a valid commodity."));
137 bool parse_namespace (
const std::string& namespace_str)
139 if (namespace_str.empty())
147 throw std::invalid_argument (_(
"Value can't be parsed into a valid namespace."));
152 void GncImportPrice::set (GncPricePropType prop_type,
const std::string& value,
bool enable_test_empty)
157 m_errors.erase(prop_type);
160 if (value.empty() && enable_test_empty)
161 throw std::invalid_argument (_(
"Column value can not be empty."));
163 gnc_commodity *comm =
nullptr;
166 case GncPricePropType::DATE:
171 case GncPricePropType::AMOUNT:
173 m_amount = parse_amount_price (value, m_currency_format);
176 case GncPricePropType::FROM_SYMBOL:
177 m_from_symbol.reset();
180 throw std::invalid_argument (_(
"'From Symbol' can not be empty."));
182 m_from_symbol = value;
184 if (m_from_namespace)
186 comm = parse_commodity_price_comm (value, *m_from_namespace);
189 if (m_to_currency == comm)
190 throw std::invalid_argument (_(
"'Commodity From' can not be the same as 'Currency To'."));
191 m_from_commodity = comm;
196 case GncPricePropType::FROM_NAMESPACE:
197 m_from_namespace.reset();
200 throw std::invalid_argument (_(
"'From Namespace' can not be empty."));
202 if (parse_namespace (value))
204 m_from_namespace = value;
208 comm = parse_commodity_price_comm (*m_from_symbol, *m_from_namespace);
211 if (m_to_currency == comm)
212 throw std::invalid_argument (_(
"'Commodity From' can not be the same as 'Currency To'."));
213 m_from_commodity = comm;
219 case GncPricePropType::TO_CURRENCY:
220 m_to_currency.reset();
221 comm = parse_commodity_price_comm (value, GNC_COMMODITY_NS_CURRENCY);
224 if (m_from_commodity == comm)
225 throw std::invalid_argument (_(
"'Currency To' can not be the same as 'Commodity From'."));
227 throw std::invalid_argument (_(
"Value parsed into an invalid currency for a currency column type."));
228 m_to_currency = comm;
234 PWARN (
"%d is an invalid property for a Price", static_cast<int>(prop_type));
238 catch (
const std::invalid_argument& e)
240 auto err_str = (bl::format (std::string{_(
"{1}: {2}")}) %
241 std::string{_(gnc_price_col_type_strs[prop_type])} %
243 m_errors.emplace(prop_type, err_str);
244 throw std::invalid_argument (err_str);
246 catch (
const std::out_of_range& e)
248 auto err_str = (bl::format (std::string{_(
"{1}: {2}")}) %
249 std::string{_(gnc_price_col_type_strs[prop_type])} %
251 m_errors.emplace(prop_type, err_str);
252 throw std::invalid_argument (err_str);
256 void GncImportPrice::reset (GncPricePropType prop_type)
260 if ((prop_type == GncPricePropType::FROM_NAMESPACE) ||
261 (prop_type == GncPricePropType::FROM_SYMBOL))
262 set_from_commodity (
nullptr);
264 if (prop_type == GncPricePropType::TO_CURRENCY)
265 set_to_currency (
nullptr);
268 set (prop_type, std::string(),
false);
274 m_errors.erase(prop_type);
278 std::string GncImportPrice::verify_essentials (
void)
282 return _(
"No date column.");
284 return _(
"No amount column.");
285 else if (!m_to_currency)
286 return _(
"No 'Currency to'.");
287 else if (!m_from_commodity)
288 return _(
"No 'Commodity from'.");
290 return _(
"'Commodity From' can not be the same as 'Currency To'.");
292 return std::string();
295 Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb,
bool over)
300 auto check = verify_essentials();
303 PWARN (
"Refusing to create price because essentials not set properly: %s", check.c_str());
309 auto amount = *m_amount;
310 Result ret_val = ADDED;
313 *m_to_currency, date);
316 if ((old_price !=
nullptr) && (over ==
true))
326 memset (date_str, 0,
sizeof(date_str));
328 DEBUG(
"Date is %s, Commodity from is '%s', Currency is '%s', " 329 "Amount is %s", date_str,
332 amount.to_string().c_str());
334 if (old_price ==
nullptr)
338 gnc_price_begin_edit (price);
340 gnc_price_set_commodity (price, *m_from_commodity);
341 gnc_price_set_currency (price, *m_to_currency);
344 auto amount_conv = amount.convert<RoundType::half_up>(scu * COMMODITY_DENOM_MULT);
346 gnc_price_set_value (price, static_cast<gnc_numeric>(amount_conv));
348 gnc_price_set_time64 (price, date);
349 gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE);
350 gnc_price_set_typestr (price, PRICE_TYPE_LAST);
351 gnc_price_commit_edit (price);
358 throw std::invalid_argument (_(
"Failed to create price from selected columns."));
363 ret_val = DUPLICATED;
368 static std::string gen_err_str (std::map<GncPricePropType, std::string>& errors)
370 auto full_error = std::string();
371 for (
auto error : errors)
373 full_error += (full_error.empty() ?
"" :
"\n") + error.second;
378 std::string GncImportPrice::errors ()
380 return gen_err_str (m_errors);
GNCPrice * gnc_pricedb_lookup_day_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commodities on the indicated day.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
gboolean xaccParseAmountImport(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr, gboolean skip)
Similar to xaccParseAmount, but with two differences.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
utility functions for the GnuCash UI
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
#define DEBUG(format, args...)
Print a debugging message.
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
The primary numeric class for representing amounts and values.
#define PWARN(format, args...)
Log a warning.
gboolean xaccParseAmountExtImport(const char *in_str, gboolean monetary, gunichar negative_sign, gunichar decimal_point, gunichar group_separator, const char *ignore_list, gnc_numeric *result, char **endstr)
Similar to xaccParseAmountExtended, but will not automatically set a decimal point, regardless of what the user has set for this option.
int gnc_commodity_table_has_namespace(const gnc_commodity_table *table, const char *name_space)
Test to see if the indicated namespace exits in the commodity table.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
static const std::vector< GncDateFormat > c_formats
A vector with all the date formats supported by the string constructor.