Existing products cannot be saved when using a comma as decimal separator (unofficial patch in the comments)
I found an issue in FluentCart 1.6.0+ when the store is configured to use a comma as the decimal separator.
Problem:
When I open an existing product, make any change and try to save it, FluentCart returns:
Error: Price must be a number.
This happens even when I don't touch the price field.
Interestingly, if I edit the price field manually first, FluentCart allows the product to be saved again.
So the issue seems specifically related to the localized price value that is loaded into the editor when opening an existing product.
How to reproduce:
- Configure FluentCart to use a comma as the decimal separator.
- Create or open a product with a price such as
19,95. - Open the existing product again.
- Change something unrelated, for example the product title.
- Save the product.
- FluentCart returns: βError: Price must be a number.β
- Now manually edit/re-enter the price.
- Save again, the product can now be saved successfully.
Cause:
From what I can see in FluentCart 1.6.0, the product update validation checks fields such as:
variants.*.item_price
using PHP's numeric validation.
However, when an existing product is loaded, the admin UI can send the localized value back as:
19,95
PHP does not consider 19,95 numeric.
After manually editing the price, the PriceInput apparently converts it back into a format that the backend accepts, which would also explain why saving suddenly works afterwards.
The same issue can potentially affect localized values such as:
1.234,56
So this seems to be a mismatch between localized admin price formatting/state and the server-side validation format.
Expected behaviour:
The admin can continue displaying:
β¬ 1.234,56
but the value submitted to the API should be normalized internally to:
1234.56
before numeric validation.
I currently have a small workaround that normalizes these values before the FluentCart REST validation runs, and saving existing products works correctly again without having to touch the price field. The snippet is in the comments.
Would be great if this could be fixed in FluentCart itself, since stores using comma decimal formatting can otherwise run into this every time an existing product is edited.
/**
* FluentCart 1.6.x
* Fix decimal-comma prices when saving products.
*
* Examples:
* 19,95 -> 19.95
* 1.234,56 -> 1234.56
* 1.234 -> 1234
*/
add_filter('rest_request_before_callbacks', function ($response, $handler, $request) {
if (is_wp_error($response) || !($request instanceof WP_REST_Request)) {
return $response;
}
$method = strtoupper($request->get_method());
if (!in_array($method, ['POST', 'PUT', 'PATCH'], true)) {
return $response;
}
$route = $request->get_route();
if (!preg_match('#^/fluent-cart/v2/products(?:/|$)#', $route)) {
return $response;
}
$store_settings = get_option('fluent_cart_store_settings', []);
$decimal_setting = $store_settings['decimal_separator'] ?? 'dot';
$uses_decimal_comma = in_array(
$decimal_setting,
['comma', ','],
true
);
$price_keys = [
'item_price' => true,
'compare_price' => true,
'item_cost' => true,
'signup_fee' => true,
];
$normalize_price = static function ($value) use ($uses_decimal_comma) {
if (!is_string($value)) {
return $value;
}
$original = $value;
$value = str_replace(
[" ", "\xC2\xA0", "\xE2\x80\xAF"],
'',
trim($value)
);
if ($value === '') {
return $value;
}
if (!preg_match('/^[+-]?\d[\d.,]*$/', $value)) {
return $original;
}
if ($uses_decimal_comma) {
if (strpos($value, ',') !== false) {
$value = str_replace('.', '', $value);
if (substr_count($value, ',') !== 1) {
return $original;
}
$value = str_replace(',', '.', $value);
} elseif (strpos($value, '.') !== false) {
$dot_count = substr_count($value, '.');
if ($dot_count > 1) {
$value = str_replace('.', '', $value);
} else {
$parts = explode('.', $value, 2);
$fraction = $parts[1] ?? '';
if (strlen($fraction) === 3) {
$value = ($parts[0] ?? '') . $fraction;
}
}
}
} else {
$value = str_replace(',', '', $value);
}
if (!preg_match('/^[+-]?\d+(?:.\d+)?$/', $value)) {
return $original;
}
return $value;
};
$normalize_payload = function ($value, $key = null) use (
&$normalize_payload,
$price_keys,
$normalize_price
) {
if (is_array($value)) {
foreach ($value as $child_key => $child_value) {
$value[$child_key] = $normalize_payload(
$child_value,
$child_key
);
}
return $value;
}
if ($key !== null && isset($price_keys[$key])) {
return $normalize_price($value);
}
return $value;
};
foreach ($request->get_params() as $key => $value) {
$request->set_param(
$key,
$normalize_payload($value, $key)
);
}
return $response;
}, 5, 3);