gnucash master: Bug 797033 - The CSV Importer should not obey with the "automatic digital point" setting

Geert Janssens gjanssens at code.gnucash.org
Fri Feb 24 17:09:12 EST 2023


Updated	 via  https://github.com/Gnucash/gnucash/commit/26482f39 (commit)
	from  https://github.com/Gnucash/gnucash/commit/fcbda57a (commit)



commit 26482f397b57915b73805e48f5543f70b5aeddc0
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Feb 24 22:35:40 2023 +0100

    Bug 797033 - The CSV Importer should not obey with the "automatic digital point" setting
    
    Expose variations of xaccParseAmount and
    xaccParseAmountExtended that will ignore the
    automatic decimal point user preference.
    This preference is really only useful for manual number
    entering in the register.
    
    The xaccParseAmountImport variant replaces xaccParseAmountPosSign
    which was used exclusively by the csv importers.
    Like xaccParseAmountPosSign, this replacement has the flag
    to ignore or parse the positive number indicator.

diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp b/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp
index 8e9a70bf2..666b4098e 100644
--- a/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp
+++ b/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp
@@ -78,17 +78,17 @@ GncNumeric parse_amount_price (const std::string &str, int currency_format)
     {
     case 0:
         /* Currency locale */
-        if (!(xaccParseAmountPosSign (str_no_symbols.c_str(), TRUE, &val, &endptr, TRUE)))
+        if (!(xaccParseAmountImport (str_no_symbols.c_str(), TRUE, &val, &endptr, TRUE)))
             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, '-', '.', ',', "$+", &val, &endptr)))
+        if (!(xaccParseAmountExtImport (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 2:
         /* Currency decimal comma */
-        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "$+", &val, &endptr)))
+        if (!(xaccParseAmountExtImport (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;
     }
diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp b/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp
index 9bc6fb132..6298f8707 100644
--- a/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp
+++ b/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp
@@ -149,17 +149,17 @@ GncNumeric parse_monetary (const std::string &str, int currency_format)
     {
     case 0:
         /* Currency locale */
-        if (!(xaccParseAmountPosSign (str_no_symbols.c_str(), TRUE, &val, &endptr, TRUE)))
+        if (!(xaccParseAmountImport (str_no_symbols.c_str(), TRUE, &val, &endptr, TRUE)))
             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, '-', '.', ',', "$+", &val, &endptr)))
