diff --git a/assets/js/admin/woocommerce_admin.js b/assets/js/admin/woocommerce_admin.js index 77512a122a2..51351a52839 100644 --- a/assets/js/admin/woocommerce_admin.js +++ b/assets/js/admin/woocommerce_admin.js @@ -61,16 +61,18 @@ }) .on( 'change', '.wc_input_price[type=text], .wc_input_decimal[type=text], .wc-order-totals #refund_amount[type=text]', function() { - var regex; + var regex, decimalRegex, + decimailPoint = woocommerce_admin.decimal_point; if ( $( this ).is( '.wc_input_price' ) || $( this ).is( '#refund_amount' ) ) { - regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.mon_decimal_point + ']+', 'gi' ); - } else { - regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.decimal_point + ']+', 'gi' ); + decimailPoint = woocommerce_admin.mon_decimal_point; } + regex = new RegExp( '[^\-0-9\%\\' + decimailPoint + ']+', 'gi' ); + decimalRegex = new RegExp( '\\' + decimailPoint + '+', 'gi' ); + var value = $( this ).val(); - var newvalue = value.replace( regex, '' ); + var newvalue = value.replace( regex, '' ).replace( decimalRegex, decimailPoint ); if ( value !== newvalue ) { $( this ).val( newvalue ); @@ -78,22 +80,32 @@ }) .on( 'keyup', '.wc_input_price[type=text], .wc_input_decimal[type=text], .wc_input_country_iso[type=text], .wc-order-totals #refund_amount[type=text]', function() { - var regex, error; + var regex, error, decimalRegex; + var checkDecimalNumbers = false; if ( $( this ).is( '.wc_input_price' ) || $( this ).is( '#refund_amount' ) ) { + checkDecimalNumbers = true; regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.mon_decimal_point + ']+', 'gi' ); + decimalRegex = new RegExp( '[^\\' + woocommerce_admin.mon_decimal_point + ']', 'gi' ); error = 'i18n_mon_decimal_error'; } else if ( $( this ).is( '.wc_input_country_iso' ) ) { regex = new RegExp( '([^A-Z])+|(.){3,}', 'im' ); error = 'i18n_country_iso_error'; } else { + checkDecimalNumbers = true; regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.decimal_point + ']+', 'gi' ); + decimalRegex = new RegExp( '[^\\' + woocommerce_admin.decimal_point + ']', 'gi' ); error = 'i18n_decimal_error'; } var value = $( this ).val(); var newvalue = value.replace( regex, '' ); + // Check if newvalue have more than one decimal point. + if ( checkDecimalNumbers && 1 < newvalue.replace( decimalRegex, '' ).length ) { + newvalue = newvalue.replace( decimalRegex, '' ); + } + if ( value !== newvalue ) { $( document.body ).triggerHandler( 'wc_add_error_tip', [ $( this ), error ] ); } else { diff --git a/includes/admin/class-wc-admin-assets.php b/includes/admin/class-wc-admin-assets.php index 8a5c7b7f955..34defe99fc6 100644 --- a/includes/admin/class-wc-admin-assets.php +++ b/includes/admin/class-wc-admin-assets.php @@ -169,9 +169,9 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : $params = array( /* translators: %s: decimal */ - 'i18n_decimal_error' => sprintf( __( 'Please enter in decimal (%s) format without thousand separators.', 'woocommerce' ), $decimal ), + 'i18n_decimal_error' => sprintf( __( 'Please enter with one decimal point (%s) without thousand separators.', 'woocommerce' ), $decimal ), /* translators: %s: price decimal separator */ - 'i18n_mon_decimal_error' => sprintf( __( 'Please enter in monetary decimal (%s) format without thousand separators and currency symbols.', 'woocommerce' ), wc_get_price_decimal_separator() ), + 'i18n_mon_decimal_error' => sprintf( __( 'Please enter with one monetary decimal point (%s) without thousand separators and currency symbols.', 'woocommerce' ), wc_get_price_decimal_separator() ), 'i18n_country_iso_error' => __( 'Please enter in country code with two capital letters.', 'woocommerce' ), 'i18n_sale_less_than_regular_error' => __( 'Please enter in a value less than the regular price.', 'woocommerce' ), 'i18n_delete_product_notice' => __( 'This product has produced sales and may be linked to existing orders. Are you sure you want to delete it?', 'woocommerce' ), diff --git a/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/includes/data-stores/class-wc-product-variable-data-store-cpt.php index ee49191d7b3..c579aed5bda 100644 --- a/includes/data-stores/class-wc-product-variable-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variable-data-store-cpt.php @@ -361,7 +361,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple $prices_array['price'][ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); $prices_array['regular_price'][ $variation_id ] = wc_format_decimal( $regular_price, wc_get_price_decimals() ); - $prices_array['sale_price'][ $variation_id ] = wc_format_decimal( $sale_price . '.00', wc_get_price_decimals() ); + $prices_array['sale_price'][ $variation_id ] = wc_format_decimal( $sale_price, wc_get_price_decimals() ); $prices_array = apply_filters( 'woocommerce_variation_prices_array', $prices_array, $variation, $for_display ); } diff --git a/includes/wc-formatting-functions.php b/includes/wc-formatting-functions.php index 5fccc627c5c..967bb207f34 100644 --- a/includes/wc-formatting-functions.php +++ b/includes/wc-formatting-functions.php @@ -292,7 +292,9 @@ function wc_format_decimal( $number, $dp = false, $trim_zeros = false ) { // Remove locale from string. if ( ! is_float( $number ) ) { $number = str_replace( $decimals, '.', $number ); - $number = preg_replace( '/[^0-9\.,-]/', '', wc_clean( $number ) ); + + // Convert multiple dots to just one. + $number = preg_replace( '/\.(?![^.]+$)|[^0-9.-]/', '', wc_clean( $number ) ); } if ( false !== $dp ) { @@ -735,7 +737,8 @@ function wc_timezone_string() { // Last try, guess timezone string manually. foreach ( timezone_abbreviations_list() as $abbr ) { foreach ( $abbr as $city ) { - if ( (bool) date( 'I' ) === (bool) $city['dst'] && $city['timezone_id'] && intval( $city['offset'] ) === $utc_offset ) { + // WordPress restrict the use of date(), since it's affected by timezone settings, but in this case is just what we need to guess the correct timezone. + if ( (bool) date( 'I' ) === (bool) $city['dst'] && $city['timezone_id'] && intval( $city['offset'] ) === $utc_offset ) { // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date return $city['timezone_id']; } } diff --git a/tests/unit-tests/formatting/functions.php b/tests/unit-tests/formatting/functions.php index d6c67b46a37..a2e517dfea4 100644 --- a/tests/unit-tests/formatting/functions.php +++ b/tests/unit-tests/formatting/functions.php @@ -298,6 +298,15 @@ class WC_Tests_Formatting_Functions extends WC_Unit_Test_Case { // Given string. $this->assertEquals( '9.99', wc_format_decimal( '9.99' ) ); + // Given string with multiple decimals points. + $this->assertEquals( '9.99', wc_format_decimal( '9...99' ) ); + + // Given string with multiple decimals points. + $this->assertEquals( '99.9', wc_format_decimal( '9...9....9' ) ); + + // Negative string. + $this->assertEquals( '-9.99', wc_format_decimal( '-9.99' ) ); + // Float. $this->assertEquals( '9.99', wc_format_decimal( 9.99 ) ); @@ -324,7 +333,16 @@ class WC_Tests_Formatting_Functions extends WC_Unit_Test_Case { update_option( 'woocommerce_price_thousand_sep', '.' ); // Given string. - $this->assertEquals( '9.99', wc_format_decimal( '9.99' ) ); + $this->assertEquals( '9.99', wc_format_decimal( '9,99' ) ); + + // Given string with multiple decimals points. + $this->assertEquals( '9.99', wc_format_decimal( '9,,,99' ) ); + + // Given string with multiple decimals points. + $this->assertEquals( '99.9', wc_format_decimal( '9,,,9,,,,9' ) ); + + // Negative string. + $this->assertEquals( '-9.99', wc_format_decimal( '-9,99' ) ); // Float. $this->assertEquals( '9.99', wc_format_decimal( 9.99 ) );