diff --git a/changelog/68_UNRELEASED_xxxx-xx-xx.md b/changelog/68_UNRELEASED_xxxx-xx-xx.md index 0687555c..5fdcb49f 100644 --- a/changelog/68_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/68_UNRELEASED_xxxx-xx-xx.md @@ -11,6 +11,7 @@ - New product option "Move on open" (defaults to disabled) - When enabled, on marking the product as opened, the corresponding amount will be moved to the products default consume location - (Thanks @RosemaryOrchard) +- The stock setting "Decimal places allowed for prices" has been split into separate settings for input and displaying prices (the existing setting will be set for both new options on migration, so no changed behavior when not configured) - Optimized that when the plural form(s) of a quantity unit is/are not provided, the singular form is used to display plural amounts - Fixed "Automatically add products that are below their defined min. stock amount to the shopping list" (stock setting) was only done when consuming products, not when opening them diff --git a/config-dist.php b/config-dist.php index d5988841..3545e938 100644 --- a/config-dist.php +++ b/config-dist.php @@ -173,7 +173,8 @@ DefaultUserSetting('product_presets_qu_id', -1); // Default quantity unit id for DefaultUserSetting('product_presets_default_due_days', 0); // Default due days for new products (-1 means that the product will be never overdue) DefaultUserSetting('product_presets_treat_opened_as_out_of_stock', true); // Default "Treat opened as out of stock" option for new products DefaultUserSetting('stock_decimal_places_amounts', 4); // Default decimal places allowed for amounts -DefaultUserSetting('stock_decimal_places_prices', 2); // Default decimal places allowed for prices +DefaultUserSetting('stock_decimal_places_prices_input', 2); // Default decimal places allowed for prices (input) +DefaultUserSetting('stock_decimal_places_prices_display', 2); // Default decimal places allowed for prices (display) DefaultUserSetting('stock_auto_decimal_separator_prices', false); // If the decimal separator should be set automatically for amount inputs DefaultUserSetting('stock_due_soon_days', 5); // The "expiring soon" days DefaultUserSetting('stock_default_purchase_amount', 0); // The default amount prefilled on the purchase page diff --git a/localization/strings.pot b/localization/strings.pot index a7c88736..9f3fded1 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -1721,9 +1721,6 @@ msgstr "" msgid "Decimal places allowed for amounts" msgstr "" -msgid "Decimal places allowed for prices" -msgstr "" - msgid "Edit shopping list" msgstr "" @@ -2335,3 +2332,9 @@ msgstr "" msgid "Moved to %1$s" msgstr "" + +msgid "Decimal places allowed for prices (input)" +msgstr "" + +msgid "Decimal places allowed for prices (display)" +msgstr "" diff --git a/migrations/0193.sql b/migrations/0193.sql new file mode 100644 index 00000000..b075c5b4 --- /dev/null +++ b/migrations/0193.sql @@ -0,0 +1,9 @@ +UPDATE user_settings +SET key = 'stock_decimal_places_prices_input' +WHERE key = 'stock_decimal_places_prices'; + +INSERT INTO user_settings + (user_id, key, value) +SELECT user_id, 'stock_decimal_places_prices_display', value +FROM user_settings +WHERE key = 'stock_decimal_places_prices_input'; diff --git a/public/js/grocy.js b/public/js/grocy.js index ce21c862..5dc6551c 100644 --- a/public/js/grocy.js +++ b/public/js/grocy.js @@ -627,7 +627,7 @@ function RefreshLocaleNumberDisplay(rootSelector = "#page-content") } var value = parseFloat(text); - $(this).text(value.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices })); + $(this).text(value.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display })); }); $(rootSelector + " .locale-number.locale-number-quantity-amount").each(function() @@ -666,7 +666,7 @@ function RefreshLocaleNumberInput(rootSelector = "#page-content") return; } - $(this).val(parseFloat(value).toLocaleString("en", { minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, useGrouping: false })); + $(this).val(parseFloat(value).toLocaleString("en", { minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_input, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_input, useGrouping: false })); }); $(rootSelector + " .locale-number-input.locale-number-quantity-amount").each(function() diff --git a/public/viewjs/components/numberpicker.js b/public/viewjs/components/numberpicker.js index 596da19b..9162b0d8 100644 --- a/public/viewjs/components/numberpicker.js +++ b/public/viewjs/components/numberpicker.js @@ -98,7 +98,7 @@ $(".numberpicker.locale-number-input.locale-number-currency").on("blur", functio if (BoolVal(Grocy.UserSettings.stock_auto_decimal_separator_prices)) { var value = this.value.toString(); - var decimalPlaces = parseInt(Grocy.UserSettings.stock_decimal_places_prices); + var decimalPlaces = parseInt(Grocy.UserSettings.stock_decimal_places_prices_input); if (value.length <= decimalPlaces) { diff --git a/public/viewjs/components/productcard.js b/public/viewjs/components/productcard.js index 78c56766..f09a4df3 100644 --- a/public/viewjs/components/productcard.js +++ b/public/viewjs/components/productcard.js @@ -84,8 +84,8 @@ Grocy.Components.ProductCard.Refresh = function(productId) if (productDetails.last_price !== null) { - $('#productcard-product-last-price').text(__t("%1$s per %2$s", (Number.parseFloat(productDetails.last_price) * Number.parseFloat(productDetails.product.qu_factor_purchase_to_stock)).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.default_quantity_unit_purchase.name)); - $('#productcard-product-last-price').attr("data-original-title", __t("%1$s per %2$s", Number.parseFloat(productDetails.last_price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.quantity_unit_stock.name)); + $('#productcard-product-last-price').text(__t("%1$s per %2$s", (Number.parseFloat(productDetails.last_price) * Number.parseFloat(productDetails.product.qu_factor_purchase_to_stock)).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), productDetails.default_quantity_unit_purchase.name)); + $('#productcard-product-last-price').attr("data-original-title", __t("%1$s per %2$s", Number.parseFloat(productDetails.last_price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), productDetails.quantity_unit_stock.name)); } else { @@ -95,8 +95,8 @@ Grocy.Components.ProductCard.Refresh = function(productId) if (productDetails.avg_price !== null) { - $('#productcard-product-average-price').text(__t("%1$s per %2$s", (Number.parseFloat(productDetails.avg_price) * Number.parseFloat(productDetails.product.qu_factor_purchase_to_stock)).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.default_quantity_unit_purchase.name)); - $('#productcard-product-average-price').attr("data-original-title", __t("%1$s per %2$s", Number.parseFloat(productDetails.avg_price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.quantity_unit_stock.name)); + $('#productcard-product-average-price').text(__t("%1$s per %2$s", (Number.parseFloat(productDetails.avg_price) * Number.parseFloat(productDetails.product.qu_factor_purchase_to_stock)).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), productDetails.default_quantity_unit_purchase.name)); + $('#productcard-product-average-price').attr("data-original-title", __t("%1$s per %2$s", Number.parseFloat(productDetails.avg_price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), productDetails.quantity_unit_stock.name)); } else { @@ -227,7 +227,7 @@ Grocy.Components.ProductCard.ReInitPriceHistoryChart = function() beginAtZero: true, callback: function(value, index, ticks) { - return Number.parseFloat(value).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }); + return Number.parseFloat(value).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }); } } }] @@ -246,7 +246,7 @@ Grocy.Components.ProductCard.ReInitPriceHistoryChart = function() label += ': '; } - label += tooltipItem.yLabel.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }) + label += tooltipItem.yLabel.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }) return label; } } diff --git a/public/viewjs/inventory.js b/public/viewjs/inventory.js index 0bbcd61c..b4c41f4a 100644 --- a/public/viewjs/inventory.js +++ b/public/viewjs/inventory.js @@ -23,7 +23,7 @@ $('#save-inventory-button').on('click', function(e) var price = ""; if (!jsonForm.price.toString().isEmpty()) { - price = parseFloat(jsonForm.price * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices); + price = parseFloat(jsonForm.price * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); } var jsonData = {}; @@ -332,8 +332,8 @@ function refreshPriceHint() amount -= parseFloat(CurrentProductDetails.product.tare_weight); } - var price = parseFloat($('#price').val() * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices); - $('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), $("#qu_id").attr("data-destination-qu-name"))); + var price = parseFloat($('#price').val() * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); + $('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), $("#qu_id").attr("data-destination-qu-name"))); } else { diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js index 9ef0eb33..910caaec 100644 --- a/public/viewjs/purchase.js +++ b/public/viewjs/purchase.js @@ -43,10 +43,10 @@ $('#save-purchase-button').on('click', function(e) amount -= parseFloat(productDetails.product.tare_weight); } - var price = parseFloat(jsonForm.price * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices); + var price = parseFloat(jsonForm.price * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); if ($("input[name='price-type']:checked").val() == "total-price") { - price = parseFloat(price / amount).toFixed(Grocy.UserSettings.stock_decimal_places_prices); + price = parseFloat(price / amount).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); } jsonData.price = price; @@ -595,13 +595,13 @@ function refreshPriceHint() amount -= parseFloat(CurrentProductDetails.product.tare_weight); } - var price = parseFloat($('#price').val() * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices); + var price = parseFloat($('#price').val() * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); if ($("input[name='price-type']:checked").val() == "total-price") { - price = parseFloat(price / amount).toFixed(Grocy.UserSettings.stock_decimal_places_prices); + price = parseFloat(price / amount).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); } - $('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), $("#qu_id").attr("data-destination-qu-name"))); + $('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), $("#qu_id").attr("data-destination-qu-name"))); } else { diff --git a/public/viewjs/stockentries.js b/public/viewjs/stockentries.js index 266db407..a920c0fb 100644 --- a/public/viewjs/stockentries.js +++ b/public/viewjs/stockentries.js @@ -254,8 +254,8 @@ function RefreshStockEntryRow(stockRowId) result.price = 0; } - $('#stock-' + stockRowId + '-price').text(__t("%1$s per %2$s", (Number.parseFloat(result.price) * Number.parseFloat(productDetails.product.qu_factor_purchase_to_stock)).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.default_quantity_unit_purchase.name)); - $('#stock-' + stockRowId + '-price').attr("data-original-title", __t("%1$s per %2$s", Number.parseFloat(result.price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.quantity_unit_stock.name)); + $('#stock-' + stockRowId + '-price').text(__t("%1$s per %2$s", (Number.parseFloat(result.price) * Number.parseFloat(productDetails.product.qu_factor_purchase_to_stock)).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), productDetails.default_quantity_unit_purchase.name)); + $('#stock-' + stockRowId + '-price').attr("data-original-title", __t("%1$s per %2$s", Number.parseFloat(result.price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices_display }), productDetails.quantity_unit_stock.name)); }, function(xhr) { diff --git a/public/viewjs/stockentryform.js b/public/viewjs/stockentryform.js index ec87221f..273e4131 100644 --- a/public/viewjs/stockentryform.js +++ b/public/viewjs/stockentryform.js @@ -17,7 +17,7 @@ if (!jsonForm.price.toString().isEmpty()) { - price = parseFloat(jsonForm.price).toFixed(Grocy.UserSettings.stock_decimal_places_prices); + price = parseFloat(jsonForm.price).toFixed(Grocy.UserSettings.stock_decimal_places_prices_input); } var jsonData = {}; diff --git a/public/viewjs/stocksettings.js b/public/viewjs/stocksettings.js index 06d0afa3..43feca23 100644 --- a/public/viewjs/stocksettings.js +++ b/public/viewjs/stocksettings.js @@ -10,7 +10,8 @@ $("#stock_due_soon_days").val(Grocy.UserSettings.stock_due_soon_days); $("#stock_default_purchase_amount").val(Grocy.UserSettings.stock_default_purchase_amount); $("#stock_default_consume_amount").val(Grocy.UserSettings.stock_default_consume_amount); $("#stock_decimal_places_amounts").val(Grocy.UserSettings.stock_decimal_places_amounts); -$("#stock_decimal_places_prices").val(Grocy.UserSettings.stock_decimal_places_prices); +$("#stock_decimal_places_prices_input").val(Grocy.UserSettings.stock_decimal_places_prices_input); +$("#stock_decimal_places_prices_display").val(Grocy.UserSettings.stock_decimal_places_prices_display); if (BoolVal(Grocy.UserSettings.show_icon_on_stock_overview_page_when_product_is_on_shopping_list)) { diff --git a/views/inventory.blade.php b/views/inventory.blade.php index a69ab26d..7e0965f3 100644 --- a/views/inventory.blade.php +++ b/views/inventory.blade.php @@ -82,8 +82,8 @@ @include('components.numberpicker', array( 'id' => 'price', 'label' => 'Price', - 'min' => '0.' . str_repeat('0', $userSettings['stock_decimal_places_prices']), - 'decimals' => $userSettings['stock_decimal_places_prices'], + 'min' => '0.' . str_repeat('0', $userSettings['stock_decimal_places_prices_input']), + 'decimals' => $userSettings['stock_decimal_places_prices_input'], 'value' => '', 'contextInfoId' => 'price-hint', 'hint' => $__t('This will apply to added products'), diff --git a/views/purchase.blade.php b/views/purchase.blade.php index d0e8c2ed..347cc395 100644 --- a/views/purchase.blade.php +++ b/views/purchase.blade.php @@ -102,8 +102,8 @@ @include('components.numberpicker', array( 'id' => 'price', 'label' => 'Price', - 'min' => '0.' . str_repeat('0', $userSettings['stock_decimal_places_prices']), - 'decimals' => $userSettings['stock_decimal_places_prices'], + 'min' => '0.' . str_repeat('0', $userSettings['stock_decimal_places_prices_input']), + 'decimals' => $userSettings['stock_decimal_places_prices_input'], 'value' => '', 'contextInfoId' => 'price-hint', 'isRequired' => false, diff --git a/views/stockentryform.blade.php b/views/stockentryform.blade.php index cb41acf0..68c73f7d 100644 --- a/views/stockentryform.blade.php +++ b/views/stockentryform.blade.php @@ -92,8 +92,8 @@ 'id' => 'price', 'value' => $price, 'label' => 'Price', - 'min' => '0.' . str_repeat('0', $userSettings['stock_decimal_places_prices']), - 'decimals' => $userSettings['stock_decimal_places_prices'], + 'min' => '0.' . str_repeat('0', $userSettings['stock_decimal_places_prices_input']), + 'decimals' => $userSettings['stock_decimal_places_prices_input'], 'hint' => $__t('Per stock quantity unit'), 'isRequired' => false, 'additionalCssClasses' => 'locale-number-input locale-number-currency' diff --git a/views/stocksettings.blade.php b/views/stocksettings.blade.php index d442e409..353e76eb 100644 --- a/views/stocksettings.blade.php +++ b/views/stocksettings.blade.php @@ -173,10 +173,19 @@ )) @if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) + @include('components.numberpicker', array( - 'id' => 'stock_decimal_places_prices', - 'additionalAttributes' => 'data-setting-key="stock_decimal_places_prices"', - 'label' => 'Decimal places allowed for prices', + 'id' => 'stock_decimal_places_prices_input', + 'additionalAttributes' => 'data-setting-key="stock_decimal_places_prices_input"', + 'label' => 'Decimal places allowed for prices (input)', + 'min' => 0, + 'additionalCssClasses' => 'user-setting-control' + )) + + @include('components.numberpicker', array( + 'id' => 'stock_decimal_places_prices_display', + 'additionalAttributes' => 'data-setting-key="stock_decimal_places_prices_display"', + 'label' => 'Decimal places allowed for prices (display)', 'min' => 0, 'additionalCssClasses' => 'user-setting-control' )) @@ -197,6 +206,7 @@ + @endif