+        if (!(xaccParseAmountExtImport (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 2:
         /* Currency decimal comma */
-        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "$+", &val, &endptr)))
+        if (!(xaccParseAmountExtImport (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;
     }
diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c
index 43b6c427a..fdcb45910 100644
--- a/libgnucash/app-utils/gnc-ui-util.c
+++ b/libgnucash/app-utils/gnc-ui-util.c
@@ -1759,56 +1759,11 @@ multiplier (int num_decimals)
     return 1;
 }
 
-gboolean
-xaccParseAmount (const char * in_str, gboolean monetary, gnc_numeric *result,
-                 char **endstr)
-{
-    return xaccParseAmountPosSign (in_str, monetary, result, endstr, FALSE);
-}
-
-gboolean
-xaccParseAmountPosSign (const char * in_str, gboolean monetary, gnc_numeric *result,
-                        char **endstr, gboolean skip)
-{
-    struct lconv *lc = gnc_localeconv();
-
-    gunichar negative_sign;
-    gunichar decimal_point;
-    gunichar group_separator;
-    gchar *ignore = NULL;
-    char *plus_sign = "+";
-
-    negative_sign = g_utf8_get_char(lc->negative_sign);
-    if (monetary)
-    {
-        group_separator = g_utf8_get_char(lc->mon_thousands_sep);
-        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
-    }
-    else
-    {
-        group_separator = g_utf8_get_char(lc->thousands_sep);
-        decimal_point = g_utf8_get_char(lc->decimal_point);
-    }
-
-    if (skip)
-    {
-        /* We want the locale's positive sign to be ignored.
-        * If the locale doesn't specify one, we assume "+" as
-        * an optional positive sign and ignore that */
-        ignore = lc->positive_sign;
-        if (!ignore || !*ignore)
-            ignore = plus_sign;
-    }
-
-    return xaccParseAmountExtended(in_str, monetary, negative_sign,
-                                   decimal_point, group_separator,
-                                   ignore, result, endstr);
-}
-
-gboolean
-xaccParseAmountExtended (const char * in_str, gboolean monetary,
+static gboolean
+xaccParseAmountInternal (const char * in_str, gboolean monetary,
                          gunichar negative_sign, gunichar decimal_point,
                          gunichar group_separator, const char *ignore_list,
+                         gboolean use_auto_decimal,
                          gnc_numeric *result, char **endstr)
 {
     gboolean is_negative;
@@ -1874,137 +1829,137 @@ xaccParseAmountExtended (const char * in_str, gboolean monetary,
         switch (state)
         {
             /* START_ST means we have parsed 0 or more whitespace characters */
-        case START_ST:
-            if (g_unichar_isdigit(uc))
-            {
-                count = g_unichar_to_utf8(uc, out);
-                out += count; /* we record the digits themselves in out_str
-                         * for later conversion by libc routines */
-                next_state = NUMER_ST;
-            }
-            else if (uc == decimal_point)
-            {
-                next_state = FRAC_ST;
-            }
-            else if (g_unichar_isspace(uc))
-            {
-            }
-            else if (uc == negative_sign)
-            {
-                is_negative = TRUE;
-                next_state = NEG_ST;
-            }
-            else if (uc == '(')
-            {
-                is_negative = TRUE;
-                need_paren = TRUE;
-                next_state = NEG_ST;
-            }
-            else
-            {
-                next_state = NO_NUM_ST;
-            }
-
-            break;
-
-            /* NEG_ST means we have just parsed a negative sign. For now,
-             * we only recognize formats where the negative sign comes first. */
-        case NEG_ST:
-            if (g_unichar_isdigit(uc))
-            {
-                count = g_unichar_to_utf8(uc, out);
-                out += count;
-                next_state = NUMER_ST;
-            }
-            else if (uc == decimal_point)
-            {
-                next_state = FRAC_ST;
-            }
-            else if (g_unichar_isspace(uc))
-            {
-            }
-            else
-            {
-                next_state = NO_NUM_ST;
-            }
-
-            break;
+            case START_ST:
+                if (g_unichar_isdigit(uc))
+                {
+                    count = g_unichar_to_utf8(uc, out);
+                    out += count; /* we record the digits themselves in out_str
+                    * for later conversion by libc routines */
+                    next_state = NUMER_ST;
+                }
+                else if (uc == decimal_point)
+                {
+                    next_state = FRAC_ST;
+                }
+                else if (g_unichar_isspace(uc))
+                {
+                }
+                else if (uc == negative_sign)
+                {
+                    is_negative = TRUE;
+                    next_state = NEG_ST;
+                }
+                else if (uc == '(')
+                {
+                    is_negative = TRUE;
+                    need_paren = TRUE;
+                    next_state = NEG_ST;
+                }
+                else
+                {
+                    next_state = NO_NUM_ST;
+                }
 
-            /* NUMER_ST means we have started parsing the number, but
-             * have not encountered a decimal separator. */
-        case NUMER_ST:
-            if (g_unichar_isdigit(uc))
-            {
-                count = g_unichar_to_utf8(uc, out);
-                out += count;
-            }
-            else if (uc == decimal_point)
-            {
-                next_state = FRAC_ST;
-            }
-            else if (uc == group_separator)
-            {
-                 ; //ignore it
-            }
-            else if (uc == ')' && need_paren)
-            {
-                next_state = DONE_ST;
-                need_paren = FALSE;
-            }
-            else
-            {
-                next_state = DONE_ST;
-            }
+                break;
 
-            break;
+                /* NEG_ST means we have just parsed a negative sign. For now,
+                 * we only recognize formats where the negative sign comes first. */
+                case NEG_ST:
+                    if (g_unichar_isdigit(uc))
+                    {
+                        count = g_unichar_to_utf8(uc, out);
+                        out += count;
+                        next_state = NUMER_ST;
+                    }
+                    else if (uc == decimal_point)
+                    {
+                        next_state = FRAC_ST;
+                    }
+                    else if (g_unichar_isspace(uc))
+                    {
+                    }
+                    else
+                    {
+                        next_state = NO_NUM_ST;
+                    }
 
-            /* FRAC_ST means we are now parsing fractional digits. */
-        case FRAC_ST:
-            if (g_unichar_isdigit(uc))
-            {
-                count = g_unichar_to_utf8(uc, out);
-                out += count;
-            }
-            else if (uc == decimal_point)
-            {
-                /* If a subsequent decimal point is also whitespace,
-                 * assume it was intended as such and stop parsing.
-                 * Otherwise, there is a problem. */
-                if (g_unichar_isspace(decimal_point))
-                    next_state = DONE_ST;
-                else
-                    next_state = NO_NUM_ST;
-            }
-            else if (uc == group_separator)
-            {
-                /* If a subsequent group separator is also whitespace,
-                 * assume it was intended as such and stop parsing.
-                 * Otherwise ignore it. */
-                if (g_unichar_isspace(group_separator))
-                    next_state = DONE_ST;
-            }
-            else if (uc == ')' && need_paren)
-            {
-                next_state = DONE_ST;
-                need_paren = FALSE;
-            }
-            else
-            {
-                next_state = DONE_ST;
-            }
+                    break;
+
+                    /* NUMER_ST means we have started parsing the number, but
+                     * have not encountered a decimal separator. */
+                    case NUMER_ST:
+                        if (g_unichar_isdigit(uc))
+                        {
+                            count = g_unichar_to_utf8(uc, out);
+                            out += count;
+                        }
+                        else if (uc == decimal_point)
+                        {
+                            next_state = FRAC_ST;
+                        }
+                        else if (uc == group_separator)
+                        {
+                            ; //ignore it
+                        }
+                        else if (uc == ')' && need_paren)
+                        {
+                            next_state = DONE_ST;
+                            need_paren = FALSE;
+                        }
+                        else
+                        {
+                            next_state = DONE_ST;
+                        }
 
-            break;
+                        break;
 
-        default:
-            PERR("bad state");
-            g_assert_not_reached();
-            break;
+                        /* FRAC_ST means we are now parsing fractional digits. */
+                        case FRAC_ST:
+                            if (g_unichar_isdigit(uc))
+                            {
+                                count = g_unichar_to_utf8(uc, out);
+                                out += count;
+                            }
+                            else if (uc == decimal_point)
+                            {
+                                /* If a subsequent decimal point is also whitespace,
+                                 * assume it was intended as such and stop parsing.
+                                 * Otherwise, there is a problem. */
+                                if (g_unichar_isspace(decimal_point))
+                                    next_state = DONE_ST;
+                                else
+                                    next_state = NO_NUM_ST;
+                            }
+                            else if (uc == group_separator)
+                            {
+                                /* If a subsequent group separator is also whitespace,
+                                 * assume it was intended as such and stop parsing.
+                                 * Otherwise ignore it. */
+                                if (g_unichar_isspace(group_separator))
+                                    next_state = DONE_ST;
+                            }
+                            else if (uc == ')' && need_paren)
+                            {
+                                next_state = DONE_ST;
+                                need_paren = FALSE;
+                            }
+                            else
+                            {
+                                next_state = DONE_ST;
+                            }
+
+                            break;
+
+                        default:
+                            PERR("bad state");
+                            g_assert_not_reached();
+                            break;
         }
 
         /* If we're moving into the FRAC_ST or out of the machine
          * without going through FRAC_ST, record the integral value. */
         if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
-                ((next_state == DONE_ST) && !got_decimal))
+            ((next_state == DONE_ST) && !got_decimal))
         {
             *out = '\0';
 
@@ -2063,7 +2018,7 @@ xaccParseAmountExtended (const char * in_str, gboolean monetary,
         numer *= denom;
         numer += fraction;
     }
-    else if (monetary && auto_decimal_enabled && !got_decimal)
+    else if (monetary && use_auto_decimal && !got_decimal)
     {
         if ((auto_decimal_places > 0) && (auto_decimal_places <= 12))
         {
@@ -2092,6 +2047,92 @@ xaccParseAmountExtended (const char * in_str, gboolean monetary,
     return TRUE;
 }
 
+
+static gboolean
+xaccParseAmountBasicInternal (const char * in_str, gboolean monetary,
+                              gboolean use_auto_decimal, gnc_numeric *result,
+                              char **endstr, gboolean skip)
+{
+    struct lconv *lc = gnc_localeconv();
+
+    gunichar negative_sign;
+    gunichar decimal_point;
+    gunichar group_separator;
+    gchar *ignore = NULL;
+    char *plus_sign = "+";
+
+    negative_sign = g_utf8_get_char(lc->negative_sign);
+    if (monetary)
+    {
+        group_separator = g_utf8_get_char(lc->mon_thousands_sep);
+        decimal_point = g_utf8_get_char(lc->mon_decimal_point);
+    }
+    else
+    {
+        group_separator = g_utf8_get_char(lc->thousands_sep);
+        decimal_point = g_utf8_get_char(lc->decimal_point);
+    }
+
+    if (skip)
+    {
+        /* We want the locale's positive sign to be ignored.
+         * If the locale doesn't specify one, we assume "+" as
+         * an optional positive sign and ignore that */
+        ignore = lc->positive_sign;
+        if (!ignore || !*ignore)
+            ignore = plus_sign;
+    }
+
+    return xaccParseAmountInternal(in_str, monetary, negative_sign,
+                                   decimal_point, group_separator,
+                                   ignore, use_auto_decimal,
+                                   result, endstr);
+}
+
+
+gboolean
+xaccParseAmount (const char * in_str, gboolean monetary, gnc_numeric *result,
+                 char **endstr)
+{
+    return xaccParseAmountBasicInternal (in_str, monetary, auto_decimal_enabled,
+                                         result, endstr, FALSE);
+}
+
+gboolean
+xaccParseAmountImport (const char * in_str, gboolean monetary,
+                        gnc_numeric *result,
+                        char **endstr, gboolean skip)
+{
+    return xaccParseAmountBasicInternal (in_str, monetary, FALSE,
+                                         result, endstr, FALSE);
+}
+
+
+gboolean
+xaccParseAmountExtended (const char * in_str, gboolean monetary,
+                         gunichar negative_sign, gunichar decimal_point,
+                         gunichar group_separator, const char *ignore_list,
+                         gnc_numeric *result, char **endstr)
+{
+    return xaccParseAmountInternal (in_str, monetary, negative_sign,
+                                    decimal_point, group_separator,
+                                    ignore_list, auto_decimal_enabled,
+                                    result, endstr);
+}
+
+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)
+{
+    return xaccParseAmountInternal (in_str, monetary, negative_sign,
+                                    decimal_point, group_separator,
+                                    ignore_list, FALSE,
+                                    result, endstr);
+}
+
+
 /* enable/disable the auto_decimal_enabled option */
 static void
 gnc_set_auto_decimal_enabled (gpointer settings, gchar *key, gpointer user_data)
diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h
index 54cd23306..3ad7e6bdc 100644
--- a/libgnucash/app-utils/gnc-ui-util.h
+++ b/libgnucash/app-utils/gnc-ui-util.h
@@ -331,19 +331,6 @@ gchar *numeric_to_words(gnc_numeric val);
 gboolean xaccParseAmount (const char * in_str, gboolean monetary,
                           gnc_numeric *result, char **endstr);
 
-/**
- * Parses in_str to a gnc_numeric, with a flag to indicate whether the
- * locale's positive sign (or in absence the '+') character is
- * ignored. Setting skip to TRUE will cause the function to ignore any
- * positive sign. Setting it to FALSE, and positive signs will be
- * treated as unrecognized characters.  xaccParseAmount will run as if
- * skip is FALSE for compatibility reasons (gnc-expression-parser
- * depends on this behaviour).
- */
-gboolean
-xaccParseAmountPosSign (const char * in_str, gboolean monetary, gnc_numeric *result,
-                        char **endstr, gboolean skip);
-
 /**
  * Converts a string to a gnc_numeric. The caller must provide all the
  * locale-specific information.
@@ -358,6 +345,37 @@ xaccParseAmountExtended (const char * in_str, gboolean monetary,
                          gunichar group_separator, const char *ignore_list,
                          gnc_numeric *result, char **endstr);
 
+/**
+ * Similar to xaccParseAmount, but with two differences
+ * - it exposes a flag to indicate whether the
+ * locale's positive sign (or in absence the '+') character is
+ * ignored. Setting skip to TRUE will cause the function to ignore any
+ * positive sign. Setting it to FALSE, and positive signs will be
+ * treated as unrecognized characters.  xaccParseAmount will run as if
+ * skip is FALSE for compatibility reasons (gnc-expression-parser
+ * depends on this behaviour).
+ * - The other important difference with xaccParseAmount is that this
+ * function will never apply automatic decimal point logc, whereas
+ * xaccParseAmount will follow the automatic decimal point preference
+ * as set by the user.
+ */
+gboolean
+xaccParseAmountImport (const char * in_str, gboolean monetary,
+                       gnc_numeric *result,
+                       char **endstr, gboolean skip);
+
+/**
+ * Similar to xaccParseAmountExtended, but will not automatically
+ * set a decimal point, regardless of what the user has set for this
+ * option. Primarily meant for cases where numbers are coming into
+ * gnucash that are not typed in by the user (like via csv import).
+ */
+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);
+
 /**
  * Make a string representation of a gnc_numeric.  Warning, the
  * gnc_numeric is not checked for validity and the returned char* may



Summary of changes:
 .../import-export/csv-imp/gnc-imp-props-price.cpp  |   6 +-
 gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp |   6 +-
 libgnucash/app-utils/gnc-ui-util.c                 | 381 ++++++++++++---------
 libgnucash/app-utils/gnc-ui-util.h                 |  44 ++-
 4 files changed, 248 insertions(+), 189 deletions(-)



More information about the gnucash-changes mailing list