Implemented stock sub-feature-flags (closes #314)

This commit is contained in:
Bernd Bestel
2019-09-19 17:46:52 +02:00
parent 5e9a7fb7ca
commit cbf1d1ca40
16 changed files with 164 additions and 50 deletions
+6 -1
View File
@@ -7,7 +7,7 @@
- Available on any barcode-enabled field (so currently only for picking products) - a new camera button at the right of side the text field
- Implemented using [QuaggaJS](https://github.com/serratus/quaggaJS) - camera stream processing happens totally offline / client-side
- Please note due to browser security restrictions, this only works when serving grocy via a secure connection (`https://`)
- There is also a `config.php` setting `DISABLE_BROWSER_BARCODE_CAMERA_SCANNING` to disable this, if you don't need it at all
- There is also a `config.php` setting `DISABLE_BROWSER_BARCODE_CAMERA_SCANNING` to disable this, if you don't need it at all (defaults to `false`)
### Stock improvements
- Products can now have variations (nested products)
@@ -21,6 +21,11 @@
- It's now possible to print a "Location Content Sheet" with the current stock per location - new button at the top of the stock overview page (thought to hang it at the location, note used amounts on paper and track it in grocy later)
- The product description now can have formattings (HTML/WYSIWYG editor like for recipes)
- "Factor purchase to stock quantity unit" (product option) can now also be a decimal number when "Allow partial units in stock" is enabled
- New "Sub feature flags" in `config.php` to disable some sub-features (hide the corresponding UI elements) if you don't need them (all new feature flags default to `true`, so no changed behaviour when not configured)
- `FEATURE_FLAG_STOCK_PRICE_TRACKING` to disable product price tracking
- `FEATURE_FLAG_STOCK_LOCATION_TRACKING` to disable product location tracking
- `FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING` to disable product best before date tracking
- `FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING` to disable product opened tracking
### Recipe improvements
- Based on the new linked quantity units, recipe ingredients can now use any product related unit, the amount is calculated according to the cnoversion factor of the unit relation
+12 -8
View File
@@ -17,8 +17,8 @@
# Either "production", "dev" or "prerelease"
Setting('MODE', 'production');
# Either "en" or "de" or the filename (without extension) of
# one of the other available localization files in the "/localization" directory
# Either "en" or "de" or the directory name of
# one of the other available localization folders in the "/localization" directory
Setting('CULTURE', 'en');
# This is used to define the first day of a week for calendar views in the frontend,
@@ -45,12 +45,11 @@ Setting('BASE_URL', '/');
# see /data/plugins/DemoBarcodeLookupPlugin.php for an example implementation
Setting('STOCK_BARCODE_LOOKUP_PLUGIN', 'DemoBarcodeLookupPlugin');
# If, however, your webserver does not support URL rewriting,
# set this to true
# If, however, your webserver does not support URL rewriting, set this to true
Setting('DISABLE_URL_REWRITING', false);
# Specify an custom homepage if desired. By default the homepage will be set to the stock overview.
# You can chosen one of the following values:
# Specify an custom homepage if desired - by default the homepage will be set to the stock overview,
# this needs to be one of the following values:
# stock, shoppinglist, recipes, chores, tasks, batteries, equipment, calendar
Setting('ENTRY_PAGE', 'stock');
@@ -61,6 +60,7 @@ Setting('DISABLE_AUTH', false);
# Set this to true if you want to disable the ability to scan a barcode via the device camera (Browser API)
Setting('DISABLE_BROWSER_BARCODE_CAMERA_SCANNING', false);
# Default user settings
# These settings can be changed per user, here the defaults
# are defined which are used when the user has not changed the setting so far
@@ -103,8 +103,6 @@ DefaultUserSetting('show_clock_in_header', false);
DefaultUserSetting('shopping_list_to_stock_workflow_auto_submit_when_prefilled', false);
# Feature flags
# grocy was initially about "stock management for your household", many other things
# came and still come by, because they are useful - here you can disable the parts
@@ -118,3 +116,9 @@ Setting('FEATURE_FLAG_TASKS', true);
Setting('FEATURE_FLAG_BATTERIES', true);
Setting('FEATURE_FLAG_EQUIPMENT', true);
Setting('FEATURE_FLAG_CALENDAR', true);
# Sub feature flags
Setting('FEATURE_FLAG_STOCK_PRICE_TRACKING', true);
Setting('FEATURE_FLAG_STOCK_LOCATION_TRACKING', true);
Setting('FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING', true);
Setting('FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING', true);
+27 -24
View File
@@ -100,35 +100,38 @@ Grocy.Components.ProductCard.Refresh = function(productId)
}
);
Grocy.Api.Get('stock/products/' + productId + '/price-history',
function(priceHistoryDataPoints)
{
if (priceHistoryDataPoints.length > 0)
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
Grocy.Api.Get('stock/products/' + productId + '/price-history',
function(priceHistoryDataPoints)
{
$("#productcard-product-price-history-chart").removeClass("d-none");
$("#productcard-no-price-data-hint").addClass("d-none");
Grocy.Components.ProductCard.ReInitPriceHistoryChart();
priceHistoryDataPoints.forEach((dataPoint) =>
if (priceHistoryDataPoints.length > 0)
{
Grocy.Components.ProductCard.PriceHistoryChart.data.labels.push(moment(dataPoint.date).toDate());
$("#productcard-product-price-history-chart").removeClass("d-none");
$("#productcard-no-price-data-hint").addClass("d-none");
var dataset = Grocy.Components.ProductCard.PriceHistoryChart.data.datasets[0];
dataset.data.push(dataPoint.price);
});
Grocy.Components.ProductCard.PriceHistoryChart.update();
}
else
Grocy.Components.ProductCard.ReInitPriceHistoryChart();
priceHistoryDataPoints.forEach((dataPoint) =>
{
Grocy.Components.ProductCard.PriceHistoryChart.data.labels.push(moment(dataPoint.date).toDate());
var dataset = Grocy.Components.ProductCard.PriceHistoryChart.data.datasets[0];
dataset.data.push(dataPoint.price);
});
Grocy.Components.ProductCard.PriceHistoryChart.update();
}
else
{
$("#productcard-product-price-history-chart").addClass("d-none");
$("#productcard-no-price-data-hint").removeClass("d-none");
}
},
function(xhr)
{
$("#productcard-product-price-history-chart").addClass("d-none");
$("#productcard-no-price-data-hint").removeClass("d-none");
console.error(xhr);
}
},
function(xhr)
{
console.error(xhr);
}
);
);
}
};
Grocy.Components.ProductCard.ReInitPriceHistoryChart = function()
+25 -4
View File
@@ -17,7 +17,14 @@
var jsonData = { };
jsonData.new_amount = jsonForm.new_amount;
jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue();
jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
}
else
{
jsonData.location_id = 1;
}
jsonData.price = price;
Grocy.Api.Post('stock/products/' + jsonForm.product_id + '/inventory', jsonData,
@@ -120,7 +127,10 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
}
$('#price').val(productDetails.last_price);
Grocy.Components.LocationPicker.SetId(productDetails.location.id);
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
Grocy.Components.LocationPicker.SetId(productDetails.location.id);
}
$('#new_amount').focus();
},
function(xhr)
@@ -221,13 +231,24 @@ $('#new_amount').on('keyup', function(e)
{
$('#inventory-change-info').text(__t('This means %s will be added to stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)));
Grocy.Components.DateTimePicker.GetInputElement().attr('required', '');
Grocy.Components.LocationPicker.GetInputElement().attr('required', '');
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
Grocy.Components.LocationPicker.GetInputElement().attr('required', '');
}
}
else if (newAmount < productStockAmount + containerWeight)
{
$('#inventory-change-info').text(__t('This means %s will be removed from stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)));
Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required');
Grocy.Components.LocationPicker.GetInputElement().removeAttr('required');
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
Grocy.Components.LocationPicker.GetInputElement().removeAttr('required');
}
}
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
{
Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required');
}
Grocy.FrontendHelpers.ValidateForm('inventory-form');
+25 -4
View File
@@ -20,7 +20,14 @@
jsonData.amount = amount;
jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue();
jsonData.price = price;
jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
}
else
{
jsonData.location_id = 1;
}
Grocy.Api.Post('stock/products/' + jsonForm.product_id + '/add', jsonData,
function(result)
@@ -73,7 +80,10 @@
$('#price').val('');
$('#amount_qu_unit').text("");
$("#tare-weight-handling-info").addClass("d-none");
Grocy.Components.LocationPicker.Clear();
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
Grocy.Components.LocationPicker.Clear();
}
Grocy.Components.DateTimePicker.Clear();
Grocy.Components.ProductPicker.SetValue('');
Grocy.Components.ProductPicker.GetInputElement().focus();
@@ -107,7 +117,10 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
function (productDetails)
{
$('#price').val(productDetails.last_price);
Grocy.Components.LocationPicker.SetId(productDetails.location.id);
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
{
Grocy.Components.LocationPicker.SetId(productDetails.location.id);
}
if (productDetails.product.qu_id_purchase === productDetails.product.qu_id_stock)
{
@@ -166,7 +179,15 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
}
else
{
Grocy.Components.DateTimePicker.GetInputElement().focus();
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
{
Grocy.Components.DateTimePicker.GetInputElement().focus();
}
else
{
Grocy.Components.DateTimePicker.SetValue(moment().format('YYYY-MM-DD'));
$('#amount').focus();
}
}
},
function(xhr)
+2 -1
View File
@@ -13,8 +13,9 @@
@php if(!isset($noNameAttribute)) { $noNameAttribute = false; } @endphp
@php if(!isset($nextInputSelector)) { $nextInputSelector = false; } @endphp
@php if(empty($additionalAttributes)) { $additionalAttributes = ''; } @endphp
@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp
<div class="form-group">
<div class="form-group {{ $additionalGroupCssClasses }}">
<label for="{{ $id }}">{{ $__t($label) }}
<span class="small text-muted">
@if(!empty($hint)){{ $__t($hint) }}@endif
+5 -3
View File
@@ -20,19 +20,21 @@
<strong>{{ $__t('Stock amount') . ' / ' . $__t('Quantity unit') }}:</strong> <span id="productcard-product-stock-amount"></span> <span id="productcard-product-stock-qu-name"></span> <span id="productcard-product-stock-opened-amount" class="small font-italic"></span>
<span id="productcard-aggregated-amounts" class="pl-2 text-secondary d-none"><i class="fas fa-custom-sigma-sign"></i> <span id="productcard-product-stock-amount-aggregated"></span> <span id="productcard-product-stock-qu-name-aggregated"></span> <span id="productcard-product-stock-opened-amount-aggregated" class="small font-italic"></span></span><br>
<strong>{{ $__t('Location') }}:</strong> <span id="productcard-product-location"></span><br>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)<strong>{{ $__t('Location') }}:</strong> <span id="productcard-product-location"></span><br>@endif
<strong>{{ $__t('Last purchased') }}:</strong> <span id="productcard-product-last-purchased"></span> <time id="productcard-product-last-purchased-timeago" class="timeago timeago-contextual"></time><br>
<strong>{{ $__t('Last used') }}:</strong> <span id="productcard-product-last-used"></span> <time id="productcard-product-last-used-timeago" class="timeago timeago-contextual"></time><br>
<strong>{{ $__t('Last price') }}:</strong> <span id="productcard-product-last-price"></span><br>
<strong>{{ $__t('Average shelf life') }}:</strong> <span id="productcard-product-average-shelf-life"></span><br>
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)<strong>{{ $__t('Last price') }}:</strong> <span id="productcard-product-last-price"></span><br>@endif
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)<strong>{{ $__t('Average shelf life') }}:</strong> <span id="productcard-product-average-shelf-life"></span><br>@endif
<strong>{{ $__t('Spoil rate') }}:</strong> <span id="productcard-product-spoil-rate"></span>
<h5 class="mt-3">{{ $__t('Product picture') }}</h5>
<p class="w-75 mx-auto"><img id="productcard-product-picture" data-src="" class="img-fluid img-thumbnail d-none lazy"></p>
<span id="productcard-no-product-picture" class="font-italic d-none">{{ $__t('No picture available') }}</span>
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
<h5 class="mt-3">{{ $__t('Price history') }}</h5>
<canvas id="productcard-product-price-history-chart" class="w-100 d-none"></canvas>
<span id="productcard-no-price-data-hint" class="font-italic d-none">{{ $__t('No price history available') }}</span>
@endif
</div>
</div>
+3
View File
@@ -52,7 +52,10 @@
@endif
<button id="save-consume-button" class="btn btn-success">{{ $__t('OK') }}</button>
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
<button id="save-mark-as-open-button" class="btn btn-secondary">{{ $__t('Mark as opened') }}</button>
@endif
</form>
</div>
+18 -1
View File
@@ -28,6 +28,13 @@
'additionalHtmlContextHelp' => '<div id="tare-weight-handling-info" class="text-small 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>'
))
@php
$additionalGroupCssClasses = '';
if (!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
{
$additionalGroupCssClasses = 'd-none';
}
@endphp
@include('components.datetimepicker', array(
'id' => 'best_before_date',
'label' => 'Best before',
@@ -42,9 +49,12 @@
'shortcutValue' => '2999-12-31',
'shortcutLabel' => 'Never expires',
'earlierThanInfoLimit' => date('Y-m-d'),
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?')
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?'),
'additionalGroupCssClasses' => $additionalGroupCssClasses
))
@php $additionalGroupCssClasses = ''; @endphp
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
@include('components.numberpicker', array(
'id' => 'price',
'label' => 'Price',
@@ -56,11 +66,18 @@
'invalidFeedback' => $__t('The price cannot be lower than %s', '0'),
'isRequired' => false
))
@else
<input type="hidden" name="price" id="price" value="0">
@endif
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
@include('components.locationpicker', array(
'locations' => $locations,
'hint' => $__t('This will apply to added products')
))
@else
<input type="hidden" name="location_id" id="location_id" value="1">
@endif
<button id="save-inventory-button" class="btn btn-success">{{ $__t('OK') }}</button>
+2
View File
@@ -212,12 +212,14 @@
<span class="nav-link-text">{{ $__t('Products') }}</span>
</a>
</li>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
<li data-nav-for-page="locations" data-sub-menu-of="#top-nav-manager-master-data">
<a class="nav-link discrete-link" href="{{ $U('/locations') }}">
<i class="fas fa-map-marker-alt"></i>
<span class="nav-link-text">{{ $__t('Locations') }}</span>
</a>
</li>
@endif
<li data-nav-for-page="quantityunits" data-sub-menu-of="#top-nav-manager-master-data">
<a class="nav-link discrete-link" href="{{ $U('/quantityunits') }}">
<i class="fas fa-balance-scale"></i>
+4
View File
@@ -73,6 +73,7 @@
<div id="barcode-taginput-container"></div>
</div>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
<div class="form-group">
<label for="location_id">{{ $__t('Location') }}</label>
<select required class="form-control" id="location_id" name="location_id">
@@ -83,6 +84,9 @@
</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
@php if($mode == 'edit') { $value = $product->min_stock_amount; } else { $value = 0; } @endphp
@include('components.numberpicker', array(
+2 -2
View File
@@ -45,7 +45,7 @@
<tr>
<th class="border-right"></th>
<th>{{ $__t('Name') }}</th>
<th>{{ $__t('Location') }}</th>
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">{{ $__t('Location') }}</th>
<th>{{ $__t('Min. stock amount') }}</th>
<th>{{ $__t('QU purchase') }}</th>
<th>{{ $__t('QU stock') }}</th>
@@ -73,7 +73,7 @@
<td>
{{ $product->name }}@if(!empty($product->picture_file_name)) <i class="fas fa-image text-muted"></i>@endif
</td>
<td>
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
{{ FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name }}
</td>
<td>
+18 -1
View File
@@ -16,6 +16,13 @@
'nextInputSelector' => '#best_before_date .datetimepicker-input'
))
@php
$additionalGroupCssClasses = '';
if (!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
{
$additionalGroupCssClasses = 'd-none';
}
@endphp
@include('components.datetimepicker', array(
'id' => 'best_before_date',
'label' => 'Best before',
@@ -29,8 +36,10 @@
'shortcutValue' => '2999-12-31',
'shortcutLabel' => 'Never expires',
'earlierThanInfoLimit' => date('Y-m-d'),
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?')
'earlierThanInfoText' => $__t('The given date is earlier than today, are you sure?'),
'additionalGroupCssClasses' => $additionalGroupCssClasses
))
@php $additionalGroupCssClasses = ''; @endphp
@include('components.numberpicker', array(
'id' => 'amount',
@@ -41,6 +50,7 @@
'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>'
))
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
@include('components.numberpicker', array(
'id' => 'price',
'label' => 'Price',
@@ -51,11 +61,18 @@
'invalidFeedback' => $__t('The price cannot be lower than %s', '0'),
'isRequired' => false
))
@else
<input type="hidden" name="price" id="price" value="0">
@endif
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
@include('components.locationpicker', array(
'locations' => $locations,
'isRequired' => false
))
@else
<input type="hidden" name="location_id" id="location_id" value="1">
@endif
<button id="save-purchase-button" class="btn btn-success">{{ $__t('OK') }}</button>
+2
View File
@@ -155,6 +155,7 @@
'additionalAttributes' => 'data-recipe-id="' . $selectedRecipe->id . '"'
))
</div>
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
<div class="col-7">
<label>{{ $__t('Costs') }}&nbsp;&nbsp;
<span class="small text-muted">{{ $__t('Based on the prices of the last purchase per product') }}</span>
@@ -163,6 +164,7 @@
<span class="locale-number-format" data-format="currency">{{ $selectedRecipeTotalCosts }}</span>
</p>
</div>
@endif
</div>
</div>
+11 -1
View File
@@ -24,12 +24,16 @@
<a class="btn btn-outline-dark responsive-button" href="{{ $U('/stockjournal') }}">
<i class="fas fa-file-alt"></i> {{ $__t('Journal') }}
</a>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
<a class="btn btn-outline-dark responsive-button" href="{{ $U('/locationcontentsheet') }}">
<i class="fas fa-print"></i> {{ $__t('Location Content Sheet') }}
</a>
@endif
</h1>
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<p id="info-expiring-products" data-next-x-days="{{ $nextXDays }}" data-status-filter="expiring" class="btn btn-lg btn-warning status-filter-button responsive-button mr-2"></p>
<p id="info-expired-products" data-status-filter="expired" class="btn btn-lg btn-danger status-filter-button responsive-button mr-2"></p>
@endif
<p id="info-missing-products" data-status-filter="belowminstockamount" class="btn btn-lg btn-info status-filter-button responsive-button"></p>
</div>
</div>
@@ -39,6 +43,7 @@
<label for="search">{{ $__t('Search') }}</label> <i class="fas fa-search"></i>
<input type="text" class="form-control" id="search">
</div>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
<div class="col-xs-12 col-md-6 col-xl-3">
<label for="location-filter">{{ $__t('Filter by location') }}</label> <i class="fas fa-filter"></i>
<select class="form-control" id="location-filter">
@@ -48,6 +53,7 @@
@endforeach
</select>
</div>
@endif
<div class="col-xs-12 col-md-6 col-xl-3">
<label for="location-filter">{{ $__t('Filter by product group') }}</label> <i class="fas fa-filter"></i>
<select class="form-control" id="product-group-filter">
@@ -61,8 +67,10 @@
<label for="status-filter">{{ $__t('Filter by status') }}</label> <i class="fas fa-filter"></i>
<select class="form-control" id="status-filter">
<option class="bg-white" value="all">{{ $__t('All') }}</option>
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<option class="bg-warning" value="expiring">{{ $__t('Expiring soon') }}</option>
<option class="bg-danger" value="expired">{{ $__t('Already expired') }}</option>
@endif
<option class="bg-info" value="belowminstockamount">{{ $__t('Below min. stock amount') }}</option>
</select>
</div>
@@ -89,7 +97,7 @@
</thead>
<tbody class="d-none">
@foreach($currentStock as $currentStockEntry)
<tr id="product-{{ $currentStockEntry->product_id }}-row" class="@if($currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) table-danger @elseif($currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days")) && $currentStockEntry->amount > 0) table-warning @elseif (FindObjectInArrayByPropertyValue($missingProducts, 'id', $currentStockEntry->product_id) !== null) table-info @endif">
<tr id="product-{{ $currentStockEntry->product_id }}-row" class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) table-danger @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days")) && $currentStockEntry->amount > 0) table-warning @elseif (FindObjectInArrayByPropertyValue($missingProducts, 'id', $currentStockEntry->product_id) !== null) table-info @endif">
<td class="fit-content border-right">
<a class="btn btn-success btn-sm product-consume-button @if($currentStockEntry->amount < 1) disabled @endif" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Consume %1$s of %2$s', '1 ' . FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name) }}"
data-product-id="{{ $currentStockEntry->product_id }}"
@@ -105,12 +113,14 @@
data-consume-amount="{{ $currentStockEntry->amount }}">
<i class="fas fa-utensils"></i> {{ $__t('All') }}
</a>
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
<a class="btn btn-success btn-sm product-open-button @if($currentStockEntry->amount < 1 || $currentStockEntry->amount == $currentStockEntry->amount_opened) disabled @endif" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Mark %1$s of %2$s as open', '1 ' . FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name) }}"
data-product-id="{{ $currentStockEntry->product_id }}"
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}"
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name }}">
<i class="fas fa-box-open"></i> 1
</a>
@endif
<div class="dropdown d-inline-block">
<button class="btn btn-sm btn-light text-secondary" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-v"></i>
+2
View File
@@ -12,6 +12,7 @@
<div id="productpresets">
<h4>{{ $__t('Presets for new products') }}</h4>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
<div class="form-group">
<label for="product_presets_location_id">{{ $__t('Location') }}</label>
<select class="form-control user-setting-control" id="product_presets_location_id" data-setting-key="product_presets_location_id">
@@ -21,6 +22,7 @@
@endforeach
</select>
</div>
@endif
<div class="form-group">
<label for="product_presets_product_group_id">{{ $__t('Product group') }}</label>