WIP productamountpicker to purchase

This commit is contained in:
Kurt Riddlesperger 2020-05-05 22:45:12 -05:00
parent 939b98e470
commit e1248ca77a
7 changed files with 125 additions and 78 deletions

View File

@ -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')
]);

View File

@ -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('<option value="' + conversion.from_qu_id + '" data-qu-factor="' + factor + '">' + conversion.from_qu_name + '</option>');
factor = 1;
}
$("#qu_id").append('<option value="' + conversion.to_qu_id + '" data-qu-factor="' + factor + '">' + conversion.to_qu_name + '</option>');
$("#qu-id").append('<option value="' + conversion.to_qu_id + '" data-qu-factor="' + factor + '">' + conversion.to_qu_name + '</option>');
});
}
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('<option value="' + qu.id + '" data-qu-factor="1">' + qu.name + '</option>');
$("#qu-id").append('<option value="' + qu.id + '" data-qu-factor="1">' + qu.name + '</option>');
});
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();

View File

@ -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");
}
}
};

View File

@ -19,7 +19,8 @@
<div id="group-{{ $id }}" class="form-group {{ $additionalGroupCssClasses }}">
<label for="{{ $id }}">
{{ $__t($label) }}&nbsp;
<i class="fas fa-question-circle" id="{{ $hintId }}" data-toggle="tooltip" title="{{ $hint }}"></i>{!! $additionalHtmlContextHelp !!}</label>
{!! $additionalHtmlContextHelp !!}</label>
<i class="fas fa-question-circle d-none" id="{{ $hintId }}" data-toggle="tooltip" title="{{ $hint }}"></i>
<div class="input-group">
<input {!! $additionalAttributes !!} type="number" class="form-control numberpicker {{ $additionalCssClasses }}" id="{{ $id }}" @if(!$noNameAttribute) name="{{ $id }}" @endif value="{{ $value }}" min="{{ $min }}" max="{{ $max }}" step="{{ $step }}" @if($isRequired) required @endif>
<div class="input-group-append">

View File

@ -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
<div class="form-group row {{ $additionalGroupCssClasses }}">
<div class="col">
<div class="row">
<div class="form-group">
@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'
))
<div class="form-group col-8 mb-1">
<label for="qu_id">{{ $__t('Quantity unit') }}</label>
<select required class="form-control input-group-productamountpicker" id="qu_id" name="qu_id" data-initial-qu-id="{{ $initialQuId }}">
<div id="group-qu-id" class="form-group d-none">
<label for="qu-id">{{ $__t('Quantity unit') }}</label>
<select required class="form-control input-group-productamountpicker" id="qu-id" name="qu-id" data-initial-qu-id="{{ $initialQuId }}">
<option></option>
</select>
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
</div>
@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'
))
<div id="qu-conversion-info" class="col form-text text-info d-none"></div>
<input type="hidden" id="amount" name="amount" value="">
</div>
</div>
</div>

View File

@ -16,7 +16,7 @@
<label for="product_id">
{{ $__t($label) }}&nbsp;<i class="fas fa-barcode"></i>&nbsp;
<span id="barcode-lookup-disabled-hint" class="small text-muted d-none"> {{ $__t('Barcode lookup is disabled') }}</span>&nbsp;
<i class="fas fa-question-circle" data-toggle="tooltip" title="{{ $hint }}"></i>
<i class="fas fa-question-circle d-none" data-toggle="tooltip" title="{{ $hint }}"></i>
</label>
<select class="form-control product-combobox barcodescanner-input" id="product_id" name="product_id" @if($isRequired) required @endif @if($disabled) disabled @endif data-target="@productpicker">
<option value=""></option>

View File

@ -25,6 +25,10 @@
</div>
</div>
<hr>
<script>
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
</script>
<form id="purchase-form" novalidate>
@ -34,10 +38,9 @@
'nextInputSelector' => '#amount'
))
@include('components.numberpicker', array(
'id' => 'amount',
'label' => 'Amount',
'hintId' => 'amount_qu_unit',
@include('components.productamountpicker', array(
'additionalGroupCssClasses' => 'mb-0',
'hintId' => 'display_amount_qu_unit',
'min' => 1,
'invalidFeedback' => $__t('The amount cannot be lower than %s', '1'),
'additionalHtmlContextHelp' => '<div id="tare-weight-handling-info" class="text-info font-italic d-none">' . $__t('Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated') . '</div>'
@ -97,15 +100,6 @@
<input type="hidden" name="price" id="price" value="0">
@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' => '<p id="qu-conversion-info" class="form-text text-muted small d-none"></p>'
))
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
@include('components.locationpicker', array(