diff --git a/controllers/StockController.php b/controllers/StockController.php index ab88ffaa..0c0c73f4 100644 --- a/controllers/StockController.php +++ b/controllers/StockController.php @@ -52,6 +52,8 @@ class StockController extends BaseController return $this->renderPage($response, 'purchase', [ 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'barcodes' => $productBarcodes, + 'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name'), + 'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved(), 'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name'), 'locations' => $this->getDatabase()->locations()->orderBy('name') ]); diff --git a/public/viewjs/components/productamountpicker.js b/public/viewjs/components/productamountpicker.js index c451c2fd..b43ad10e 100644 --- a/public/viewjs/components/productamountpicker.js +++ b/public/viewjs/components/productamountpicker.js @@ -7,28 +7,29 @@ Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuI if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled) { - $("#qu_id").find("option").remove().end(); - $("#qu_id").attr("data-destination-qu-name", FindObjectInArrayByPropertyValue(Grocy.QuantityUnits, 'id', destinationQuId).name); + $("#qu-id").find("option").remove().end(); + $("#qu-id").attr("data-destination-qu-name", FindObjectInArrayByPropertyValue(Grocy.QuantityUnits, 'id', destinationQuId).name); conversionsForProduct.forEach(conversion => { var factor = conversion.factor; if (conversion.to_qu_id == destinationQuId) { + $("#qu-id").append(''); factor = 1; } - $("#qu_id").append(''); + $("#qu-id").append(''); }); } if (!Grocy.Components.ProductAmountPicker.InitialValueSet || forceInitialDisplayQu) { - $("#qu_id").val($("#qu_id").attr("data-initial-qu-id")); + $("#qu-id").val($("#qu-id").attr("data-initial-qu-id")); } if (!Grocy.Components.ProductAmountPicker.InitialValueSet) { - var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor"); + var convertedAmount = $("#display_amount").val() * $("#qu-id option:selected").attr("data-qu-factor"); $("#display_amount").val(convertedAmount); Grocy.Components.ProductAmountPicker.InitialValueSet = true; @@ -36,7 +37,7 @@ Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuI if (conversionsForProduct.length === 1 && !forceInitialDisplayQu) { - $("#qu_id").val($("#qu_id option:first").val()); + $("#qu-id").val($("#qu-id option:first").val()); } $(".input-group-productamountpicker").trigger("change"); @@ -44,28 +45,33 @@ Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuI Grocy.Components.ProductAmountPicker.SetQuantityUnit = function(quId) { - $("#qu_id").val(quId); + $("#qu-id").val(quId); +} + +Grocy.Components.ProductAmountPicker.SetQuantityUnitFactorPurchaseToStock = function(quFactorPurchaseToStock) +{ + $("#qu-factor-purchase-to-stock").val(quFactorPurchaseToStock); } Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false) { Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true; - $("#qu_id").find("option").remove().end(); + $("#qu-id").find("option").remove().end(); Grocy.QuantityUnits.forEach(qu => { - $("#qu_id").append(''); + $("#qu-id").append(''); }); if (keepInitialQu) { - Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu_id").attr("data-initial-qu-id")); + Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu-id").attr("data-initial-qu-id")); } $(".input-group-productamountpicker").trigger("change"); } -$(".input-group-productamountpicker").on("change", function() +$("#qu-id").on("change", function() { var destinationQuName = $("#qu_id").attr("data-destination-qu-name"); var selectedQuName = $("#qu_id option:selected").text(); diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js index 7fe1d843..9540f4d6 100644 --- a/public/viewjs/purchase.js +++ b/public/viewjs/purchase.js @@ -87,12 +87,12 @@ Grocy.FrontendHelpers.EndUiBusy("purchase-form"); toastr.success(successMessage); - $("#amount").attr("min", "1"); - $("#amount").attr("step", "1"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1')); - $('#amount').val(parseFloat(Grocy.UserSettings.stock_default_purchase_amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 4 })); + $("#display_amount").attr("min", "1"); + $("#display_amount").attr("step", "1"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1')); + $('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_purchase_amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 4 })); $('#price').val(''); - $('#amount_qu_unit').text(""); + $('#display_amount_qu_unit').text(""); $("#tare-weight-handling-info").addClass("d-none"); if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) { @@ -212,22 +212,23 @@ if (Grocy.Components.ProductPicker !== undefined) Grocy.Components.LocationPicker.SetId(productDetails.location.id); } - $('#amount_qu_unit').attr("qu-factor-purchase-to-stock", qu_factor_purchase_to_stock); - $('#amount_qu_unit').attr("quantity-unit-purchase-name", productDetails.quantity_unit_purchase.name); - $('#amount_qu_unit').attr("quantity-unit-stock-name", productDetails.quantity_unit_stock.name); - $('#amount_qu_unit').attr("quantity-unit-stock-name-plural", productDetails.quantity_unit_stock.name_plural); - $('#qu_factor_purchase_to_stock').val(qu_factor_purchase_to_stock); + $('#display_amount_qu_unit').attr("quantity-unit-stock-name", productDetails.quantity_unit_stock.name); + $('#display_amount_qu_unit').attr("quantity-unit-stock-name-plural", productDetails.quantity_unit_stock.name_plural); + Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id, false); + Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_purchase.id); + Grocy.Components.ProductAmountPicker.SetQuantityUnitFactorPurchaseToStock(qu_factor_purchase_to_stock); if (qu_factor_purchase_to_stock == 1) { - $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name); - $('#group-qu_factor_purchase_to_stock').addClass('d-none'); + $('#display_amount_qu_unit').text(productDetails.quantity_unit_purchase.name); + $('#group-qu-factor-purchase-to-stock').addClass('d-none'); + $('#group-qu-id').addClass('d-none'); } else { - $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name + " (" + __t("will be multiplied by a factor of %1$s to get %2$s", parseFloat(qu_factor_purchase_to_stock).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 2 }), __n(2, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)) + ")"); - $('#group-qu_factor_purchase_to_stock').removeClass('d-none'); + $('#group-qu-factor-purchase-to-stock').removeClass('d-none'); + $('#group-qu-id').removeClass('d-none'); } var priceTypeUnitPrice = $("#price-type-unit-price"); @@ -238,23 +239,35 @@ if (Grocy.Components.ProductPicker !== undefined) if (productDetails.product.allow_partial_units_in_stock == 1) { - $("#amount").attr("min", "0.01"); - $("#amount").attr("step", "0.01"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', 0.01.toLocaleString())); + $("#display_amount").attr("min", "0.01"); + $("#display_amount").attr("step", "0.01"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', 0.01.toLocaleString())); } else { - $("#amount").attr("min", "1"); - $("#amount").attr("step", "1"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1')); + $("#display_amount").attr("min", "1"); + $("#display_amount").attr("step", "1"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1')); } if (productDetails.product.enable_tare_weight_handling == 1) { var minAmount = parseFloat(productDetails.product.tare_weight) / qu_factor_purchase_to_stock + parseFloat(productDetails.stock_amount); - $("#amount").attr("min", minAmount); - $("#amount").attr("step", "0.0001"); - $("#amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', minAmount.toLocaleString())); + $("#display_amount").attr("min", minAmount); + $("#display_amount").attr("step", "0.0001"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', minAmount.toLocaleString())); + $("#tare-weight-handling-info").removeClass("d-none"); + } + else + { + $("#tare-weight-handling-info").addClass("d-none"); + } + if (productDetails.product.enable_tare_weight_handling == 1) + { + var minAmount = parseFloat(productDetails.product.tare_weight) / qu_factor_purchase_to_stock + parseFloat(productDetails.stock_amount); + $("#display_amount").attr("min", minAmount); + $("#display_amount").attr("step", "0.0001"); + $("#display_amount").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', minAmount.toLocaleString())); $("#tare-weight-handling-info").removeClass("d-none"); } else @@ -282,7 +295,7 @@ if (Grocy.Components.ProductPicker !== undefined) } } - $("#amount").focus(); + $("#display_amount").focus(); Grocy.FrontendHelpers.ValidateForm('purchase-form'); if (GetUriParam("flow") === "shoppinglistitemtostock" && BoolVal(Grocy.UserSettings.shopping_list_to_stock_workflow_auto_submit_when_prefilled) && document.getElementById("purchase-form").checkValidity() === true) @@ -292,7 +305,7 @@ if (Grocy.Components.ProductPicker !== undefined) if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled)) { - $("#amount").val(1); + $("#display_amount").val(1); Grocy.FrontendHelpers.ValidateForm("purchase-form"); if (document.getElementById("purchase-form").checkValidity() === true) { @@ -314,7 +327,7 @@ if (Grocy.Components.ProductPicker !== undefined) }); } -$('#amount').val(parseFloat(Grocy.UserSettings.stock_default_purchase_amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 4 })); +$('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_purchase_amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 4 })); Grocy.FrontendHelpers.ValidateForm('purchase-form'); if (Grocy.Components.ProductPicker) @@ -329,7 +342,7 @@ if (Grocy.Components.ProductPicker) } } -$('#amount').on('focus', function(e) +$('#display_amount').on('focus', function(e) { if (Grocy.Components.ProductPicker.GetValue().length === 0) { @@ -391,56 +404,67 @@ $('#price-type-total-price').on('change', function(e) refreshPriceHint(); }); -$('#amount').on('change', function(e) +$('#display_amount').on('change', function(e) { refreshPriceHint(); Grocy.FrontendHelpers.ValidateForm('purchase-form'); }); -$('#qu_factor_purchase_to_stock').on('change', function(e) +$('#qu-id').on('change', function(e) +{ + var priceTypeUnitPrice = $("#price-type-unit-price"); + var priceTypeUnitPriceLabel = $("[for=" + priceTypeUnitPrice.attr("id") + "]"); + priceTypeUnitPriceLabel.text( $("#qu-id option:selected").text() + " price"); + refreshPriceHint(); + Grocy.FrontendHelpers.ValidateForm('purchase-form'); +}); + +$('#qu-factor-purchase-to-stock').on('change', function(e) { - var value = $(e.target).val(); - $('#amount_qu_unit').attr("qu-factor-purchase-to-stock", value); - $('#amount_qu_unit').text(document.getElementById("amount_qu_unit").getAttribute("quantity-unit-purchase-name") + " (" + __t("will be multiplied by a factor of %1$s to get %2$s", parseFloat(value).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 2 }), __n(2, document.getElementById("amount_qu_unit").getAttribute("quantity-unit-stock-name"), document.getElementById("amount_qu_unit").getAttribute("quantity-unit-stock-name-plural")) + ")")); refreshPriceHint(); Grocy.FrontendHelpers.ValidateForm('purchase-form'); }); if (GetUriParam("flow") === "shoppinglistitemtostock") { - $('#amount').val(parseFloat(GetUriParam("amount")).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 4 })); + $('#display_amount').val(parseFloat(GetUriParam("amount")).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 4 })); } function refreshPriceHint() { - if ($('#amount').val() == 0) + if ($('#display_amount').val() == 0) { $('#price-hint').text(""); + $("#price-hint").addClass("d-none"); return; } if ($('#price').val() == 0) { $('#price-hint').text(""); + $("#price-hint").addClass("d-none"); return; } if ($("input[name='price-type']:checked").val() == "total-price") { - var price = parseFloat($('#price').val()) / parseFloat($('#amount').val()); + var price = parseFloat($('#price').val()) / parseFloat($('#display_amount').val()); - $('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString({ minimumFractionDigits: 2, maximumFractionDigits: 2 }), document.getElementById("amount_qu_unit").getAttribute("quantity-unit-stock-name"))); + $('#price-hint').attr("title",(__t('means %1$s per %2$s', price.toLocaleString({ minimumFractionDigits: 2, maximumFractionDigits: 2 }), document.getElementById("display_amount_qu_unit").getAttribute("quantity-unit-stock-name")))); + $("#price-hint").removeClass("d-none"); } else { - if (document.getElementById("amount_qu_unit").getAttribute("qu-factor-purchase-to-stock") > 1) + if ($('#qu-factor-purchase-to-stock').val() > 1) { - var price = $('#price').val() / document.getElementById("amount_qu_unit").getAttribute("qu-factor-purchase-to-stock"); - $('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString({ minimumFractionDigits: 2, maximumFractionDigits: 2 }), document.getElementById("amount_qu_unit").getAttribute("quantity-unit-stock-name"))); + var price = $('#price').val() / $('#qu-factor-purchase-to-stock').val(); + $('#price-hint').attr("title", (__t('means %1$s per %2$s', price.toLocaleString({ minimumFractionDigits: 2, maximumFractionDigits: 2 }), document.getElementById("display_amount_qu_unit").getAttribute("quantity-unit-stock-name")))); + $("#price-hint").removeClass("d-none"); } else { $('#price-hint').text(""); + $("#price-hint").addClass("d-none"); } } }; diff --git a/views/components/numberpicker.blade.php b/views/components/numberpicker.blade.php index 3ffa3cb6..2792bece 100644 --- a/views/components/numberpicker.blade.php +++ b/views/components/numberpicker.blade.php @@ -19,7 +19,8 @@
+ {!! $additionalHtmlContextHelp !!} +
diff --git a/views/components/productamountpicker.blade.php b/views/components/productamountpicker.blade.php index 36400119..bb83fe88 100644 --- a/views/components/productamountpicker.blade.php +++ b/views/components/productamountpicker.blade.php @@ -3,32 +3,52 @@ @endpush @php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp +@php if(!isset($value)) { $value = 1; } @endphp +@php if(empty($id)) { $id = 'display_amount'; } @endphp +@php if(empty($label)) { $label = 'Amount'; } @endphp +@php if(empty($min)) { $min = 0; } @endphp +@php if(empty($max)) { $max = 999999; } @endphp +@php if(empty($step)) { $step = 1; } @endphp +@php if(empty($hint)) { $hint = ''; } @endphp +@php if(empty($hintId)) { $hintId = ''; } @endphp +@php if(empty($additionalCssClasses)) { $additionalCssClasses = ''; } @endphp +@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp +@php if(empty($additionalAttributes)) { $additionalAttributes = ''; } @endphp +@php if(empty($additionalHtmlElements)) { $additionalHtmlElements = ''; } @endphp +@php if(empty($additionalHtmlContextHelp)) { $additionalHtmlContextHelp = ''; } @endphp +@php if(!isset($isRequired)) { $isRequired = true; } @endphp +@php if(!isset($noNameAttribute)) { $noNameAttribute = false; } @endphp -
-
-
- +
@include('components.numberpicker', array( - 'id' => 'display_amount', - 'label' => 'Amount', - 'min' => 0, + 'id' => $id, + 'label' => $label, + 'min' => $min, 'value' => $value, + 'hintId' => $hintId, 'invalidFeedback' => $__t('This cannot be negative and must be an integral number'), - 'additionalGroupCssClasses' => 'col-4 mb-1', + 'additionalHtmlContextHelp' => $additionalHtmlContextHelp, 'additionalCssClasses' => 'input-group-productamountpicker' )) -
- -
{{ $__t('A quantity unit is required') }}
+ @include('components.numberpicker', array( + 'id' => 'qu-factor-purchase-to-stock', + 'label' => 'Factor purchase to stock quantity unit', + 'min' => 1, + 'additionalGroupCssClasses' => 'd-none', + 'invalidFeedback' => $__t('The amount cannot be lower than %s', '1'), + 'additionalCssClasses' => 'input-group-qu' + )) +
-
-
diff --git a/views/components/productpicker.blade.php b/views/components/productpicker.blade.php index 2d2cf033..b76eb500 100644 --- a/views/components/productpicker.blade.php +++ b/views/components/productpicker.blade.php @@ -16,7 +16,7 @@ @endif - @include('components.numberpicker', array( - 'id' => 'qu_factor_purchase_to_stock', - 'label' => 'Factor purchase to stock quantity unit', - 'min' => 1, - 'additionalGroupCssClasses' => 'd-none', - 'invalidFeedback' => $__t('The amount cannot be lower than %s', '1'), - 'additionalCssClasses' => 'input-group-qu', - 'additionalHtmlElements' => '

' - )) @if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) @include('components.locationpicker', array(