mirror of
https://github.com/grocy/grocy.git
synced 2025-04-28 17:23:56 +00:00
Implemented "Scan mode"
This commit is contained in:
parent
7a048136c6
commit
c7bcb9984a
@ -4,6 +4,14 @@
|
||||
- From there you can also edit the stock entries
|
||||
- A huge THANK YOU goes to @kriddles for the work on this feature
|
||||
|
||||
### New feature: Scan mode
|
||||
- New switch-button on the purchase and consume page
|
||||
- When enabled
|
||||
- The amount will always be filled with `1` after changing/scanning a product
|
||||
- If all fields could be automatically populated (means for purchase the product has a default best before date set), the transaction is automatically submitted
|
||||
- If not, a warning is displayed and you can fill in the missing information
|
||||
- Audio feedback is provided after scanning and on success/error of the transaction
|
||||
|
||||
### New feature: Self produced products
|
||||
- To a recipe a product can be attached
|
||||
- This products needs a "Default best before date"
|
||||
|
@ -91,6 +91,8 @@ DefaultUserSetting('product_presets_qu_id', -1); // Default quantity unit id for
|
||||
DefaultUserSetting('stock_expring_soon_days', 5);
|
||||
DefaultUserSetting('stock_default_purchase_amount', 0);
|
||||
DefaultUserSetting('stock_default_consume_amount', 1);
|
||||
DefaultUserSetting('scan_mode_consume_enabled', false);
|
||||
DefaultUserSetting('scan_mode_purchase_enabled', false);
|
||||
|
||||
# Chores settings
|
||||
DefaultUserSetting('chores_due_soon_days', 5);
|
||||
|
@ -1669,3 +1669,15 @@ msgstr ""
|
||||
|
||||
msgid "Meal plan product"
|
||||
msgstr ""
|
||||
|
||||
msgid "Scan mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "on"
|
||||
msgstr ""
|
||||
|
||||
msgid "off"
|
||||
msgstr ""
|
||||
|
||||
msgid "Scan mode is on but not all required fields could be populated automatically"
|
||||
msgstr ""
|
||||
|
@ -8,6 +8,7 @@
|
||||
"bootbox": "^5.3.2",
|
||||
"bootstrap": "^4.3.1",
|
||||
"bootstrap-select": "^1.13.10",
|
||||
"bootstrap-switch-button": "https://github.com/walidbagh/bootstrap-switch-button#Fix-module-export",
|
||||
"chart.js": "^2.8.0",
|
||||
"datatables.net": "^1.10.19",
|
||||
"datatables.net-bs4": "^1.10.19",
|
||||
|
@ -425,7 +425,7 @@ $(document).on("click", "select", function()
|
||||
});
|
||||
|
||||
// Auto saving user setting controls
|
||||
$(".user-setting-control").on("change", function()
|
||||
$(document).on("change", ".user-setting-control", function()
|
||||
{
|
||||
var element = $(this);
|
||||
var settingKey = element.attr("data-setting-key");
|
||||
|
26
public/js/grocy_uisound.js
Normal file
26
public/js/grocy_uisound.js
Normal file
@ -0,0 +1,26 @@
|
||||
Grocy.UISound = { };
|
||||
|
||||
Grocy.UISound.Play = function(url)
|
||||
{
|
||||
new Audio(url).play();
|
||||
}
|
||||
|
||||
Grocy.UISound.AskForPermission = function()
|
||||
{
|
||||
Grocy.UISound.Play(U("/uisounds/silence.mp3"));
|
||||
}
|
||||
|
||||
Grocy.UISound.Success = function()
|
||||
{
|
||||
Grocy.UISound.Play(U("/uisounds/success.mp3"));
|
||||
}
|
||||
|
||||
Grocy.UISound.Error = function()
|
||||
{
|
||||
Grocy.UISound.Play(U("/uisounds/error.mp3"));
|
||||
}
|
||||
|
||||
Grocy.UISound.BarcodeScannerBeep = function()
|
||||
{
|
||||
Grocy.UISound.Play(U("/uisounds/barcodescannerbeep.mp3"));
|
||||
}
|
BIN
public/uisounds/barcodescannerbeep.mp3
Normal file
BIN
public/uisounds/barcodescannerbeep.mp3
Normal file
Binary file not shown.
BIN
public/uisounds/error.mp3
Normal file
BIN
public/uisounds/error.mp3
Normal file
Binary file not shown.
BIN
public/uisounds/silence.mp3
Normal file
BIN
public/uisounds/silence.mp3
Normal file
Binary file not shown.
BIN
public/uisounds/success.mp3
Normal file
BIN
public/uisounds/success.mp3
Normal file
Binary file not shown.
@ -38,6 +38,11 @@
|
||||
Grocy.Api.Post(apiUrl, jsonData,
|
||||
function(result)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled))
|
||||
{
|
||||
Grocy.UISound.Success();
|
||||
}
|
||||
|
||||
bookingResponse = result;
|
||||
|
||||
var addBarcode = GetUriParam('addbarcodetoselection');
|
||||
@ -246,6 +251,11 @@ $("#location_id").on('change', function(e)
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled))
|
||||
{
|
||||
Grocy.UISound.BarcodeScannerBeep();
|
||||
}
|
||||
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
@ -265,13 +275,14 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
$('#amount_qu_unit').text(productDetails.quantity_unit_stock.name);
|
||||
|
||||
$("#location_id").find("option").remove().end().append("<option></option>");
|
||||
Grocy.Api.Get("stock/products/" + productId + '/locations',
|
||||
Grocy.Api.Get("stock/products/" + productId + '/locations',
|
||||
function(stockLocations)
|
||||
{
|
||||
var setDefault = 0;
|
||||
stockLocations.forEach(stockLocation =>
|
||||
{
|
||||
if (productDetails.location.id == stockLocation.location_id) {
|
||||
if (productDetails.location.id == stockLocation.location_id)
|
||||
{
|
||||
$("#location_id").append($("<option>", {
|
||||
value: stockLocation.location_id,
|
||||
text: stockLocation.location_name + " (" + __t("Default location") + ")"
|
||||
@ -294,6 +305,21 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
$("#location_id").trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled))
|
||||
{
|
||||
$("#amount").val(1);
|
||||
Grocy.FrontendHelpers.ValidateForm("consume-form");
|
||||
if (document.getElementById("consume-form").checkValidity() === true)
|
||||
{
|
||||
$('#save-consume-button').click();
|
||||
}
|
||||
else
|
||||
{
|
||||
toastr.warning(__t("Scan mode is on but not all required fields could be populated automatically"));
|
||||
Grocy.UISound.Error();
|
||||
}
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
@ -495,3 +521,18 @@ if (GetUriParam("embedded") !== undefined)
|
||||
$("#use_specific_stock_entry").trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
// Default input field
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
|
||||
// Can only be set via JS however...
|
||||
$("#scan-mode").addClass("user-setting-control");
|
||||
$("#scan-mode").attr("data-setting-key", "scan_mode_consume_enabled");
|
||||
|
||||
$(document).on("change", "#scan-mode", function(e)
|
||||
{
|
||||
if ($(this).prop("checked"))
|
||||
{
|
||||
Grocy.UISound.AskForPermission();
|
||||
}
|
||||
});
|
||||
|
@ -42,6 +42,11 @@
|
||||
Grocy.Api.Post('stock/products/' + jsonForm.product_id + '/add', jsonData,
|
||||
function(result)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled))
|
||||
{
|
||||
Grocy.UISound.Success();
|
||||
}
|
||||
|
||||
var addBarcode = GetUriParam('addbarcodetoselection');
|
||||
if (addBarcode !== undefined)
|
||||
{
|
||||
@ -122,6 +127,11 @@ if (Grocy.Components.ProductPicker !== undefined)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled))
|
||||
{
|
||||
Grocy.UISound.BarcodeScannerBeep();
|
||||
}
|
||||
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
@ -129,7 +139,7 @@ if (Grocy.Components.ProductPicker !== undefined)
|
||||
Grocy.Components.ProductCard.Refresh(productId);
|
||||
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function (productDetails)
|
||||
function(productDetails)
|
||||
{
|
||||
$('#price').val(productDetails.last_price);
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
@ -205,6 +215,21 @@ if (Grocy.Components.ProductPicker !== undefined)
|
||||
$('#amount').focus();
|
||||
}
|
||||
}
|
||||
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled))
|
||||
{
|
||||
$("#amount").val(1);
|
||||
Grocy.FrontendHelpers.ValidateForm("purchase-form");
|
||||
if (document.getElementById("purchase-form").checkValidity() === true)
|
||||
{
|
||||
$('#save-purchase-button').click();
|
||||
}
|
||||
else
|
||||
{
|
||||
toastr.warning(__t("Scan mode is on but not all required fields could be populated automatically"));
|
||||
Grocy.UISound.Error();
|
||||
}
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
@ -336,3 +361,15 @@ function UndoStockTransaction(transactionId)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Can only be set via JS however...
|
||||
$("#scan-mode").addClass("user-setting-control");
|
||||
$("#scan-mode").attr("data-setting-key", "scan_mode_purchase_enabled");
|
||||
|
||||
$(document).on("change", "#scan-mode", function(e)
|
||||
{
|
||||
if ($(this).prop("checked"))
|
||||
{
|
||||
Grocy.UISound.AskForPermission();
|
||||
}
|
||||
});
|
||||
|
@ -440,3 +440,6 @@ if (GetUriParam("embedded") !== undefined)
|
||||
$("#use_specific_stock_entry").trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
// Default input field
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
|
@ -4,10 +4,22 @@
|
||||
@section('activeNav', 'consume')
|
||||
@section('viewJsName', 'consume')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/bootstrap-switch-button/css/bootstrap-switch-button.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/node_modules/bootstrap-switch-button/js/bootstrap-switch-button.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/js/grocy_uisound.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6 col-xl-4 pb-3">
|
||||
<h1>@yield('title')</h1>
|
||||
<h1>
|
||||
@yield('title')
|
||||
<input @if(boolval($userSettings['scan_mode_consume_enabled'])) checked @endif id="scan-mode" type="checkbox" data-setting-key="scan_mode_consume_enabled" data-toggle="switchbutton" data-onlabel="{{ $__t('Scan mode') }} {{ $__t('on') }}" data-offlabel="{{ $__t('Scan mode') }} {{ $__t('off') }}" data-onstyle="success" data-offstyle="primary" data-style="ml-2" data-width="160">
|
||||
</h1>
|
||||
|
||||
<form id="consume-form" novalidate>
|
||||
|
||||
@ -28,23 +40,23 @@
|
||||
))
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
@php /*@include('components.locationpicker', array(
|
||||
'id' => 'location_id',
|
||||
'locations' => $locations,
|
||||
'isRequired' => true,
|
||||
'label' => 'Location'
|
||||
))*/ @endphp
|
||||
@php /*@include('components.locationpicker', array(
|
||||
'id' => 'location_id',
|
||||
'locations' => $locations,
|
||||
'isRequired' => true,
|
||||
'label' => 'Location'
|
||||
))*/ @endphp
|
||||
|
||||
<div class="form-group">
|
||||
<label for="location_id">{{ $__t('Location') }}</label>
|
||||
<select required class="form-control location-combobox" id="location_id" name="location_id">
|
||||
<option></option>
|
||||
@foreach($locations as $location)
|
||||
<option value="{{ $location->id }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A location is required') }}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location_id">{{ $__t('Location') }}</label>
|
||||
<select required class="form-control location-combobox" id="location_id" name="location_id">
|
||||
<option></option>
|
||||
@foreach($locations as $location)
|
||||
<option value="{{ $location->id }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A location is required') }}</div>
|
||||
</div>
|
||||
@else
|
||||
<input type="hidden" name="location_id" id="location_id" value="1">
|
||||
@endif
|
||||
|
@ -4,10 +4,22 @@
|
||||
@section('activeNav', 'purchase')
|
||||
@section('viewJsName', 'purchase')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/bootstrap-switch-button/css/bootstrap-switch-button.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/node_modules/bootstrap-switch-button/js/bootstrap-switch-button.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/js/grocy_uisound.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6 col-xl-4 pb-3">
|
||||
<h1>@yield('title')</h1>
|
||||
<h1>
|
||||
@yield('title')
|
||||
<input @if(boolval($userSettings['scan_mode_purchase_enabled'])) checked @endif id="scan-mode" type="checkbox" data-setting-key="scan_mode_purchase_enabled" data-toggle="switchbutton" data-onlabel="{{ $__t('Scan mode') }} {{ $__t('on') }}" data-offlabel="{{ $__t('Scan mode') }} {{ $__t('off') }}" data-onstyle="success" data-offstyle="primary" data-style="ml-2" data-width="160">
|
||||
</h1>
|
||||
|
||||
<form id="purchase-form" novalidate>
|
||||
|
||||
|
14
yarn.lock
14
yarn.lock
@ -17,6 +17,11 @@
|
||||
dependencies:
|
||||
jquery "1"
|
||||
|
||||
add@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235"
|
||||
integrity sha1-JI8Kn25aUo7yKV2+7DBTITCuIjU=
|
||||
|
||||
ajv@^6.5.5:
|
||||
version "6.10.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
|
||||
@ -75,6 +80,10 @@ bootstrap-select@^1.13.10:
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-select/-/bootstrap-select-1.13.11.tgz#d364ccc67cd6c1f9158689087f1d2d029df82c29"
|
||||
integrity sha512-WPpx2DYL9jVilNoqy4Pjcfa/Q0LOq8V0+xws/pmnRDn/deS7OYjo1njvD1Cv0s9/1ZUXt77UypxuloimEnkYsA==
|
||||
|
||||
"bootstrap-switch-button@https://github.com/walidbagh/bootstrap-switch-button#Fix-module-export":
|
||||
version "1.0.0"
|
||||
resolved "https://github.com/walidbagh/bootstrap-switch-button#551ccd361cc9e291cd04c2278357b1db76318f1e"
|
||||
|
||||
bootstrap@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0.tgz#ceb03842c145fcc1b9b4e15da2a05656ba68469a"
|
||||
@ -779,3 +788,8 @@ verror@1.10.0:
|
||||
assert-plus "^1.0.0"
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
yarn@^1.21.1:
|
||||
version "1.21.1"
|
||||
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.21.1.tgz#1d5da01a9a03492dc4a5957befc1fd12da83d89c"
|
||||
integrity sha512-dQgmJv676X/NQczpbiDtc2hsE/pppGDJAzwlRiADMTvFzYbdxPj2WO4PcNyriSt2c4jsCMpt8UFRKHUozt21GQ==
|
||||
|
Loading…
x
Reference in New Issue
Block a user